45d1d31847
the namespace might be shared by other processes. instead, we create a anonymous pipe with pipe() and use devdup to open one end close-on-exec. this is shorter and avoids the race condition. do not touch Execargs after writing the error message as the process might be gone after the write. this was to manually close the fd which isnt neccesary as the kernel will do it for us on the following exit.
638 lines
13 KiB
Plaintext
638 lines
13 KiB
Plaintext
.TH THREAD 2
|
|
.SH NAME
|
|
alt,
|
|
chanclose,
|
|
chancreate,
|
|
chanfree,
|
|
chaninit,
|
|
chanclosing,
|
|
chanprint,
|
|
mainstacksize,
|
|
proccreate,
|
|
procdata,
|
|
procexec,
|
|
procexecl,
|
|
procrfork,
|
|
recv,
|
|
recvp,
|
|
recvul,
|
|
send,
|
|
sendp,
|
|
sendul,
|
|
nbrecv,
|
|
nbrecvp,
|
|
nbrecvul,
|
|
nbsend,
|
|
nbsendp,
|
|
nbsendul,
|
|
threadcreate,
|
|
threaddata,
|
|
threadexits,
|
|
threadexitsall,
|
|
threadgetgrp,
|
|
threadgetname,
|
|
threadint,
|
|
threadintgrp,
|
|
threadkill,
|
|
threadkillgrp,
|
|
threadmain,
|
|
threadnotify,
|
|
threadid,
|
|
threadpid,
|
|
threadsetgrp,
|
|
threadsetname,
|
|
threadwaitchan,
|
|
yield \- thread and proc management
|
|
.SH SYNOPSIS
|
|
.EX
|
|
.ta 4n +4n +4n +4n +4n +4n +4n
|
|
#include <u.h>
|
|
#include <libc.h>
|
|
#include <thread.h>
|
|
.sp
|
|
typedef enum {
|
|
CHANEND,
|
|
CHANSND,
|
|
CHANRCV,
|
|
CHANNOP,
|
|
CHANNOBLK,
|
|
} ChanOp;
|
|
.sp
|
|
.ta \w' 'u +\w'Channel 'u
|
|
typedef struct Alt Alt;
|
|
struct Alt {
|
|
Channel *c; /* channel */
|
|
void *v; /* pointer to value */
|
|
ChanOp op; /* operation */
|
|
char *err; /* did the op fail? */
|
|
/*
|
|
* the next variables are used internally to alt
|
|
* they need not be initialized
|
|
*/
|
|
Channel **tag; /* pointer to rendez-vous tag */
|
|
int entryno; /* entry number */
|
|
};
|
|
.fi
|
|
.de XX
|
|
.ift .sp 0.5
|
|
.ifn .sp
|
|
..
|
|
.PP
|
|
.nf
|
|
.ft L
|
|
.ta \w'\fLChannel* 'u +4n +4n +4n +4n
|
|
void threadmain(int argc, char *argv[])
|
|
int mainstacksize
|
|
int proccreate(void (*fn)(void*), void *arg, uint stacksize)
|
|
int procrfork(void (*fn)(void*), void *arg, uint stacksize,
|
|
int rforkflag)
|
|
int threadcreate(void (*fn)(void*), void *arg, uint stacksize)
|
|
void threadexits(char *status)
|
|
void threadexitsall(char *status)
|
|
void yield(void)
|
|
.XX
|
|
int threadid(void)
|
|
int threadgrp(void)
|
|
int threadsetgrp(int group)
|
|
int threadpid(int id)
|
|
.XX
|
|
int threadint(int id)
|
|
void threadintgrp(int group)
|
|
void threadkill(int id)
|
|
int threadkillgrp(int group)
|
|
.XX
|
|
void threadsetname(char *name, ...)
|
|
char* threadgetname(void)
|
|
.XX
|
|
void** threaddata(void)
|
|
void** procdata(void)
|
|
.XX
|
|
int chaninit(Channel *c, int elsize, int nel)
|
|
Channel* chancreate(int elsize, int nel)
|
|
void chanfree(Channel *c)
|
|
.XX
|
|
int alt(Alt *alts)
|
|
int recv(Channel *c, void *v)
|
|
void* recvp(Channel *c)
|
|
ulong recvul(Channel *c)
|
|
int nbrecv(Channel *c, void *v)
|
|
void* nbrecvp(Channel *c)
|
|
ulong nbrecvul(Channel *c)
|
|
int send(Channel *c, void *v)
|
|
int sendp(Channel *c, void *v)
|
|
int sendul(Channel *c, ulong v)
|
|
int nbsend(Channel *c, void *v)
|
|
int nbsendp(Channel *c, void *v)
|
|
int nbsendul(Channel *c, ulong v)
|
|
int chanprint(Channel *c, char *fmt, ...)
|
|
int chanclose(Channel *c);
|
|
int chanclosing(Channel *c);
|
|
.XX
|
|
void procexecl(Channel *cpid, char *file, ...)
|
|
void procexec(Channel *cpid, char *file, char *args[])
|
|
Channel* threadwaitchan(void)
|
|
.XX
|
|
int threadnotify(int (*f)(void*, char*), int in)
|
|
.EE
|
|
.SH DESCRIPTION
|
|
The thread library provides parallel programming support similar to that
|
|
of the languages
|
|
Alef and Newsqueak.
|
|
.I Threads
|
|
and
|
|
.I procs
|
|
occupy a shared address space,
|
|
communicating and synchronizing through
|
|
.I channels
|
|
and shared variables.
|
|
.PP
|
|
A
|
|
.I proc
|
|
is a Plan 9 process that contains one or more cooperatively-scheduled
|
|
.IR threads .
|
|
Programs using threads must replace
|
|
.I main
|
|
by
|
|
.IR threadmain .
|
|
The thread library provides a
|
|
.I main
|
|
function that sets up a proc with a single thread executing
|
|
.I threadmain
|
|
on a stack of size
|
|
.I mainstacksize
|
|
(default eight kilobytes).
|
|
To set
|
|
.IR mainstacksize ,
|
|
declare a global variable
|
|
initialized to the desired value
|
|
.RI ( e.g. ,
|
|
.B int
|
|
.B mainstacksize
|
|
.B =
|
|
.BR 1024 ).
|
|
.SS Creation
|
|
.I Threadcreate
|
|
creates a new thread in the calling proc, returning a unique integer
|
|
identifying the thread; the thread
|
|
executes
|
|
.I fn(arg)
|
|
on a stack of size
|
|
.IR stacksize .
|
|
Thread stacks are allocated in shared memory, making it valid to pass
|
|
pointers to stack variables between threads and procs.
|
|
.I Procrfork
|
|
creates a new proc, and inside that proc creates
|
|
a single thread as
|
|
.I threadcreate
|
|
would,
|
|
returning the id of the created thread.
|
|
.I Procrfork
|
|
creates the new proc by calling
|
|
.B rfork
|
|
(see
|
|
.IR fork (2))
|
|
with flags
|
|
.BR RFPROC|RFMEM|RFNOWAIT| \fIrforkflag\fR.
|
|
(The thread library depends on all its procs
|
|
running in the same rendezvous group.
|
|
Do not include
|
|
.B RFREND
|
|
in
|
|
.IR rforkflag .)
|
|
.I Proccreate
|
|
is identical to
|
|
.I procrfork
|
|
with
|
|
.I rforkflag
|
|
set to zero.
|
|
Be aware that the calling thread may continue
|
|
execution before
|
|
the newly created proc and thread
|
|
are scheduled.
|
|
Because of this,
|
|
.I arg
|
|
should not point to data on the stack of a function that could
|
|
return before the new process is scheduled.
|
|
.PP
|
|
.I Threadexits
|
|
terminates the calling thread.
|
|
If the thread is the last in its proc,
|
|
.I threadexits
|
|
also terminates the proc, using
|
|
.I status
|
|
as the exit status.
|
|
.I Threadexitsall
|
|
terminates all procs in the program,
|
|
using
|
|
.I status
|
|
as the exit status.
|
|
.SS Scheduling
|
|
The threads in a proc are coroutines, scheduled non-preemptively
|
|
in a round-robin fashion.
|
|
A thread must explicitly relinquish control of the processor
|
|
before another thread in the same proc is run.
|
|
Calls that do this are
|
|
.IR yield ,
|
|
.IR proccreate ,
|
|
.IR procexec ,
|
|
.IR procexecl ,
|
|
.IR threadexits ,
|
|
.IR alt ,
|
|
.IR send ,
|
|
and
|
|
.I recv
|
|
(and the calls related to
|
|
.I send
|
|
and
|
|
.IR recv \(emsee
|
|
their descriptions further on),
|
|
plus these from
|
|
.IR lock (2):
|
|
.IR qlock ,
|
|
.IR rlock ,
|
|
.IR wlock ,
|
|
.IR rsleep .
|
|
Procs are scheduled by the operating system.
|
|
Therefore, threads in different procs can preempt one another
|
|
in arbitrary ways and should synchronize their
|
|
actions using
|
|
.B qlocks
|
|
(see
|
|
.IR lock (2))
|
|
or channel communication.
|
|
System calls such as
|
|
.IR read (2)
|
|
block the entire proc;
|
|
all threads in a proc block until the system call finishes.
|
|
.PP
|
|
As mentioned above, each thread has a unique integer thread id.
|
|
Thread ids are not reused; they are unique across the life of the program.
|
|
.I Threadid
|
|
returns the id for the current thread.
|
|
Each thread also has a thread group id.
|
|
The initial thread has a group id of zero.
|
|
Each new thread inherits the group id of
|
|
the thread that created it.
|
|
.I Threadgrp
|
|
returns the group id for the current thread;
|
|
.I threadsetgrp
|
|
sets it.
|
|
.I Threadpid
|
|
returns the pid of the Plan 9 process containing
|
|
the thread identified by
|
|
.IR id ,
|
|
or \-1
|
|
if no such thread is found.
|
|
.PP
|
|
.I Threadint
|
|
interrupts a thread that is blocked in a channel operation
|
|
or system call.
|
|
.I Threadintgrp
|
|
interrupts all threads with the given group id.
|
|
.I Threadkill
|
|
marks a thread to die when it next relinquishes the processor
|
|
(via one of the calls listed above).
|
|
If the thread is blocked in a channel operation or system call,
|
|
it is also interrupted.
|
|
.I Threadkillgrp
|
|
kills all threads with the given group id.
|
|
Note that
|
|
.I threadkill
|
|
and
|
|
.I threadkillgrp
|
|
will not terminate a thread that never relinquishes
|
|
the processor.
|
|
.SS Names and per-thread data
|
|
Primarily for debugging,
|
|
threads can have string names associated with them.
|
|
.I Threadgetname
|
|
returns the current thread's name;
|
|
.I threadsetname
|
|
sets it.
|
|
The pointer returned by
|
|
.I threadgetname
|
|
is only valid until the next call to
|
|
.IR threadsetname .
|
|
.PP
|
|
.I Threaddata
|
|
returns a pointer to a per-thread pointer
|
|
that may be modified by threaded programs for
|
|
per-thread storage.
|
|
Similarly,
|
|
.I procdata
|
|
returns a pointer to a per-proc pointer.
|
|
.SS Executing new programs
|
|
.I Procexecl
|
|
and
|
|
.I procexec
|
|
are threaded analogues of
|
|
.I exec
|
|
and
|
|
.I execl
|
|
(see
|
|
.IR exec (2));
|
|
on success,
|
|
they replace the calling thread (which must be the only thread in its proc)
|
|
and invoke the external program, never returning.
|
|
On error, they return \-1.
|
|
If
|
|
.I cpid
|
|
is not null, the pid of the invoked program
|
|
will be sent along
|
|
.I cpid
|
|
once the program has been started, or \-1 will be sent if an
|
|
error occurs.
|
|
.I Procexec
|
|
and
|
|
.I procexecl
|
|
will not access their arguments after sending a result
|
|
along
|
|
.IR cpid .
|
|
Thus, programs that malloc the
|
|
.I argv
|
|
passed to
|
|
.I procexec
|
|
can safely free it once they have
|
|
received the
|
|
.I cpid
|
|
response.
|
|
.PP
|
|
.I Threadwaitchan
|
|
returns a channel of pointers to
|
|
.B Waitmsg
|
|
structures (see
|
|
.IR wait (2)).
|
|
When an exec'ed process exits, a pointer to a
|
|
.B Waitmsg
|
|
is sent to this channel.
|
|
These
|
|
.B Waitmsg
|
|
structures have been allocated with
|
|
.IR malloc (2)
|
|
and should be freed after use.
|
|
.SS Channels
|
|
A
|
|
.B Channel
|
|
is a buffered or unbuffered queue for fixed-size messages.
|
|
Procs and threads
|
|
.I send
|
|
messages into the channel and
|
|
.I recv
|
|
messages from the channel. If the channel is unbuffered, a
|
|
.I send
|
|
operation blocks until the corresponding
|
|
.I recv
|
|
operation occurs and
|
|
.IR "vice versa" .
|
|
.I Chaninit
|
|
initializes a
|
|
.B Channel
|
|
for messages of size
|
|
.I elsize
|
|
and with a buffer holding
|
|
.I nel
|
|
messages.
|
|
If
|
|
.I nel
|
|
is zero, the channel is unbuffered.
|
|
.IR Chancreate
|
|
allocates a new channel and initializes it.
|
|
.I Chanfree
|
|
frees a channel that is no longer used.
|
|
.I Chanfree
|
|
can be called by either sender or receiver after the last item has been
|
|
sent or received. Freeing the channel will be delayed if there is a thread
|
|
blocked on it until that thread unblocks (but
|
|
.I chanfree
|
|
returns immediately).
|
|
.PP
|
|
.I Send
|
|
sends the element pointed at by
|
|
.I v
|
|
to the channel
|
|
.IR c .
|
|
If
|
|
.I v
|
|
is null, zeros are sent.
|
|
.I Recv
|
|
receives an element from
|
|
.I c
|
|
and stores it in
|
|
.IR v .
|
|
If
|
|
.I v
|
|
is null,
|
|
the received value is discarded.
|
|
.I Send
|
|
and
|
|
.I recv
|
|
return 1 on success, \-1 if interrupted.
|
|
.I Nbsend
|
|
and
|
|
.I nbrecv
|
|
behave similarly, but return 0 rather than blocking.
|
|
.PP
|
|
.IR Sendp ,
|
|
.IR nbsendp ,
|
|
.IR sendul ,
|
|
and
|
|
.I nbsendul
|
|
send a pointer or an unsigned long; the channel must
|
|
have been initialized with the appropriate
|
|
.IR elsize .
|
|
.IR Recvp ,
|
|
.IR nbrecvp ,
|
|
.IR recvul ,
|
|
and
|
|
.I nbrecvul
|
|
receive a pointer or an unsigned long;
|
|
they return zero when a zero is received,
|
|
when interrupted, or
|
|
(for
|
|
.I nbrecvp
|
|
and
|
|
.IR nbrecvul )
|
|
when the operation would have blocked.
|
|
To distinguish between these three cases,
|
|
use
|
|
.I recv
|
|
or
|
|
.IR nbrecv .
|
|
.PP
|
|
.I Alt
|
|
can be used to recv from or send to one of a number of channels,
|
|
as directed by an array of
|
|
.B Alt
|
|
structures,
|
|
each of which describes a potential send or receive operation.
|
|
In an
|
|
.B Alt
|
|
structure,
|
|
.B c
|
|
is the channel;
|
|
.B v
|
|
the value pointer (which may be null); and
|
|
.B op
|
|
the operation:
|
|
.B CHANSND
|
|
for a send operation,
|
|
.B CHANRCV
|
|
for a recv operation;
|
|
.B CHANNOP
|
|
for no operation
|
|
(useful
|
|
when
|
|
.I alt
|
|
is called with a varying set of operations).
|
|
The array of
|
|
.B Alt
|
|
structures is terminated by an entry with
|
|
.I op
|
|
.B CHANEND
|
|
or
|
|
.BR CHANNOBLK .
|
|
If at least one
|
|
.B Alt
|
|
structure can proceed, one of them is
|
|
chosen at random to be executed.
|
|
.I Alt
|
|
returns the index of the chosen structure.
|
|
If no operations can proceed and the list is terminated with
|
|
.BR CHANNOBLK ,
|
|
.I alt
|
|
returns the index of the terminating
|
|
.B CHANNOBLK
|
|
structure.
|
|
Otherwise,
|
|
.I alt
|
|
blocks until one of the operations can proceed,
|
|
eventually returning the index of the structure executes.
|
|
.I Alt
|
|
returns \-1 when interrupted.
|
|
The
|
|
.B tag
|
|
and
|
|
.B entryno
|
|
fields in the
|
|
.B Alt
|
|
structure are used internally by
|
|
.I alt
|
|
and need not be initialized.
|
|
They are not used between
|
|
.I alt
|
|
calls.
|
|
.PP
|
|
.I Chanprint
|
|
formats its arguments in the manner of
|
|
.IR print (2)
|
|
and sends the result to the channel
|
|
.IR c .
|
|
The string delivered by
|
|
.I chanprint
|
|
is allocated with
|
|
.IR malloc (2)
|
|
and should be freed upon receipt.
|
|
.PP
|
|
.I Chanclose
|
|
prevents further elements being sent to the channel
|
|
.IR c .
|
|
After closing a channel,
|
|
.I send
|
|
and
|
|
.I recv
|
|
never block.
|
|
.I Send
|
|
always
|
|
returns \-1.
|
|
.I Recv
|
|
returns \-1 if the channel is empty.
|
|
.I Alt
|
|
may choose a
|
|
.B CHANSND
|
|
or
|
|
.B CHANRCV
|
|
that failed because the channel was closed.
|
|
In this case, the
|
|
.B err
|
|
field of the
|
|
.B Alt
|
|
entry points to an error string stating that the
|
|
channel was closed and the operation was completed
|
|
with failure.
|
|
If all entries have been selected and failed because
|
|
they were closed,
|
|
.I alt
|
|
returns \-1.
|
|
.SS Errors, notes and resources
|
|
Thread library functions do not return on failure;
|
|
if errors occur, the entire program is aborted.
|
|
.PP
|
|
.I Chanclosing
|
|
returns \-1 if no one called
|
|
.I closed
|
|
on the channel, and otherwise
|
|
the number of elements still in the channel.
|
|
.PP
|
|
Threaded programs should use
|
|
.I threadnotify
|
|
in place of
|
|
.I atnotify
|
|
(see
|
|
.IR notify (2)).
|
|
.PP
|
|
It is safe to use
|
|
.B sysfatal
|
|
(see
|
|
.IR perror (2))
|
|
in threaded programs.
|
|
.I Sysfatal
|
|
will print the error string and call
|
|
.IR threadexitsall .
|
|
.PP
|
|
It is safe to use
|
|
.IR rfork
|
|
(see
|
|
.IR fork (2))
|
|
to manage the namespace, file descriptors, note group, and environment of a
|
|
single process.
|
|
That is, it is safe to call
|
|
.I rfork
|
|
with the flags
|
|
.BR RFNAMEG ,
|
|
.BR RFFDG ,
|
|
.BR RFCFDG ,
|
|
.BR RFNOTEG ,
|
|
.BR RFENVG ,
|
|
and
|
|
.BR RFCENVG.
|
|
(To create new processes, use
|
|
.I proccreate
|
|
and
|
|
.IR procrfork .)
|
|
As mentioned above,
|
|
the thread library depends on all procs being in the
|
|
same rendezvous group; do not change the rendezvous
|
|
group with
|
|
.IR rfork .
|
|
.SH FILES
|
|
.TF /sys/lib/acid/thread
|
|
.TP
|
|
.B /sys/lib/acid/thread
|
|
useful
|
|
.IR acid (1)
|
|
functions for debugging threaded programs.
|
|
.TP
|
|
.B /sys/src/libthread/example.c
|
|
a full example program.
|
|
.TP
|
|
.B /mnt/temp
|
|
a place for
|
|
.I procexec
|
|
to create pipes.
|
|
.SH SOURCE
|
|
.B /sys/src/libthread
|
|
.SH SEE ALSO
|
|
.IR intro (2),
|
|
.IR ioproc (2),
|
|
.IR lock (2)
|