#include <u.h>#include <libc.h>#include "assert.h"#include "threadimpl.h" static void initproc(void (*)(ulong, int, char*[]), int, char*[], uint);static void garbagethread(Thread *t);static Thread* getqbytag(Tqueue *, ulong);static void putq(Tqueue *, Thread *);static Thread* getq(Tqueue *);static void launcher(ulong, void (*)(void *), void *);static void mainlauncher(ulong, int argc, char *argv[]);static void garbageproc(Proc *);static Proc* prepproc(Newproc *);static int setnoteid(int);static int getnoteid(void); static int notefd;static int passerpid;static long totalmalloc; static Thread* dontkill; static Tqueue rendez;static Lock rendezlock;int mainstacksize;Channel* thewaitchan; struct Pqueue pq;Proc **procp; /* Pointer to pointer to proc's Proc structure */ #define STACKOFF 2#ifdef M386 #define FIX1 #define FIX2 *--tos = 0#endif#ifdef Mmips #define FIX1 #define FIX2#endif#ifdef Mpower /* This could be wrong ... */ #define FIX1 #define FIX2 *--tos = 0#endif#ifdef Malpha #define FIX1 *--tos = 0 #define FIX2 *--tos = 0#endif#ifdef Marm #define FIX1 #define FIX2#endif static intnextID(void){ static Lock l; static id; int i; lock(&l); i = ++id; unlock(&l); return i;} static intnotehandler(void *, char *s) { Proc *p; Thread *t; int id; if (DBGNOTE & _threaddebuglevel) threadprint(2, "Got note %s\n", s); if (getpid() == passerpid) { if (DBGNOTE & _threaddebuglevel) threadprint(2, "Notepasser %d got note %s\n", passerpid, s); if (strcmp(s, "kilpasser") == 0) exits(nil); write(notefd, s, strlen(s)); if (strncmp(s, "kilthr", 6) == 0 || strncmp(s, "kilgrp", 6) == 0) return 1; } else { if (strncmp(s, "kilthr", 6) == 0) { p = *procp; id = strtoul(s+6, nil, 10); if (DBGNOTE & _threaddebuglevel) threadprint(2, "Thread id %d\n", id); for (t = p->threads.head; t; t = t->nextt) { if (t->id == id) { break; } } if (t == nil) { if (DBGNOTE & _threaddebuglevel) threadprint(2, "Thread id %d not found\n", id); return 1; } if (t == p->curthread) { char err[ERRLEN] = "exiting"; if (DBGNOTE & _threaddebuglevel) threadprint(2, "Killing current thread\n"); errstr(err); } threadassert(t->state != Rendezvous); t->exiting = 1; return 1; } if (strncmp(s, "kilgrp", 6) == 0) { p = *procp; id = strtoul(s+6, nil, 10); if (DBGNOTE & _threaddebuglevel) threadprint(2, "Thread grp %d", id); for (t = p->threads.head; t; t = t->nextt) { if (t->grp == id) { if (t != p->curthread) t->exiting = 1; } } return 1; } } if (strncmp(s, "kilall", 6) == 0) exits(s+6); return 0;} voidthreadexits(char *exitstr) { Thread *t, *new; Proc *p; Channel *c; _threaddebug(DBGTHRD|DBGKILL, "Exitthread()"); p = *procp; t = p->curthread; threadassert(t->state == Running); if (p->nthreads > 1) { t->exiting = 1; p->nthreads--; lock(&rendezlock); while ((new = getq(&p->runnable)) == nil) { _threaddebug(DBGTHRD, "Nothing left to run"); /* called with rendezlock held */ p->blocked = 1; unlock(&rendezlock); if ((new = (Thread *)rendezvous((ulong)p, 0)) != (Thread *)~0) { threadassert(!p->blocked); threadassert(new->proc == p); p->curthread = new; new->state = Running; /* switch to new thread, pass it `t' for cleanup */ new->garbage = t; longjmp(new->env, (int)t); /* No return */ } _threaddebug(DBGNOTE|DBGTHRD, "interrupted"); lock(&rendezlock); } unlock(&rendezlock); _threaddebug(DBGTHRD, "Yield atexit to %d.%d", p->pid, new->id); p->curthread = new; new->state = Running; if (new->exiting) threadexits(nil); /* switch to new thread, pass it `t' for cleanup */ new->garbage = t; longjmp(new->env, (int)t); /* no return */ } /* * thewaitchan confounds exiting the entire program, so handle it * carefully; store exposed global in local variable c: */ if ((c = thewaitchan) != nil) { Waitmsg w; long t[4]; snprint(w.pid, sizeof(w.pid), "%d", p->pid); times(t); snprint(w.time + 0*12, 12, "%10.2fu", t[0]/1000.0); snprint(w.time + 1*12, 12, "%10.2fu", t[1]/1000.0); snprint(w.time + 2*12, 12, "%10.2fu", (t[0]+t[1]+t[2]+t[3])/1000.0); /* Cheating just a bit */ if (exitstr) strncpy(w.msg, exitstr, sizeof(w.msg)); else w.msg[0] = '\0'; _threaddebug(DBGCHLD, "In thread %s: sending exit status %s for %d\n", p->curthread->cmdname, exitstr, p->pid); send(c, &w); } _threaddebug(DBGPROC, "Exiting\n"); t->exiting = 1; if(exitstr == nil) p->str[0] = '\0'; else strncpy(p->str, exitstr, sizeof(p->str)); p->nthreads--; /* Clean up and exit */ longjmp(p->oldenv, DOEXIT);} static Thread *threadof(int id) { Proc *pp; Thread *t; lock(&pq.lock); for (pp = pq.head; pp->next; pp = pp->next) for (t = pp->threads.head; t; t = t->nextt) if (t->id == id) { unlock(&pq.lock); return t; } unlock(&pq.lock); return nil;} intthreadpid(int id) { Proc *pp; Thread *t; if (id < 0) return id; if (id == 0) return (*procp)->pid; lock(&pq.lock); for (pp = pq.head; pp->next; pp = pp->next) for (t = pp->threads.head; t; t = t->nextt) if (t->id == id) { unlock(&pq.lock); return pp->pid; } unlock(&pq.lock); return -1;} voidthreadkillgrp(int grp) { int fd; char buf[128]; sprint(buf, "/proc/%d/notepg", getpid()); fd = open(buf, OWRITE|OCEXEC); sprint(buf, "kilgrp%d", grp); write(fd, buf, strlen(buf)); close(fd);} voidthreadkill(int id) { char buf[16]; Thread *t; int pid; Proc *p; if ((t = threadof(id)) == nil) { if (_threaddebuglevel & DBGNOTE) threadprint(2, "Can't find thread to kill\n"); return; } sprint(buf, "kilthr%d", id); if ((pid = t->proc->pid) == 0) { if (_threaddebuglevel & DBGNOTE) threadprint(2, "Thread has zero pid\n"); if (postnote(PNGROUP, getpid(), buf) < 0) threadprint(2, "postnote failed: %r\n"); return; } p = *procp; if (pid == p->pid) { if (_threaddebuglevel & DBGNOTE) threadprint(2, "Thread in same proc\n"); assert(pid == getpid()); if (t == p->curthread) { threadprint(2, "Threadkill: killing self\n"); threadexits("threadicide"); } lock(&rendezlock); if (t->state == Rendezvous) { if (_threaddebuglevel & DBGNOTE) threadprint(2, "Thread was in rendezvous\n"); assert(getqbytag(&rendez, t->tag) == t); } t->exiting = 1; unlock(&rendezlock); garbagethread(t); return; } if (postnote(PNPROC, pid, buf) < 0) threadprint(2, "postnote failed: %r\n");} voidthreadexitsall(char *status) { int fd; Channel *c; char buf[128]; if ((c = thewaitchan) != nil) { thewaitchan = nil; while (nbrecv(c, nil)) ; /* Unblock threads previously exited, * sending on thewaitchan, not yet waited for */ } sprint(buf, "/proc/%d/note", passerpid); if ((fd = open(buf, OWRITE)) < 0) { /* passer proc died, send note to notepg directly */ sprint(buf, "/proc/%d/notepg", getpid()); if ((fd = open(buf, OWRITE)) < 0) { threadprint(2, "threadexitsall: can't open %s: %r\n", buf); exits("panic"); } snprint(buf, sizeof buf, "kilall%s", status?status:""); if (DBGNOTE & _threaddebuglevel) fprint(2, "Sending note %s to process group of %d\n", buf, getpid()); } else { snprint(buf, sizeof buf, "kilall%s", status?status:""); if (DBGNOTE & _threaddebuglevel) fprint(2, "Sending note %s to passer proc %d\n", buf, passerpid); } write(fd, buf, strlen(buf)); exits(status);} voidyield(void) { Thread *new, *t; Proc *p; ulong thr; p = *procp; t = p->curthread; if (t->exiting) { _threaddebug(DBGTHRD|DBGKILL, "Exiting in yield()"); threadexits(nil); /* no return */ } if ((new = getq(&p->runnable)) == nil) { _threaddebug(DBGTHRD, "Nothing to yield for"); return; /* Nothing to yield for */ } if ((thr = setjmp(p->curthread->env))) { if (thr != ~0) garbagethread((Thread *)thr); if ((*procp)->curthread->exiting) threadexits(nil); return; /* Return from yielding */ } putq(&p->runnable, p->curthread); p->curthread->state = Runnable; _threaddebug(DBGTHRD, "Yielding to %d.%d", p->pid, new->id); p->curthread = new; new->state = Running; longjmp(new->env, ~0); /* no return */} ulong_threadrendezvous(ulong tag, ulong value) { Proc *p; Thread *this, *that, *new; ulong v, t; p = *procp; this = p->curthread; lock(&rendezlock); _threaddebug(DBGREND, "rendezvous tag %lux", tag); /* find a thread waiting in a rendezvous on tag */ that = getqbytag(&rendez, tag); /* if a thread in same or another proc waiting, rendezvous */ if (that) { _threaddebug(DBGREND, "waiting proc is %lud.%d", that->proc->pid, that->id); threadassert(that->state == Rendezvous); /* exchange values */ v = that->value; that->value = value; /* remove from rendez-vous queue */ that->state = Runnable; if (that->proc->blocked) { threadassert(that->proc != p); that->proc->blocked = 0; _threaddebug(DBGREND, "unblocking rendezvous, tag = %lux", (ulong)that->proc); unlock(&rendezlock); /* `Non-blocking' rendez-vous */ while (rendezvous((ulong)that->proc, (ulong)that) == ~0) { _threaddebug(DBGNOTE|DBGTHRD, "interrupted"); if (this->exiting) { _threaddebug(DBGNOTE|DBGTHRD, "and committing suicide"); threadexits(nil); } } } else { putq(&that->proc->runnable, that); unlock(&rendezlock); } yield(); return v; } _threaddebug(DBGREND, "blocking"); /* Mark this thread waiting */ this->value = value; this->state = Rendezvous; this->tag = tag; putq(&rendez, this); /* Look for runnable threads */ new = getq(&p->runnable); if (new == nil) { /* No other thread runnable, rendezvous */ p->blocked = 1; _threaddebug(DBGREND, "blocking rendezvous, tag = %lux", (ulong)p); unlock(&rendezlock); while ((new = (Thread *)rendezvous((ulong)p, 0)) == (Thread *)~0) { _threaddebug(DBGNOTE|DBGTHRD, "interrupted"); if (this->exiting) { _threaddebug(DBGNOTE|DBGTHRD, "and committing suicide"); threadexits(nil); } } threadassert(!p->blocked); threadassert(new->proc == p); if (new == this) { this->state = Running; if (this->exiting) threadexits(nil); return this->value; } _threaddebug(DBGREND, "after rendezvous, new thread is %lux, id %d", new, new->id); } else { unlock(&rendezlock); } if ((t = setjmp(p->curthread->env))) { if (t != ~0) garbagethread((Thread *)t); _threaddebug(DBGREND, "unblocking"); threadassert(p->curthread->next == (Thread *)~0); this->state = Running; if (p->curthread->exiting) { _threaddebug(DBGKILL, "Exiting after rendezvous"); threadexits(nil); } return this->value; } _threaddebug(DBGREND|DBGTHRD, "Scheduling %lud.%d", new->proc->pid, new->id); p->curthread = new; new->state = Running; longjmp(new->env, ~0); /* no return */ return ~0; /* Not called */} voidmain(int argc, char *argv[]){ char buf[40], err[ERRLEN]; int fd, id; _qlockinit(_threadrendezvous); id = getnoteid(); threadassert(id > 0); rfork(RFNOTEG|RFREND); atnotify(notehandler, 1); switch(rfork(RFPROC|RFMEM)){ case -1: threadassert(0); case 0: /* handle notes */ rfork(RFCFDG); if (_threaddebuglevel) { fd = open("/dev/cons", OWRITE); dup(fd, 2); } passerpid = getpid(); /* * The noteid is attached to the fd at open. * Notefd will correspond to our current (new) noteid * even after we set our noteid back to the old one. * * We could open main's notepg file and not depend * on this behavior, but then we would depend on the fact that * when main exits its open notepg file is still writable. * We would also depend on opening the file before main * exits, which is a race I'd rather not run. */ sprint(buf, "/proc/%d/notepg", getpid()); notefd = open(buf, OWRITE|OCEXEC); if (_threaddebuglevel & DBGNOTE) threadprint(2, "Passer is %d\n", passerpid); if(setnoteid(id) < 0){ passerpid = -1; exits(0); } for(;;){ if(sleep(120*1000) < 0){Lines 1–500 of 1157. Load more
#include <u.h> #include <libc.h> #include "assert.h" #include "threadimpl.h" static void initproc(void (*)(ulong, int, char*[]), int, char*[], uint); static void garbagethread(Thread *t); static Thread* getqbytag(Tqueue *, ulong); static void putq(Tqueue *, Thread *); static Thread* getq(Tqueue *); static void launcher(ulong, void (*)(void *), void *); static void mainlauncher(ulong, int argc, char *argv[]); static void garbageproc(Proc *); static Proc* prepproc(Newproc *); static int setnoteid(int); static int getnoteid(void); static int notefd; static int passerpid; static long totalmalloc; static Thread* dontkill; static Tqueue rendez; static Lock rendezlock; int mainstacksize; Channel* thewaitchan; struct Pqueue pq; Proc **procp; /* Pointer to pointer to proc's Proc structure */ #define STACKOFF 2 #ifdef M386 #define FIX1 #define FIX2 *--tos = 0 #endif #ifdef Mmips #define FIX1 #define FIX2 #endif #ifdef Mpower /* This could be wrong ... */ #define FIX1 #define FIX2 *--tos = 0 #endif #ifdef Malpha #define FIX1 *--tos = 0 #define FIX2 *--tos = 0 #endif #ifdef Marm #define FIX1 #define FIX2 #endif static int nextID(void) { static Lock l; static id; int i; lock(&l); i = ++id; unlock(&l); return i; } static int notehandler(void *, char *s) { Proc *p; Thread *t; int id; if (DBGNOTE & _threaddebuglevel) threadprint(2, "Got note %s\n", s); if (getpid() == passerpid) { if (DBGNOTE & _threaddebuglevel) threadprint(2, "Notepasser %d got note %s\n", passerpid, s); if (strcmp(s, "kilpasser") == 0) exits(nil); write(notefd, s, strlen(s)); if (strncmp(s, "kilthr", 6) == 0 || strncmp(s, "kilgrp", 6) == 0) return 1; } else { if (strncmp(s, "kilthr", 6) == 0) { p = *procp; id = strtoul(s+6, nil, 10); if (DBGNOTE & _threaddebuglevel) threadprint(2, "Thread id %d\n", id); for (t = p->threads.head; t; t = t->nextt) { if (t->id == id) { break; } } if (t == nil) { if (DBGNOTE & _threaddebuglevel) threadprint(2, "Thread id %d not found\n", id); return 1; } if (t == p->curthread) { char err[ERRLEN] = "exiting"; if (DBGNOTE & _threaddebuglevel) threadprint(2, "Killing current thread\n"); errstr(err); } threadassert(t->state != Rendezvous); t->exiting = 1; return 1; } if (strncmp(s, "kilgrp", 6) == 0) { p = *procp; id = strtoul(s+6, nil, 10); if (DBGNOTE & _threaddebuglevel) threadprint(2, "Thread grp %d", id); for (t = p->threads.head; t; t = t->nextt) { if (t->grp == id) { if (t != p->curthread) t->exiting = 1; } } return 1; } } if (strncmp(s, "kilall", 6) == 0) exits(s+6); return 0; } void threadexits(char *exitstr) { Thread *t, *new; Proc *p; Channel *c; _threaddebug(DBGTHRD|DBGKILL, "Exitthread()"); p = *procp; t = p->curthread; threadassert(t->state == Running); if (p->nthreads > 1) { t->exiting = 1; p->nthreads--; lock(&rendezlock); while ((new = getq(&p->runnable)) == nil) { _threaddebug(DBGTHRD, "Nothing left to run"); /* called with rendezlock held */ p->blocked = 1; unlock(&rendezlock); if ((new = (Thread *)rendezvous((ulong)p, 0)) != (Thread *)~0) { threadassert(!p->blocked); threadassert(new->proc == p); p->curthread = new; new->state = Running; /* switch to new thread, pass it `t' for cleanup */ new->garbage = t; longjmp(new->env, (int)t); /* No return */ } _threaddebug(DBGNOTE|DBGTHRD, "interrupted"); lock(&rendezlock); } unlock(&rendezlock); _threaddebug(DBGTHRD, "Yield atexit to %d.%d", p->pid, new->id); p->curthread = new; new->state = Running; if (new->exiting) threadexits(nil); /* switch to new thread, pass it `t' for cleanup */ new->garbage = t; longjmp(new->env, (int)t); /* no return */ } /* * thewaitchan confounds exiting the entire program, so handle it * carefully; store exposed global in local variable c: */ if ((c = thewaitchan) != nil) { Waitmsg w; long t[4]; snprint(w.pid, sizeof(w.pid), "%d", p->pid); times(t); snprint(w.time + 0*12, 12, "%10.2fu", t[0]/1000.0); snprint(w.time + 1*12, 12, "%10.2fu", t[1]/1000.0); snprint(w.time + 2*12, 12, "%10.2fu", (t[0]+t[1]+t[2]+t[3])/1000.0); /* Cheating just a bit */ if (exitstr) strncpy(w.msg, exitstr, sizeof(w.msg)); else w.msg[0] = '\0'; _threaddebug(DBGCHLD, "In thread %s: sending exit status %s for %d\n", p->curthread->cmdname, exitstr, p->pid); send(c, &w); } _threaddebug(DBGPROC, "Exiting\n"); t->exiting = 1; if(exitstr == nil) p->str[0] = '\0'; else strncpy(p->str, exitstr, sizeof(p->str)); p->nthreads--; /* Clean up and exit */ longjmp(p->oldenv, DOEXIT); } static Thread * threadof(int id) { Proc *pp; Thread *t; lock(&pq.lock); for (pp = pq.head; pp->next; pp = pp->next) for (t = pp->threads.head; t; t = t->nextt) if (t->id == id) { unlock(&pq.lock); return t; } unlock(&pq.lock); return nil; } int threadpid(int id) { Proc *pp; Thread *t; if (id < 0) return id; if (id == 0) return (*procp)->pid; lock(&pq.lock); for (pp = pq.head; pp->next; pp = pp->next) for (t = pp->threads.head; t; t = t->nextt) if (t->id == id) { unlock(&pq.lock); return pp->pid; } unlock(&pq.lock); return -1; } void threadkillgrp(int grp) { int fd; char buf[128]; sprint(buf, "/proc/%d/notepg", getpid()); fd = open(buf, OWRITE|OCEXEC); sprint(buf, "kilgrp%d", grp); write(fd, buf, strlen(buf)); close(fd); } void threadkill(int id) { char buf[16]; Thread *t; int pid; Proc *p; if ((t = threadof(id)) == nil) { if (_threaddebuglevel & DBGNOTE) threadprint(2, "Can't find thread to kill\n"); return; } sprint(buf, "kilthr%d", id); if ((pid = t->proc->pid) == 0) { if (_threaddebuglevel & DBGNOTE) threadprint(2, "Thread has zero pid\n"); if (postnote(PNGROUP, getpid(), buf) < 0) threadprint(2, "postnote failed: %r\n"); return; } p = *procp; if (pid == p->pid) { if (_threaddebuglevel & DBGNOTE) threadprint(2, "Thread in same proc\n"); assert(pid == getpid()); if (t == p->curthread) { threadprint(2, "Threadkill: killing self\n"); threadexits("threadicide"); } lock(&rendezlock); if (t->state == Rendezvous) { if (_threaddebuglevel & DBGNOTE) threadprint(2, "Thread was in rendezvous\n"); assert(getqbytag(&rendez, t->tag) == t); } t->exiting = 1; unlock(&rendezlock); garbagethread(t); return; } if (postnote(PNPROC, pid, buf) < 0) threadprint(2, "postnote failed: %r\n"); } void threadexitsall(char *status) { int fd; Channel *c; char buf[128]; if ((c = thewaitchan) != nil) { thewaitchan = nil; while (nbrecv(c, nil)) ; /* Unblock threads previously exited, * sending on thewaitchan, not yet waited for */ } sprint(buf, "/proc/%d/note", passerpid); if ((fd = open(buf, OWRITE)) < 0) { /* passer proc died, send note to notepg directly */ sprint(buf, "/proc/%d/notepg", getpid()); if ((fd = open(buf, OWRITE)) < 0) { threadprint(2, "threadexitsall: can't open %s: %r\n", buf); exits("panic"); } snprint(buf, sizeof buf, "kilall%s", status?status:""); if (DBGNOTE & _threaddebuglevel) fprint(2, "Sending note %s to process group of %d\n", buf, getpid()); } else { snprint(buf, sizeof buf, "kilall%s", status?status:""); if (DBGNOTE & _threaddebuglevel) fprint(2, "Sending note %s to passer proc %d\n", buf, passerpid); } write(fd, buf, strlen(buf)); exits(status); } void yield(void) { Thread *new, *t; Proc *p; ulong thr; p = *procp; t = p->curthread; if (t->exiting) { _threaddebug(DBGTHRD|DBGKILL, "Exiting in yield()"); threadexits(nil); /* no return */ } if ((new = getq(&p->runnable)) == nil) { _threaddebug(DBGTHRD, "Nothing to yield for"); return; /* Nothing to yield for */ } if ((thr = setjmp(p->curthread->env))) { if (thr != ~0) garbagethread((Thread *)thr); if ((*procp)->curthread->exiting) threadexits(nil); return; /* Return from yielding */ } putq(&p->runnable, p->curthread); p->curthread->state = Runnable; _threaddebug(DBGTHRD, "Yielding to %d.%d", p->pid, new->id); p->curthread = new; new->state = Running; longjmp(new->env, ~0); /* no return */ } ulong _threadrendezvous(ulong tag, ulong value) { Proc *p; Thread *this, *that, *new; ulong v, t; p = *procp; this = p->curthread; lock(&rendezlock); _threaddebug(DBGREND, "rendezvous tag %lux", tag); /* find a thread waiting in a rendezvous on tag */ that = getqbytag(&rendez, tag); /* if a thread in same or another proc waiting, rendezvous */ if (that) { _threaddebug(DBGREND, "waiting proc is %lud.%d", that->proc->pid, that->id); threadassert(that->state == Rendezvous); /* exchange values */ v = that->value; that->value = value; /* remove from rendez-vous queue */ that->state = Runnable; if (that->proc->blocked) { threadassert(that->proc != p); that->proc->blocked = 0; _threaddebug(DBGREND, "unblocking rendezvous, tag = %lux", (ulong)that->proc); unlock(&rendezlock); /* `Non-blocking' rendez-vous */ while (rendezvous((ulong)that->proc, (ulong)that) == ~0) { _threaddebug(DBGNOTE|DBGTHRD, "interrupted"); if (this->exiting) { _threaddebug(DBGNOTE|DBGTHRD, "and committing suicide"); threadexits(nil); } } } else { putq(&that->proc->runnable, that); unlock(&rendezlock); } yield(); return v; } _threaddebug(DBGREND, "blocking"); /* Mark this thread waiting */ this->value = value; this->state = Rendezvous; this->tag = tag; putq(&rendez, this); /* Look for runnable threads */ new = getq(&p->runnable); if (new == nil) { /* No other thread runnable, rendezvous */ p->blocked = 1; _threaddebug(DBGREND, "blocking rendezvous, tag = %lux", (ulong)p); unlock(&rendezlock); while ((new = (Thread *)rendezvous((ulong)p, 0)) == (Thread *)~0) { _threaddebug(DBGNOTE|DBGTHRD, "interrupted"); if (this->exiting) { _threaddebug(DBGNOTE|DBGTHRD, "and committing suicide"); threadexits(nil); } } threadassert(!p->blocked); threadassert(new->proc == p); if (new == this) { this->state = Running; if (this->exiting) threadexits(nil); return this->value; } _threaddebug(DBGREND, "after rendezvous, new thread is %lux, id %d", new, new->id); } else { unlock(&rendezlock); } if ((t = setjmp(p->curthread->env))) { if (t != ~0) garbagethread((Thread *)t); _threaddebug(DBGREND, "unblocking"); threadassert(p->curthread->next == (Thread *)~0); this->state = Running; if (p->curthread->exiting) { _threaddebug(DBGKILL, "Exiting after rendezvous"); threadexits(nil); } return this->value; } _threaddebug(DBGREND|DBGTHRD, "Scheduling %lud.%d", new->proc->pid, new->id); p->curthread = new; new->state = Running; longjmp(new->env, ~0); /* no return */ return ~0; /* Not called */ } void main(int argc, char *argv[]) { char buf[40], err[ERRLEN]; int fd, id; _qlockinit(_threadrendezvous); id = getnoteid(); threadassert(id > 0); rfork(RFNOTEG|RFREND); atnotify(notehandler, 1); switch(rfork(RFPROC|RFMEM)){ case -1: threadassert(0); case 0: /* handle notes */ rfork(RFCFDG); if (_threaddebuglevel) { fd = open("/dev/cons", OWRITE); dup(fd, 2); } passerpid = getpid(); /* * The noteid is attached to the fd at open. * Notefd will correspond to our current (new) noteid * even after we set our noteid back to the old one. * * We could open main's notepg file and not depend * on this behavior, but then we would depend on the fact that * when main exits its open notepg file is still writable. * We would also depend on opening the file before main * exits, which is a race I'd rather not run. */ sprint(buf, "/proc/%d/notepg", getpid()); notefd = open(buf, OWRITE|OCEXEC); if (_threaddebuglevel & DBGNOTE) threadprint(2, "Passer is %d\n", passerpid); if(setnoteid(id) < 0){ passerpid = -1; exits(0); } for(;;){ if(sleep(120*1000) < 0){