tContinue fighting pthreads. Clean up thread library a bit too. - plan9port - [… | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 7966faa931bfa9cf4ca53dd2d5b6e1eb0f174411 | |
parent c6687d4591964cb13df87f55ec4770e778a4a55c | |
Author: rsc <devnull@localhost> | |
Date: Thu, 23 Sep 2004 03:01:36 +0000 | |
Continue fighting pthreads. | |
Clean up thread library a bit too. | |
Diffstat: | |
M src/libthread/chanprint.c | 4 +++- | |
M src/libthread/create.c | 227 ++++++++++++++++-------------… | |
M src/libthread/debug.c | 4 ++-- | |
M src/libthread/exec-unix.c | 14 +++++++++++++- | |
D src/libthread/exec.c | 82 -----------------------------… | |
M src/libthread/exit.c | 31 +----------------------------… | |
M src/libthread/id.c | 8 ++++---- | |
M src/libthread/iocall.c | 5 ++++- | |
M src/libthread/ioproc.c | 5 ++++- | |
M src/libthread/main.c | 152 ++++++++++-------------------… | |
M src/libthread/mkfile | 4 ++-- | |
M src/libthread/note.c | 6 +++++- | |
M src/libthread/proctab.c | 2 +- | |
M src/libthread/ref.c | 6 ++++++ | |
D src/libthread/rendez.c | 38 -----------------------------… | |
M src/libthread/sched.c | 355 ++++++++++++++++-------------… | |
A src/libthread/sleep.c | 38 +++++++++++++++++++++++++++++… | |
M src/libthread/texec.c | 11 ++++++++--- | |
M src/libthread/threadimpl.h | 110 ++++++++++++++---------------… | |
M src/libthread/ucontext.c | 13 +++++++------ | |
20 files changed, 500 insertions(+), 615 deletions(-) | |
--- | |
diff --git a/src/libthread/chanprint.c b/src/libthread/chanprint.c | |
t@@ -1,4 +1,6 @@ | |
-#include "threadimpl.h" | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <thread.h> | |
int | |
chanprint(Channel *c, char *fmt, ...) | |
diff --git a/src/libthread/create.c b/src/libthread/create.c | |
t@@ -1,45 +1,46 @@ | |
#include "threadimpl.h" | |
-Pqueue _threadpq; | |
-int _threadprocs; | |
-int __pthread_nonstandard_stacks; | |
+Pqueue _threadpq; /* list of all procs */ | |
+int _threadnprocs; /* count of procs */ | |
-static int nextID(void); | |
+static int newthreadid(void); | |
+static int newprocid(void); | |
/* | |
* Create and initialize a new Thread structure attached to a given proc. | |
*/ | |
-void | |
-_stackfree(void *v) | |
-{ | |
- free(v); | |
-} | |
- | |
-static int | |
-newthread(Proc *p, void (*f)(void *arg), void *arg, uint stacksize, char *name… | |
+int | |
+_newthread(Proc *p, void (*f)(void *arg), void *arg, uint stacksize, | |
+ char *name, int grp) | |
{ | |
int id; | |
Thread *t; | |
- char *s; | |
- __pthread_nonstandard_stacks = 1; | |
- if(stacksize < 32) | |
- sysfatal("bad stacksize %d", stacksize); | |
t = _threadmalloc(sizeof(Thread), 1); | |
- t->lastfd = -1; | |
- s = _threadmalloc(stacksize, 0); | |
- t->stk = (uchar*)s; | |
- t->stksize = stacksize; | |
- _threaddebugmemset(s, 0xFE, stacksize); | |
- _threadinitstack(t, f, arg); | |
t->proc = p; | |
t->grp = grp; | |
+ t->id = id = newthreadid(); | |
if(name) | |
- t->cmdname = strdup(name); | |
- t->id = nextID(); | |
- id = t->id; | |
- t->next = (Thread*)~0; | |
- _threaddebug(DBGSCHED, "create thread %d.%d name %s", p->pid, t->id, n… | |
+ t->name = strdup(name); | |
+ _threaddebug(DBGSCHED, "create thread %d.%d name %s", p->id, id, name); | |
+ | |
+ /* | |
+ * Allocate and clear stack. | |
+ */ | |
+ if(stacksize < 1024) | |
+ sysfatal("bad stacksize %d", stacksize); | |
+ t->stk = _threadmalloc(stacksize, 0); | |
+ t->stksize = stacksize; | |
+ _threaddebugmemset(t->stk, 0xFE, stacksize); | |
+ | |
+ /* | |
+ * Set up t->context to call f(arg). | |
+ */ | |
+ _threadinitstack(t, f, arg); | |
+ | |
+ /* | |
+ * Add thread to proc. | |
+ */ | |
lock(&p->lock); | |
p->nthreads++; | |
if(p->threads.head == nil) | |
t@@ -49,120 +50,134 @@ newthread(Proc *p, void (*f)(void *arg), void *arg, uint… | |
t->prevt->nextt = t; | |
} | |
p->threads.tail = t; | |
+ t->next = (Thread*)~0; | |
+ | |
+ /* | |
+ * Mark thread as ready to run. | |
+ */ | |
t->state = Ready; | |
_threadready(t); | |
unlock(&p->lock); | |
+ | |
return id; | |
} | |
-static int | |
-nextID(void) | |
-{ | |
- static Lock l; | |
- static int id; | |
- int i; | |
- | |
- lock(&l); | |
- i = ++id; | |
- unlock(&l); | |
- return i; | |
-} | |
- | |
-int | |
-procrfork(void (*f)(void *), void *arg, uint stacksize, int rforkflag) | |
+/* | |
+ * Free a Thread structure. | |
+ */ | |
+void | |
+_threadfree(Thread *t) | |
{ | |
- Proc *p; | |
- int id; | |
- | |
- p = _threadgetproc(); | |
- assert(p->newproc == nil); | |
- p->newproc = _newproc(f, arg, stacksize, nil, p->thread->grp, rforkfla… | |
- id = p->newproc->threads.head->id; | |
- _sched(); | |
- return id; | |
+ free(t->stk); | |
+ free(t->name); | |
+ free(t); | |
} | |
-int | |
-proccreate(void (*f)(void*), void *arg, uint stacksize) | |
+/* | |
+ * Create and initialize a new Proc structure with a single Thread | |
+ * running inside it. Add the Proc to the global process list. | |
+ */ | |
+Proc* | |
+_newproc(void) | |
{ | |
Proc *p; | |
- p = _threadgetproc(); | |
- if(p->idle){ | |
- fprint(2, "cannot create procs once there is an idle thread\n"… | |
- werrstr("cannot create procs once there is an idle thread"); | |
- return -1; | |
- } | |
- return procrfork(f, arg, stacksize, 0); | |
+ /* | |
+ * Allocate. | |
+ */ | |
+ p = _threadmalloc(sizeof *p, 1); | |
+ p->id = newprocid(); | |
+ | |
+ /* | |
+ * Add to list. Record if we're now multiprocess. | |
+ */ | |
+ lock(&_threadpq.lock); | |
+ if(_threadpq.head == nil) | |
+ _threadpq.head = p; | |
+ else | |
+ *_threadpq.tail = p; | |
+ _threadpq.tail = &p->next; | |
+ if(_threadnprocs == 1) | |
+ _threadmultiproc(); | |
+ _threadnprocs++; | |
+ unlock(&_threadpq.lock); | |
+ | |
+ return p; | |
} | |
-void | |
-_freeproc(Proc *p) | |
+/* | |
+ * Allocate a new thread running f(arg) on a stack of size stacksize. | |
+ * Return the thread id. The thread group inherits from the current thread. | |
+ */ | |
+int | |
+threadcreate(void (*f)(void*), void *arg, uint stacksize) | |
{ | |
- Thread *t, *nextt; | |
- | |
- for(t = p->threads.head; t; t = nextt){ | |
- if(t->cmdname) | |
- free(t->cmdname); | |
- assert(t->stk != nil); | |
- _stackfree(t->stk); | |
- nextt = t->nextt; | |
- free(t); | |
- } | |
- free(p); | |
+ return _newthread(_threadgetproc(), f, arg, stacksize, nil, threadgetg… | |
} | |
/* | |
- * Create a new thread and schedule it to run. | |
- * The thread grp is inherited from the currently running thread. | |
+ * Allocate a new idle thread. Only allowed in a single-proc program. | |
*/ | |
int | |
-threadcreate(void (*f)(void *arg), void *arg, uint stacksize) | |
+threadcreateidle(void (*f)(void *arg), void *arg, uint stacksize) | |
{ | |
- return newthread(_threadgetproc(), f, arg, stacksize, nil, threadgetgr… | |
+ int id; | |
+ | |
+ assert(_threadnprocs == 1); | |
+ | |
+ id = threadcreate(f, arg, stacksize); | |
+ _threaddebug(DBGSCHED, "idle is %d", id); | |
+ _threadsetidle(id); | |
+ return id; | |
} | |
+/* | |
+ * Threadcreate, but do it inside a fresh proc. | |
+ */ | |
int | |
-threadcreateidle(void (*f)(void *arg), void *arg, uint stacksize) | |
+proccreate(void (*f)(void*), void *arg, uint stacksize) | |
{ | |
int id; | |
+ Proc *p, *np; | |
- if(_threadprocs!=1){ | |
- fprint(2, "cannot have idle thread in multi-proc program\n"); | |
- werrstr("cannot have idle thread in multi-proc program"); | |
- return -1; | |
- } | |
- id = newthread(_threadgetproc(), f, arg, stacksize, nil, threadgetgrp(… | |
- _threaddebug(DBGSCHED, "idle is %d", id); | |
- _threadidle(); | |
+ p = _threadgetproc(); | |
+ np = _newproc(); | |
+ p->newproc = np; | |
+ p->schedfn = _threadstartproc; | |
+ id = _newthread(np, f, arg, stacksize, nil, p->thread->grp); | |
+ _sched(); /* call into scheduler to create proc XXX */ | |
return id; | |
} | |
/* | |
- * Create and initialize a new Proc structure with a single Thread | |
- * running inside it. Add the Proc to the global process list. | |
+ * Allocate a new thread id. | |
*/ | |
-Proc* | |
-_newproc(void (*f)(void *arg), void *arg, uint stacksize, char *name, int grp,… | |
+static int | |
+newthreadid(void) | |
{ | |
- Proc *p; | |
+ static Lock l; | |
+ static int id; | |
+ int i; | |
- p = _threadmalloc(sizeof *p, 1); | |
- p->pid = -1; | |
- p->rforkflag = rforkflag; | |
- newthread(p, f, arg, stacksize, name, grp); | |
+ lock(&l); | |
+ i = ++id; | |
+ unlock(&l); | |
+ return i; | |
+} | |
- lock(&_threadpq.lock); | |
- if(_threadpq.head == nil) | |
- _threadpq.head = p; | |
- else | |
- *_threadpq.tail = p; | |
- _threadpq.tail = &p->next; | |
+/* | |
+ * Allocate a new proc id. | |
+ */ | |
+static int | |
+newprocid(void) | |
+{ | |
+ static Lock l; | |
+ static int id; | |
+ int i; | |
- if(_threadprocs == 1) | |
- _threadmultiproc(); | |
- _threadprocs++; | |
- unlock(&_threadpq.lock); | |
- return p; | |
+ lock(&l); | |
+ i = ++id; | |
+ unlock(&l); | |
+ return i; | |
} | |
diff --git a/src/libthread/debug.c b/src/libthread/debug.c | |
t@@ -19,9 +19,9 @@ __threaddebug(ulong flag, char *fmt, ...) | |
if(p==nil) | |
fmtprint(&f, "noproc "); | |
else if(p->thread) | |
- fmtprint(&f, "%d.%d ", p->pid, p->thread->id); | |
+ fmtprint(&f, "%d.%d ", p->id, p->thread->id); | |
else | |
- fmtprint(&f, "%d._ ", p->pid); | |
+ fmtprint(&f, "%d._ ", p->id); | |
va_start(arg, fmt); | |
fmtvprint(&f, fmt, arg); | |
diff --git a/src/libthread/exec-unix.c b/src/libthread/exec-unix.c | |
t@@ -10,9 +10,20 @@ _threadexec(Channel *pidc, int fd[3], char *prog, char *arg… | |
int pfd[2]; | |
int n, pid; | |
char exitstr[ERRMAX]; | |
+ static int firstexec = 1; | |
+ static Lock lk; | |
_threaddebug(DBGEXEC, "threadexec %s", prog); | |
- | |
+ | |
+ if(firstexec){ | |
+ lock(&lk); | |
+ if(firstexec){ | |
+ firstexec = 0; | |
+ _threadfirstexec(); | |
+ } | |
+ unlock(&lk); | |
+ } | |
+ | |
/* | |
* We want threadexec to behave like exec; if exec succeeds, | |
* never return, and if it fails, return with errstr set. | |
t@@ -41,6 +52,7 @@ _threadexec(Channel *pidc, int fd[3], char *prog, char *args… | |
efork(fd, pfd, prog, args); | |
_exit(0); | |
default: | |
+ _threadafterexec(); | |
if(freeargs) | |
free(args); | |
break; | |
diff --git a/src/libthread/exec.c b/src/libthread/exec.c | |
t@@ -1,82 +0,0 @@ | |
-#include "threadimpl.h" | |
- | |
-#define PIPEMNT "/mnt/temp" | |
- | |
-void | |
-procexec(Channel *pidc, int fd[3], char *prog, char *args[]) | |
-{ | |
- int n; | |
- Proc *p; | |
- Thread *t; | |
- | |
- _threaddebug(DBGEXEC, "procexec %s", prog); | |
- /* must be only thread in proc */ | |
- p = _threadgetproc(); | |
- t = p->thread; | |
- if(p->threads.head != t || p->threads.head->nextt != nil){ | |
- werrstr("not only thread in proc"); | |
- Bad: | |
- if(pidc) | |
- sendul(pidc, ~0); | |
- return; | |
- } | |
- | |
- /* | |
- * We want procexec to behave like exec; if exec succeeds, | |
- * never return, and if it fails, return with errstr set. | |
- * Unfortunately, the exec happens in another proc since | |
- * we have to wait for the exec'ed process to finish. | |
- * To provide the semantics, we open a pipe with the | |
- * write end close-on-exec and hand it to the proc that | |
- * is doing the exec. If the exec succeeds, the pipe will | |
- * close so that our read below fails. If the exec fails, | |
- * then the proc doing the exec sends the errstr down the | |
- * pipe to us. | |
- */ | |
- if(bind("#|", PIPEMNT, MREPL) < 0) | |
- goto Bad; | |
- if((p->exec.fd[0] = open(PIPEMNT "/data", OREAD)) < 0){ | |
- unmount(nil, PIPEMNT); | |
- goto Bad; | |
- } | |
- if((p->exec.fd[1] = open(PIPEMNT "/data1", OWRITE|OCEXEC)) < 0){ | |
- close(p->exec.fd[0]); | |
- unmount(nil, PIPEMNT); | |
- goto Bad; | |
- } | |
- unmount(nil, PIPEMNT); | |
- | |
- /* exec in parallel via the scheduler */ | |
- assert(p->needexec==0); | |
- p->exec.prog = prog; | |
- p->exec.args = args; | |
- p->exec.stdfd = fd; | |
- p->needexec = 1; | |
- _sched(); | |
- | |
- close(p->exec.fd[1]); | |
- if((n = read(p->exec.fd[0], p->exitstr, ERRMAX-1)) > 0){ /* exe… | |
- p->exitstr[n] = '\0'; | |
- errstr(p->exitstr, ERRMAX); | |
- close(p->exec.fd[0]); | |
- goto Bad; | |
- } | |
- close(p->exec.fd[0]); | |
- close(fd[0]); | |
- if(fd[1] != fd[0]) | |
- close(fd[1]); | |
- if(fd[2] != fd[1] && fd[2] != fd[0]) | |
- close(fd[2]); | |
- if(pidc) | |
- sendul(pidc, t->ret); | |
- | |
- /* wait for exec'ed program, then exit */ | |
- _schedexecwait(); | |
-} | |
- | |
-void | |
-procexecl(Channel *pidc, int fd[3], char *f, ...) | |
-{ | |
- procexec(pidc, fd, f, &f+1); | |
-} | |
- | |
diff --git a/src/libthread/exit.c b/src/libthread/exit.c | |
t@@ -26,42 +26,13 @@ threadexits(char *exitstr) | |
void | |
threadexitsall(char *exitstr) | |
{ | |
- Proc *p; | |
- int *pid; | |
- int i, npid, mypid; | |
- | |
_threaddebug(DBGSCHED, "threadexitsall %s", exitstr); | |
if(exitstr == nil) | |
exitstr = ""; | |
_threadexitsallstatus = exitstr; | |
_threaddebug(DBGSCHED, "_threadexitsallstatus set to %p", _threadexits… | |
- mypid = _threadgetpid(); | |
- | |
- /* | |
- * signal others. | |
- * copying all the pids first avoids other thread's | |
- * teardown procedures getting in the way. | |
- */ | |
- lock(&_threadpq.lock); | |
- npid = 0; | |
- for(p=_threadpq.head; p; p=p->next) | |
- npid++; | |
- pid = _threadmalloc(npid*sizeof(pid[0]), 0); | |
- npid = 0; | |
- for(p = _threadpq.head; p; p=p->next) | |
- pid[npid++] = p->pid; | |
- unlock(&_threadpq.lock); | |
- for(i=0; i<npid; i++){ | |
- _threaddebug(DBGSCHED, "threadexitsall kill %d", pid[i]); | |
- if(pid[i]==0 || pid[i]==-1) | |
- fprint(2, "bad pid in threadexitsall: %d\n", pid[i]); | |
- else if(pid[i] != mypid){ | |
- kill(pid[i], SIGTERM); | |
- } | |
- } | |
- | |
/* leave */ | |
- exits(0); | |
+ _threadexitallproc(exitstr); | |
} | |
Channel* | |
diff --git a/src/libthread/id.c b/src/libthread/id.c | |
t@@ -60,10 +60,10 @@ threadsetname(char *fmt, ...) | |
p = _threadgetproc(); | |
t = p->thread; | |
- if (t->cmdname) | |
- free(t->cmdname); | |
+ if(t->name) | |
+ free(t->name); | |
va_start(arg, fmt); | |
- t->cmdname = vsmprint(fmt, arg); | |
+ t->name = vsmprint(fmt, arg); | |
va_end(arg); | |
/* Plan 9 only | |
t@@ -85,7 +85,7 @@ threadsetname(char *fmt, ...) | |
char* | |
threadgetname(void) | |
{ | |
- return _threadgetproc()->thread->cmdname; | |
+ return _threadgetproc()->thread->name; | |
} | |
void** | |
diff --git a/src/libthread/iocall.c b/src/libthread/iocall.c | |
t@@ -1,4 +1,7 @@ | |
-#include "threadimpl.h" | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <thread.h> | |
+#include "ioproc.h" | |
long | |
iocall(Ioproc *io, long (*op)(va_list*), ...) | |
diff --git a/src/libthread/ioproc.c b/src/libthread/ioproc.c | |
t@@ -1,4 +1,7 @@ | |
-#include "threadimpl.h" | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <thread.h> | |
+#include "ioproc.h" | |
enum | |
{ | |
diff --git a/src/libthread/main.c b/src/libthread/main.c | |
t@@ -1,135 +1,79 @@ | |
-#include <u.h> | |
-#include <signal.h> | |
+/* | |
+ * Thread library. | |
+ */ | |
+ | |
#include "threadimpl.h" | |
typedef struct Mainarg Mainarg; | |
struct Mainarg | |
{ | |
- int argc; | |
- char **argv; | |
+ int argc; | |
+ char **argv; | |
}; | |
-int mainstacksize; | |
-int _threadnotefd; | |
-int _threadpasserpid; | |
-static void mainlauncher(void*); | |
+int mainstacksize; | |
extern void (*_sysfatal)(char*, va_list); | |
-void | |
-_threadstatus(int x) | |
-{ | |
- USED(x); | |
- threadstatus(); | |
-} | |
- | |
-void | |
-_threaddie(int x) | |
-{ | |
- extern char *_threadexitsallstatus; | |
- USED(x); | |
- | |
- if(_threadexitsallstatus) | |
- _exits(_threadexitsallstatus); | |
-} | |
- | |
-int | |
-main(int argc, char **argv) | |
-{ | |
- Mainarg *a; | |
- Proc *p; | |
- | |
-//_threaddebuglevel = (DBGSCHED|DBGCHAN|DBGREND)^~0; | |
- _systhreadinit(); | |
- _qlockinit(_threadsleep, _threadwakeup); | |
- _sysfatal = _threadsysfatal; | |
- notify(_threadnote); | |
- if(mainstacksize == 0) | |
- mainstacksize = 32*1024; | |
- | |
- a = _threadmalloc(sizeof *a, 1); | |
- a->argc = argc; | |
- a->argv = argv; | |
- p = _newproc(mainlauncher, a, mainstacksize, "threadmain", 0, 0); | |
- _scheduler(p); | |
- abort(); /* not reached */ | |
- return 0; | |
-} | |
- | |
static void | |
mainlauncher(void *arg) | |
{ | |
Mainarg *a; | |
a = arg; | |
+ _threadmaininit(); | |
threadmain(a->argc, a->argv); | |
threadexits("threadmain"); | |
} | |
-void | |
-_threadsignal(void) | |
-{ | |
-} | |
- | |
-void | |
-_threadsignalpasser(void) | |
-{ | |
-} | |
- | |
int | |
-_schedfork(Proc *p) | |
+main(int argc, char **argv) | |
{ | |
- int pid; | |
- lock(&p->lock); | |
- pid = ffork(RFMEM|RFNOWAIT, _scheduler, p); | |
- p->pid = pid; | |
- unlock(&p->lock); | |
- return pid; | |
- | |
-} | |
+ Mainarg a; | |
+ Proc *p; | |
-void | |
-_schedexit(Proc *p) | |
-{ | |
- char ex[ERRMAX]; | |
- Proc **l; | |
+ /* | |
+ * XXX Do daemonize hack here. | |
+ */ | |
+ | |
+ /* | |
+ * Instruct QLock et al. to use our scheduling functions | |
+ * so that they can operate at the thread level. | |
+ */ | |
+ _qlockinit(_threadsleep, _threadwakeup); | |
- lock(&_threadpq.lock); | |
- for(l=&_threadpq.head; *l; l=&(*l)->next){ | |
- if(*l == p){ | |
- *l = p->next; | |
- if(*l == nil) | |
- _threadpq.tail = l; | |
- break; | |
- } | |
- } | |
- _threadprocs--; | |
- unlock(&_threadpq.lock); | |
+ /* | |
+ * Install our own _threadsysfatal which takes down | |
+ * the whole conglomeration of procs. | |
+ */ | |
+ _sysfatal = _threadsysfatal; | |
- strncpy(ex, p->exitstr, sizeof ex); | |
- ex[sizeof ex-1] = '\0'; | |
- free(p); | |
- _exits(ex); | |
-} | |
+ /* | |
+ * XXX Install our own jump handler. | |
+ */ | |
-int | |
-nrand(int n) | |
-{ | |
- return random()%n; | |
-} | |
+ /* | |
+ * Install our own signal handlers. | |
+ */ | |
+ notify(_threadnote); | |
-void | |
-_systhreadinit(void) | |
-{ | |
+ /* | |
+ * Construct the initial proc running mainlauncher(&a). | |
+ */ | |
+ if(mainstacksize == 0) | |
+ mainstacksize = 32*1024; | |
+ a.argc = argc; | |
+ a.argv = argv; | |
+ p = _newproc(); | |
+ _newthread(p, mainlauncher, &a, mainstacksize, "threadmain", 0); | |
+ _threadscheduler(p); | |
+ abort(); /* not reached */ | |
+ return 0; | |
} | |
+/* | |
+ * No-op function here so that sched.o drags in main.o. | |
+ */ | |
void | |
-threadstats(void) | |
+_threadlinkmain(void) | |
{ | |
- extern int _threadnrendez, _threadhighnrendez, | |
- _threadnalt, _threadhighnentry; | |
- fprint(2, "*** THREAD LIBRARY STATS ***\n"); | |
- fprint(2, "nrendez %d high simultaneous %d\n", | |
- _threadnrendez, _threadhighnrendez); | |
- fprint(2, "nalt %d high simultaneous entry %d\n", | |
- _threadnalt, _threadhighnentry); | |
} | |
diff --git a/src/libthread/mkfile b/src/libthread/mkfile | |
t@@ -28,11 +28,11 @@ OFILES=\ | |
memset.$O\ | |
memsetd.$O\ | |
note.$O\ | |
- proctab.$O\ | |
+ pthread.$O\ | |
read9pmsg.$O\ | |
ref.$O\ | |
- rendez.$O\ | |
sched.$O\ | |
+ sleep.$O\ | |
HFILES=\ | |
$PLAN9/include/thread.h\ | |
diff --git a/src/libthread/note.c b/src/libthread/note.c | |
t@@ -65,6 +65,8 @@ delayednotes(Proc *p, void *v) | |
} | |
if(i==NFN){ | |
_threaddebug(DBGNOTE, "Unhandled note %s, proc… | |
+ if(strcmp(n->s, "sys: child") == 0) | |
+ noted(NCONT); | |
fprint(2, "unhandled note %s, pid %d\n", n->s,… | |
if(v != nil) | |
noted(NDFLT); | |
t@@ -85,7 +87,9 @@ _threadnote(void *v, char *s) | |
Note *n; | |
_threaddebug(DBGNOTE, "Got note %s", s); | |
- if(strncmp(s, "sys:", 4) == 0 && strcmp(s, "sys: write on closed pipe"… | |
+ if(strncmp(s, "sys:", 4) == 0 | |
+ && strcmp(s, "sys: write on closed pipe") != 0 | |
+ && strcmp(s, "sys: child") != 0) | |
noted(NDFLT); | |
// if(_threadexitsallstatus){ | |
diff --git a/src/libthread/proctab.c b/src/libthread/proctab.c | |
t@@ -46,7 +46,7 @@ __threadgetproc(int rm) | |
if(!multi) | |
return theproc; | |
- pid = _threadgetpid(); | |
+ pid = getpid(); | |
lock(&ptablock); | |
h = ((unsigned)pid)%PTABHASH; | |
diff --git a/src/libthread/ref.c b/src/libthread/ref.c | |
t@@ -1,3 +1,9 @@ | |
+/* | |
+ * Atomic reference counts - used by applications. | |
+ * | |
+ * We use locks to avoid the assembly of the Plan 9 versions. | |
+ */ | |
+ | |
#include "threadimpl.h" | |
void | |
diff --git a/src/libthread/rendez.c b/src/libthread/rendez.c | |
t@@ -1,38 +0,0 @@ | |
-#include "threadimpl.h" | |
- | |
-int _threadhighnrendez; | |
-int _threadnrendez; | |
- | |
-void | |
-_threadsleep(_Procrend *r) | |
-{ | |
- Thread *t; | |
- | |
- t = _threadgetproc()->thread; | |
- r->arg = t; | |
- t->nextstate = Rendezvous; | |
- t->inrendez = 1; | |
- unlock(r->l); | |
- _sched(); | |
- t->inrendez = 0; | |
- lock(r->l); | |
-} | |
- | |
-void | |
-_threadwakeup(_Procrend *r) | |
-{ | |
- Thread *t; | |
- | |
- t = r->arg; | |
- while(t->state == Running) | |
- sleep(0); | |
- lock(&t->proc->lock); | |
- if(t->state == Dead){ | |
- unlock(&t->proc->lock); | |
- return; | |
- } | |
- assert(t->state == Rendezvous && t->inrendez); | |
- t->state = Ready; | |
- _threadready(t); | |
- unlock(&t->proc->lock); | |
-} | |
diff --git a/src/libthread/sched.c b/src/libthread/sched.c | |
t@@ -1,76 +1,59 @@ | |
-#include <u.h> | |
-#include <signal.h> | |
-#include <errno.h> | |
+/* | |
+ * Thread scheduler. | |
+ */ | |
#include "threadimpl.h" | |
-static Thread *runthread(Proc*); | |
- | |
-static char *_psstate[] = { | |
- "Dead", | |
- "Running", | |
- "Ready", | |
- "Rendezvous", | |
-}; | |
- | |
-static char* | |
-psstate(int s) | |
-{ | |
- if(s < 0 || s >= nelem(_psstate)) | |
- return "unknown"; | |
- return _psstate[s]; | |
-} | |
- | |
-void | |
-needstack(int howmuch) | |
-{ | |
- Proc *p; | |
- Thread *t; | |
- | |
- p = _threadgetproc(); | |
- if(p == nil || (t=p->thread) == nil) | |
- return; | |
- if((ulong)&howmuch < (ulong)t->stk+howmuch){ /* stack overflow … | |
- fprint(2, "stack overflow: stack at 0x%lux, limit at 0x%lux, n… | |
- abort(); | |
- } | |
-} | |
+static Thread *runthread(Proc*); | |
+static void schedexit(Proc*); | |
+/* | |
+ * Main scheduling loop. | |
+ */ | |
void | |
-_scheduler(void *arg) | |
+_threadscheduler(void *arg) | |
{ | |
Proc *p; | |
Thread *t; | |
p = arg; | |
- lock(&p->lock); | |
- p->pid = _threadgetpid(); | |
- _threadsetproc(p); | |
+ | |
+ _threadlinkmain(); | |
+ _threadinitproc(p); | |
for(;;){ | |
+ /* | |
+ * Clean up zombie children. | |
+ */ | |
+ _threadwaitkids(p); | |
+ | |
+ /* | |
+ * Find next thread to run. | |
+ */ | |
+ _threaddebug(DBGSCHED, "runthread"); | |
t = runthread(p); | |
- if(t == nil){ | |
- _threaddebug(DBGSCHED, "all threads gone; exiting"); | |
- _threaddelproc(); | |
- _schedexit(p); | |
- } | |
- _threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id); | |
- p->thread = t; | |
- if(t->moribund){ | |
- _threaddebug(DBGSCHED, "%d.%d marked to die"); | |
- goto Moribund; | |
+ if(t == nil) | |
+ schedexit(p); | |
+ | |
+ /* | |
+ * If it's ready, run it (might instead be marked to die). | |
+ */ | |
+ lock(&p->lock); | |
+ if(t->state == Ready){ | |
+ _threaddebug(DBGSCHED, "running %d.%d", p->id, t->id); | |
+ t->state = Running; | |
+ t->nextstate = Ready; | |
+ p->thread = t; | |
+ unlock(&p->lock); | |
+ _swaplabel(&p->context, &t->context); | |
+ lock(&p->lock); | |
+ p->thread = nil; | |
} | |
- t->state = Running; | |
- t->nextstate = Ready; | |
- unlock(&p->lock); | |
- _swaplabel(&p->sched, &t->sched); | |
- | |
- lock(&p->lock); | |
- p->thread = nil; | |
+ /* | |
+ * If thread needs to die, kill it. | |
+ */ | |
if(t->moribund){ | |
- Moribund: | |
- if(t->moribund != 1) | |
- fprint(2, "moribund %d\n", t->moribund); | |
+ _threaddebug(DBGSCHED, "moribund %d.%d", p->id, t->id); | |
assert(t->moribund == 1); | |
t->state = Dead; | |
if(t->prevt) | |
t@@ -82,40 +65,37 @@ _scheduler(void *arg) | |
else | |
p->threads.tail = t->prevt; | |
unlock(&p->lock); | |
- if(t->inrendez){ | |
- abort(); | |
- // _threadflagrendez(t); | |
- // _threadbreakrendez(); | |
- } | |
- _stackfree(t->stk); | |
- free(t->cmdname); | |
- free(t); /* XXX how do we know there are no ref… | |
+ _threadfree(t); | |
p->nthreads--; | |
t = nil; | |
- lock(&p->lock); | |
continue; | |
} | |
-/* | |
- if(p->needexec){ | |
- t->ret = _schedexec(&p->exec); | |
- p->needexec = 0; | |
- } | |
-*/ | |
- if(p->newproc){ | |
- t->ret = _schedfork(p->newproc); | |
- if(t->ret < 0){ | |
-//fprint(2, "_schedfork: %r\n"); | |
- abort(); | |
- } | |
- p->newproc = nil; | |
+ unlock(&p->lock); | |
+ | |
+ /* | |
+ * If there is a request to run a function on the | |
+ * scheduling stack, do so. | |
+ */ | |
+ if(p->schedfn){ | |
+ _threaddebug(DBGSCHED, "schedfn"); | |
+ p->schedfn(p); | |
+ p->schedfn = nil; | |
+ _threaddebug(DBGSCHED, "schedfn ended"); | |
} | |
+ | |
+ /* | |
+ * Move the thread along. | |
+ */ | |
t->state = t->nextstate; | |
+ _threaddebug(DBGSCHED, "moveon %d.%d", p->id, t->id); | |
if(t->state == Ready) | |
_threadready(t); | |
- unlock(&p->lock); | |
} | |
} | |
+/* | |
+ * Called by thread to give up control of processor to scheduler. | |
+ */ | |
int | |
_sched(void) | |
{ | |
t@@ -125,166 +105,195 @@ _sched(void) | |
p = _threadgetproc(); | |
t = p->thread; | |
assert(t != nil); | |
- _swaplabel(&t->sched, &p->sched); | |
+ _swaplabel(&t->context, &p->context); | |
return p->nsched++; | |
} | |
+/* | |
+ * Called by thread to yield the processor to other threads. | |
+ * Returns number of other threads run between call and return. | |
+ */ | |
+int | |
+yield(void) | |
+{ | |
+ Proc *p; | |
+ int nsched; | |
+ | |
+ p = _threadgetproc(); | |
+ nsched = p->nsched; | |
+ return _sched() - nsched; | |
+} | |
+ | |
+/* | |
+ * Choose the next thread to run. | |
+ */ | |
static Thread* | |
runthread(Proc *p) | |
{ | |
- Channel *c; | |
Thread *t; | |
Tqueue *q; | |
- Waitmsg *w; | |
- int e, sent; | |
+ /* | |
+ * No threads left? | |
+ */ | |
if(p->nthreads==0 || (p->nthreads==1 && p->idle)) | |
return nil; | |
- q = &p->ready; | |
-relock: | |
+ | |
lock(&p->readylock); | |
- if(p->nsched%128 == 0){ | |
- /* clean up children */ | |
- e = errno; | |
- if((c = _threadwaitchan) != nil){ | |
- if(c->n <= c->s){ | |
- sent = 0; | |
- for(;;){ | |
- if((w = p->waitmsg) != nil) | |
- p->waitmsg = nil; | |
- else | |
- w = waitnohang(); | |
- if(w == nil) | |
- break; | |
- if(sent == 0){ | |
- unlock(&p->readylock); | |
- sent = 1; | |
- } | |
- if(nbsendp(c, w) != 1) | |
- break; | |
- } | |
- p->waitmsg = w; | |
- if(sent) | |
- goto relock; | |
- } | |
- }else{ | |
- while((w = waitnohang()) != nil) | |
- free(w); | |
- } | |
- errno = e; | |
- } | |
+ q = &p->ready; | |
if(q->head == nil){ | |
+ /* | |
+ * Is this a single-process program with an idle thread? | |
+ */ | |
if(p->idle){ | |
- if(p->idle->state != Ready){ | |
- fprint(2, "everyone is asleep\n"); | |
- exits("everyone is asleep"); | |
- } | |
+ /* | |
+ * The idle thread had better be ready! | |
+ */ | |
+ if(p->idle->state != Ready) | |
+ sysfatal("all threads are asleep"); | |
+ | |
+ /* | |
+ * Run the idle thread. | |
+ */ | |
unlock(&p->readylock); | |
_threaddebug(DBGSCHED, "running idle thread", p->nthre… | |
return p->idle; | |
} | |
- _threaddebug(DBGSCHED, "sleeping for more work (%d threads)", … | |
+ /* | |
+ * Wait until one of our threads is readied (by another proc!). | |
+ */ | |
q->asleep = 1; | |
p->rend.l = &p->readylock; | |
_procsleep(&p->rend); | |
+ | |
+ /* | |
+ * Maybe we were awakened to exit? | |
+ */ | |
if(_threadexitsallstatus) | |
_exits(_threadexitsallstatus); | |
+ | |
+ assert(q->head != nil); | |
} | |
+ | |
t = q->head; | |
q->head = t->next; | |
unlock(&p->readylock); | |
- return t; | |
-} | |
-long | |
-threadstack(void) | |
-{ | |
- Proc *p; | |
- Thread *t; | |
- | |
- p = _threadgetproc(); | |
- t = p->thread; | |
- return (ulong)&p - (ulong)t->stk; | |
+ return t; | |
} | |
+/* | |
+ * Add a newly-ready thread to its proc's run queue. | |
+ */ | |
void | |
_threadready(Thread *t) | |
{ | |
Tqueue *q; | |
+ /* | |
+ * The idle thread does not go on the run queue. | |
+ */ | |
if(t == t->proc->idle){ | |
_threaddebug(DBGSCHED, "idle thread is ready"); | |
return; | |
} | |
assert(t->state == Ready); | |
- _threaddebug(DBGSCHED, "readying %d.%d", t->proc->pid, t->id); | |
+ _threaddebug(DBGSCHED, "readying %d.%d", t->proc->id, t->id); | |
+ | |
+ /* | |
+ * Add thread to run queue. | |
+ */ | |
q = &t->proc->ready; | |
lock(&t->proc->readylock); | |
+ | |
t->next = nil; | |
- if(q->head==nil) | |
+ if(q->head == nil) | |
q->head = t; | |
else | |
q->tail->next = t; | |
q->tail = t; | |
+ | |
+ /* | |
+ * Wake proc scheduler if it is sleeping. | |
+ */ | |
if(q->asleep){ | |
assert(q->asleep == 1); | |
q->asleep = 0; | |
- /* lock passes to runthread */ | |
_procwakeup(&t->proc->rend); | |
} | |
unlock(&t->proc->readylock); | |
- if(_threadexitsallstatus) | |
- _exits(_threadexitsallstatus); | |
} | |
+/* | |
+ * Mark the given thread as the idle thread. | |
+ * Since the idle thread was just created, it is sitting | |
+ * somewhere on the ready queue. | |
+ */ | |
void | |
-_threadidle(void) | |
+_threadsetidle(int id) | |
{ | |
Tqueue *q; | |
- Thread *t, *idle; | |
+ Thread *t, **l, *last; | |
Proc *p; | |
p = _threadgetproc(); | |
- q = &p->ready; | |
+ | |
lock(&p->readylock); | |
- assert(q->tail); | |
- idle = q->tail; | |
- if(q->head == idle){ | |
- q->head = nil; | |
- q->tail = nil; | |
- }else{ | |
- for(t=q->head; t->next!=q->tail; t=t->next) | |
- ; | |
- t->next = nil; | |
- q->tail = t; | |
- } | |
- p->idle = idle; | |
- _threaddebug(DBGSCHED, "p->idle is %d\n", idle->id); | |
- unlock(&p->readylock); | |
-} | |
-int | |
-yield(void) | |
-{ | |
- Proc *p; | |
- int nsched; | |
+ /* | |
+ * Find thread on ready queue. | |
+ */ | |
+ q = &p->ready; | |
+ for(l=&q->head, last=nil; (t=*l) != nil; l=&t->next, last=t) | |
+ if(t->id == id) | |
+ break; | |
+ assert(t != nil); | |
- p = _threadgetproc(); | |
- nsched = p->nsched; | |
- return _sched() - nsched; | |
+ /* | |
+ * Remove it from ready queue. | |
+ */ | |
+ *l = t->next; | |
+ if(t == q->head) | |
+ q->head = t->next; | |
+ if(t->next == nil) | |
+ q->tail = last; | |
+ | |
+ /* | |
+ * Set as idle thread. | |
+ */ | |
+ p->idle = t; | |
+ _threaddebug(DBGSCHED, "p->idle is %d\n", t->id); | |
+ unlock(&p->readylock); | |
} | |
-void | |
-threadstatus(void) | |
+static void | |
+schedexit(Proc *p) | |
{ | |
- Proc *p; | |
- Thread *t; | |
+ char ex[ERRMAX]; | |
+ int n; | |
+ Proc **l; | |
- p = _threadgetproc(); | |
- for(t=p->threads.head; t; t=t->nextt) | |
- fprint(2, "[%3d] %s userpc=%lux\n", | |
- t->id, psstate(t->state), t->userpc); | |
+ _threaddebug(DBGSCHED, "exiting proc %d", p->id); | |
+ lock(&_threadpq.lock); | |
+ for(l=&_threadpq.head; *l; l=&(*l)->next){ | |
+ if(*l == p){ | |
+ *l = p->next; | |
+ if(*l == nil) | |
+ _threadpq.tail = l; | |
+ break; | |
+ } | |
+ } | |
+ n = --_threadnprocs; | |
+ unlock(&_threadpq.lock); | |
+ | |
+ strncpy(ex, p->exitstr, sizeof ex); | |
+ ex[sizeof ex-1] = '\0'; | |
+ free(p); | |
+ if(n == 0) | |
+ _threadexitallproc(ex); | |
+ else | |
+ _threadexitproc(ex); | |
} | |
- | |
+ | |
diff --git a/src/libthread/sleep.c b/src/libthread/sleep.c | |
t@@ -0,0 +1,38 @@ | |
+#include "threadimpl.h" | |
+ | |
+int _threadhighnrendez; | |
+int _threadnrendez; | |
+ | |
+void | |
+_threadsleep(_Procrend *r) | |
+{ | |
+ Thread *t; | |
+ | |
+ t = _threadgetproc()->thread; | |
+ r->arg = t; | |
+ t->nextstate = Rendezvous; | |
+ t->asleep = 1; | |
+ unlock(r->l); | |
+ _sched(); | |
+ t->asleep = 0; | |
+ lock(r->l); | |
+} | |
+ | |
+void | |
+_threadwakeup(_Procrend *r) | |
+{ | |
+ Thread *t; | |
+ | |
+ t = r->arg; | |
+ while(t->state == Running) | |
+ sleep(0); | |
+ lock(&t->proc->lock); | |
+ if(t->state == Dead){ | |
+ unlock(&t->proc->lock); | |
+ return; | |
+ } | |
+ assert(t->state == Rendezvous && t->asleep); | |
+ t->state = Ready; | |
+ _threadready(t); | |
+ unlock(&t->proc->lock); | |
+} | |
diff --git a/src/libthread/texec.c b/src/libthread/texec.c | |
t@@ -5,11 +5,16 @@ extern int _threaddebuglevel; | |
void | |
doexec(void *v) | |
{ | |
+ int fd[3]; | |
char **argv = v; | |
-print("doexec\n"); | |
- procexec(nil, argv[0], argv); | |
+ fd[0] = dup(0, -1); | |
+ fd[1] = dup(1, -1); | |
+ fd[2] = dup(2, -1); | |
+ threadexec(nil, fd, argv[0], argv); | |
+ print("exec failed: %r\n"); | |
sendp(threadwaitchan(), nil); | |
+ threadexits(nil); | |
} | |
void | |
t@@ -28,7 +33,7 @@ threadmain(int argc, char **argv) | |
proccreate(doexec, argv, 8192); | |
w = recvp(c); | |
if(w == nil) | |
- print("exec failed: %r\n"); | |
+ print("exec/recvp failed: %r\n"); | |
else | |
print("%d %lud %lud %lud %s\n", w->pid, w->time[0], w->time[1]… | |
threadexits(nil); | |
diff --git a/src/libthread/threadimpl.h b/src/libthread/threadimpl.h | |
t@@ -7,31 +7,28 @@ | |
* _threadgetproc()->thread is always a live pointer. | |
* p->threads, p->ready, and _threadrgrp also contain | |
* live thread pointers. These may only be consulted | |
- * while holding p->lock or _threadrgrp.lock; in procs | |
- * other than p, the pointers are only guaranteed to be live | |
- * while the lock is still being held. | |
+ * while holding p->lock; in procs other than p, the | |
+ * pointers are only guaranteed to be live while the lock | |
+ * is still being held. | |
* | |
* Thread structures can only be freed by the proc | |
* they belong to. Threads marked with t->inrendez | |
* need to be extracted from the _threadrgrp before | |
* being freed. | |
- * | |
- * _threadrgrp.lock cannot be acquired while holding p->lock. | |
*/ | |
+#include <u.h> | |
#include <assert.h> | |
-#include <lib9.h> | |
+#include <libc.h> | |
#include <thread.h> | |
#include "label.h" | |
typedef struct Thread Thread; | |
-typedef struct Proc Proc; | |
+typedef struct Proc Proc; | |
typedef struct Tqueue Tqueue; | |
typedef struct Pqueue Pqueue; | |
-typedef struct Rgrp Rgrp; | |
typedef struct Execargs Execargs; | |
-/* must match list in sched.c */ | |
typedef enum | |
{ | |
Dead, | |
t@@ -50,17 +47,9 @@ typedef enum | |
enum | |
{ | |
- RENDHASH = 10009, | |
- Printsize = 2048, | |
NPRIV = 8, | |
}; | |
-struct Rgrp | |
-{ | |
- Lock lock; | |
- Thread *hash[RENDHASH]; | |
-}; | |
- | |
struct Tqueue /* Thread queue */ | |
{ | |
int asleep; | |
t@@ -68,27 +57,38 @@ struct Tqueue /* Thread queue */ | |
Thread *tail; | |
}; | |
+struct Pqueue { /* Proc queue */ | |
+ Lock lock; | |
+ Proc *head; | |
+ Proc **tail; | |
+}; | |
+ | |
struct Thread | |
{ | |
Lock lock; /* protects thread da… | |
- Label sched; /* for context switches */ | |
- int id; /* thread id */ | |
+ | |
+ int asleep; /* thread is in _threadsleep… | |
+ Label context; /* for context switches */ | |
int grp; /* thread group */ | |
+ int id; /* thread id */ | |
int moribund; /* thread needs to die */ | |
- State state; /* run state */ | |
- State nextstate; /* next run state */ | |
- uchar *stk; /* top of stack (lowest addr… | |
- uint stksize; /* stack size */ | |
+ char *name; /* name of thread */ | |
Thread *next; /* next on ready queue */ | |
- | |
- Proc *proc; /* proc of this thread */ | |
Thread *nextt; /* next on list of threads in thi… | |
+ State nextstate; /* next run state */ | |
+ Proc *proc; /* proc of this thread */ | |
Thread *prevt; /* prev on list of threads in thi… | |
int ret; /* return value for Exe… | |
+ State state; /* run state */ | |
+ uchar *stk; /* top of stack (lowest addr… | |
+ uint stksize; /* stack size */ | |
+ void* udata[NPRIV]; /* User per-thread data pointer */ | |
- char *cmdname; /* ptr to name of thread */ | |
+ /* | |
+ * for debugging only | |
+ * (could go away without impacting correct behavior): | |
+ */ | |
- int inrendez; | |
Channel *altc; | |
_Procrend altrend; | |
t@@ -96,10 +96,7 @@ struct Thread | |
Alt *alt; /* pointer to current … | |
ulong userpc; | |
Channel *c; | |
- pthread_cond_t cond; | |
- void* udata[NPRIV]; /* User per-thread data pointer */ | |
- int lastfd; | |
}; | |
struct Execargs | |
t@@ -113,12 +110,13 @@ struct Execargs | |
struct Proc | |
{ | |
Lock lock; | |
- Label sched; /* for context switches */ | |
- Proc *link; /* in proctab */ | |
- int pid; /* process id */ | |
+ | |
+ Label context; /* for context switches */ | |
+ Proc *link; /* in ptab */ | |
int splhi; /* delay notes */ | |
Thread *thread; /* running thread */ | |
Thread *idle; /* idle thread */ | |
+ int id; | |
int needexec; | |
Execargs exec; /* exec argument */ | |
t@@ -131,13 +129,15 @@ struct Proc | |
Tqueue ready; /* Runnable threads */ | |
Lock readylock; | |
- char printbuf[Printsize]; | |
int blocked; /* In a rendezvous */ | |
int pending; /* delayed note pending */ | |
int nonotes; /* delay notes */ | |
uint nextID; /* ID of most recently crea… | |
Proc *next; /* linked list of Procs */ | |
+ | |
+ void (*schedfn)(Proc*); /* function to call in s… | |
+ | |
_Procrend rend; /* sleep here for more ready threads */ | |
void *arg; /* passed between sha… | |
t@@ -147,29 +147,17 @@ struct Proc | |
void* udata; /* User per-proc data pointer */ | |
int nsched; | |
-}; | |
- | |
-struct Pqueue { /* Proc queue */ | |
- Lock lock; | |
- Proc *head; | |
- Proc **tail; | |
-}; | |
-struct Ioproc | |
-{ | |
- int tid; | |
- Channel *c, *creply; | |
- int inuse; | |
- long (*op)(va_list*); | |
- va_list arg; | |
- long ret; | |
- char err[ERRMAX]; | |
- Ioproc *next; | |
+ /* | |
+ * for debugging only | |
+ */ | |
+ int pid; /* process id */ | |
+ int pthreadid; /* pthread id */ | |
}; | |
void _swaplabel(Label*, Label*); | |
-void _freeproc(Proc*); | |
-Proc* _newproc(void(*)(void*), void*, uint, char*, int, int); | |
+Proc* _newproc(void); | |
+int _newthread(Proc*, void(*)(void*), void*, uint, char*, int); | |
int _procsplhi(void); | |
void _procsplx(int); | |
int _sched(void); | |
t@@ -177,7 +165,8 @@ int _schedexec(Execargs*); | |
void _schedexecwait(void); | |
void _schedexit(Proc*); | |
int _schedfork(Proc*); | |
-void _scheduler(void*); | |
+void _threadfree(Thread*); | |
+void _threadscheduler(void*); | |
void _systhreadinit(void); | |
void _threadassert(char*); | |
void __threaddebug(ulong, char*, ...); | |
t@@ -186,12 +175,15 @@ void _threadexitsall(char*); | |
Proc* _threadgetproc(void); | |
extern void _threadmultiproc(void); | |
Proc* _threaddelproc(void); | |
+void _threadinitproc(Proc*); | |
+void _threadwaitkids(Proc*); | |
void _threadsetproc(Proc*); | |
void _threadinitstack(Thread*, void(*)(void*), void*); | |
+void _threadlinkmain(void); | |
void* _threadmalloc(long, int); | |
void _threadnote(void*, char*); | |
void _threadready(Thread*); | |
-void _threadidle(void); | |
+void _threadsetidle(int); | |
void _threadsleep(_Procrend*); | |
void _threadwakeup(_Procrend*); | |
void _threadsignal(void); | |
t@@ -200,13 +192,15 @@ long _xdec(long*); | |
void _xinc(long*); | |
void _threadremove(Proc*, Thread*); | |
void threadstatus(void); | |
+void _threadstartproc(Proc*); | |
+void _threadexitproc(char*); | |
+void _threadexitallproc(char*); | |
+extern int _threadnprocs; | |
extern int _threaddebuglevel; | |
extern char* _threadexitsallstatus; | |
extern Pqueue _threadpq; | |
extern Channel* _threadwaitchan; | |
-extern Rgrp _threadrgrp; | |
-extern void _stackfree(void*); | |
#define DBGAPPL (1 << 0) | |
#define DBGSCHED (1 << 16) | |
t@@ -216,8 +210,6 @@ extern void _stackfree(void*); | |
#define DBGNOTE (1 << 20) | |
#define DBGEXEC (1 << 21) | |
-#define ioproc_arg(io, type) (va_arg((io)->arg, type)) | |
-extern int _threadgetpid(void); | |
extern void _threadmemset(void*, int, int); | |
extern void _threaddebugmemset(void*, int, int); | |
extern int _threadprocs; | |
diff --git a/src/libthread/ucontext.c b/src/libthread/ucontext.c | |
t@@ -6,17 +6,18 @@ _threadinitstack(Thread *t, void (*f)(void*), void *arg) | |
sigset_t zero; | |
/* do a reasonable initialization */ | |
- memset(&t->sched.uc, 0, sizeof t->sched.uc); | |
+ memset(&t->context.uc, 0, sizeof t->context.uc); | |
sigemptyset(&zero); | |
- sigprocmask(SIG_BLOCK, &zero, &t->sched.uc.uc_sigmask); | |
+ sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask); | |
/* call getcontext, because on Linux makecontext neglects floating poi… | |
- getcontext(&t->sched.uc); | |
+ getcontext(&t->context.uc); | |
/* call makecontext to do the real work. */ | |
- t->sched.uc.uc_stack.ss_sp = t->stk; | |
- t->sched.uc.uc_stack.ss_size = t->stksize; | |
- makecontext(&t->sched.uc, (void(*)())f, 1, arg); | |
+ /* leave a few words open on both ends */ | |
+ t->context.uc.uc_stack.ss_sp = t->stk+8; | |
+ t->context.uc.uc_stack.ss_size = t->stksize-16; | |
+ makecontext(&t->context.uc, (void(*)())f, 1, arg); | |
} | |
void |