2011-03-30 13:49:47 +00:00
|
|
|
.TH THREAD 2
|
|
|
|
.SH NAME
|
|
|
|
alt,
|
|
|
|
chanclose,
|
|
|
|
chancreate,
|
|
|
|
chanfree,
|
|
|
|
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)
|
2016-08-06 13:09:18 +00:00
|
|
|
int threadgetgrp(void)
|
2011-03-30 13:49:47 +00:00
|
|
|
int threadsetgrp(int group)
|
|
|
|
int threadpid(int id)
|
|
|
|
.XX
|
2015-05-25 11:03:45 +00:00
|
|
|
void threadint(int id)
|
2011-03-30 13:49:47 +00:00
|
|
|
void threadintgrp(int group)
|
|
|
|
void threadkill(int id)
|
2015-05-25 11:03:45 +00:00
|
|
|
void threadkillgrp(int group)
|
2011-03-30 13:49:47 +00:00
|
|
|
.XX
|
2019-04-08 22:18:15 +00:00
|
|
|
void threadsetname(char *fmt, ...)
|
2011-03-30 13:49:47 +00:00
|
|
|
char* threadgetname(void)
|
|
|
|
.XX
|
|
|
|
void** threaddata(void)
|
|
|
|
void** procdata(void)
|
|
|
|
.XX
|
|
|
|
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.
|
2016-08-06 13:09:18 +00:00
|
|
|
.I Threadgetgrp
|
2011-03-30 13:49:47 +00:00
|
|
|
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.
|
2019-12-19 06:42:15 +00:00
|
|
|
On error, they return and set
|
|
|
|
.IR errstr .
|
2011-03-30 13:49:47 +00:00
|
|
|
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" .
|
2015-03-04 09:20:31 +00:00
|
|
|
.I Chancreate
|
|
|
|
allocates a new channel for messages of size
|
2011-03-30 13:49:47 +00:00
|
|
|
.I elsize
|
|
|
|
and with a buffer holding
|
|
|
|
.I nel
|
|
|
|
messages.
|
|
|
|
If
|
|
|
|
.I nel
|
|
|
|
is zero, the channel is unbuffered.
|
|
|
|
.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,
|
2019-11-12 21:01:38 +00:00
|
|
|
eventually returning the index of the structure it executes.
|
2011-03-30 13:49:47 +00:00
|
|
|
.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
|
2019-11-12 21:01:38 +00:00
|
|
|
.I chanclose
|
2011-03-30 13:49:47 +00:00
|
|
|
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.
|
|
|
|
.SH SOURCE
|
|
|
|
.B /sys/src/libthread
|
|
|
|
.SH SEE ALSO
|
|
|
|
.IR intro (2),
|
|
|
|
.IR ioproc (2),
|
|
|
|
.IR lock (2)
|