commit c6687d4591964cb13df87f55ec4770e778a4a55c from: rsc date: Tue Sep 21 01:11:28 2004 UTC Continue the pthreads torture. commit - 3d5e34e146b5ba5c973230abb624ce9126024569 commit + c6687d4591964cb13df87f55ec4770e778a4a55c blob - 87b6bfd038e5d7bb3d761b6a33f34f5174609f08 blob + da9f9dc1e36ad5c6ff30aef3da6aac0492df6734 --- include/libc.h +++ include/libc.h @@ -436,6 +436,7 @@ struct Lock #endif }; +extern int _tas(int*); extern void lock(Lock*); extern void unlock(Lock*); extern int canlock(Lock*); blob - 8e79ba6e1ca97bbfd8dd8b3e32740f837b48108d blob + c1ca03365434d6d0659698101d25416c9c25feaf --- include/u.h +++ include/u.h @@ -24,7 +24,6 @@ extern "C" { #include #include #include /* for tolower */ -#include /* for Locks */ /* * OS-specific crap @@ -42,9 +41,11 @@ typedef long p9jmp_buf[sizeof(sigjmp_buf)/sizeof(long) # undef _NEEDUSHORT # undef _NEEDUINT # undef _NEEDULONG -# include -# define PLAN9_PTHREADS # endif +# if defined(__Linux26__) +# include +# define PLAN9_PTHREADS 1 +# endif #endif #if defined(__sun__) # include blob - 51e733ab720f6b8825b0fcfbc399cc9ed5ff17b1 blob + f183f115c48e39f348fc22a487f8dbee9473946e --- src/cmd/9term/9term.c +++ src/cmd/9term/9term.c @@ -884,12 +884,8 @@ key(Rune r) case 0x7F: /* DEL: send interrupt */ t.qh = t.q0 = t.q1 = t.nr; show(t.q0); -{int x; x=tcgetpgrp(rcfd); -print("postnote %d pgrp %d\n", rcpid, x); - postnote(PNGROUP, x, "interrupt"); -if(x >= 2) killpg(x, 2); -} - // write(rcfd, "\x7F", 1); + // postnote(PNGROUP, x, "interrupt"); + write(rcfd, "\x7F", 1); return; } blob - 7ee49350462bb15fbe5820e63e60a90269a015f0 blob + ddaadb56c646c2a907ca007b195dc20c1d48a7cc --- src/cmd/9term/rcstart.c +++ src/cmd/9term/rcstart.c @@ -3,6 +3,25 @@ #include #include "term.h" +static void +sys(char *buf) +{ + char buf2[100]; + char *f[20]; + int nf, pid; + + strcpy(buf2, buf); + nf = tokenize(buf2, f, nelem(f)); + f[nf] = nil; + switch(pid = fork()){ + case 0: + execvp(f[0], f); + _exits("oops"); + default: + waitpid(); + } +} + int rcstart(int argc, char **argv, int *pfd, int *tfd) { @@ -33,11 +52,14 @@ rcstart(int argc, char **argv, int *pfd, int *tfd) dup(sfd, 0); dup(sfd, 1); dup(sfd, 2); - system("stty tabs -onlcr onocr icanon echo erase '^h' intr '^?'"); + sys("stty tabs -onlcr onocr icanon echo erase '^h' intr '^?'"); if(noecho) - system("stty -echo"); + sys("stty -echo"); for(i=3; i<100; i++) close(i); + signal(SIGINT, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); execvp(argv[0], argv); fprint(2, "exec %s failed: %r\n", argv[0]); _exits("oops"); blob - 0eb5f410edfbdc964b0a5aa390f28baf387c55f5 blob + 0c359c9e072aa471bbd08145df523387666ce190 --- src/lib9/9proc.h +++ src/lib9/9proc.h @@ -13,9 +13,6 @@ struct Uproc int pid; int state; void *priv[NPRIV]; - ulong rendval; - ulong rendtag; - Uproc *rendhash; p9jmp_buf notejb; }; blob - /dev/null blob + 902a957908f1c6c20f3d2dcd80d22952b1677d7a (mode 644) --- /dev/null +++ src/lib9/_p9proc-Linux.c @@ -0,0 +1,5 @@ +#ifdef __Linux26__ +#include "_p9proc-pthread.c" +#else +#include "_p9proc-getpid.c" +#endif blob - /dev/null blob + 9543bf2407c586755e85d19e9a8ae300e789d8ad (mode 644) --- /dev/null +++ src/lib9/_p9proc-getpid.c @@ -0,0 +1,113 @@ +/* + * This needs to be callable from a signal handler, so it has been + * written to avoid locks. The only lock is the one used to acquire + * an entry in the table, and we make sure that acquiring is done + * when not in a handler. Lookup and delete do not need locks. + * It's a scan-forward hash table. To avoid breaking chains, + * T ((void*)-1) is used as a non-breaking nil. + */ + +#include +#include +#include "9proc.h" + +enum { PIDHASH = 1021 }; + +#define T ((void*)-1) +static Uproc *alluproc[PIDHASH]; +static int allupid[PIDHASH]; +static Lock uproclock; + +void +_clearuproc(void) +{ + int i; + + /* called right after fork - no locking needed */ + for(i=0; ipid = pid; + lock(&uproclock); + h = pid%PIDHASH; + for(i=0; i +#include #include #include "9proc.h" #undef fork @@ -7,9 +8,15 @@ int p9fork(void) { int pid; + sigset_t all, old; + sigfillset(&all); + sigprocmask(SIG_SETMASK, &all, &old); pid = fork(); - _clearuproc(); - _p9uproc(0); + if(pid == 0){ + _clearuproc(); + _p9uproc(0); + } + sigprocmask(SIG_SETMASK, &old, nil); return pid; } blob - /dev/null blob + 231ca713ceb6147b8691d80b1b0d2b2436c7448e (mode 644) --- /dev/null +++ src/lib9/lock-Darwin.c @@ -0,0 +1 @@ +#include "lock-pthread.c" blob - /dev/null blob + bdd48a28e7e3b49884456f46f8f81c35c6fbf297 (mode 644) --- /dev/null +++ src/lib9/lock-FreeBSD.c @@ -0,0 +1 @@ +#include "lock-tas.c" blob - /dev/null blob + c25596b9aeadfbc323fe2cc48494904dff962a7c (mode 644) --- /dev/null +++ src/lib9/lock-Linux.c @@ -0,0 +1,5 @@ +#ifdef __Linux26__ +#include "lock-pthread.c" +#else +#include "lock-tas.c" +#endif blob - /dev/null blob + 689261f6378678b2321552254bc74102b2966344 (mode 644) --- /dev/null +++ src/lib9/lock-pthread.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER; + +static void +lockinit(Lock *lk) +{ + pthread_mutexattr_t attr; + + pthread_mutex_lock(&initmutex); + if(lk->init == 0){ + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&lk->mutex, &attr); + pthread_mutexattr_destroy(&attr); + lk->init = 1; + } + pthread_mutex_unlock(&initmutex); +} + +void +lock(Lock *lk) +{ + if(!lk->init) + lockinit(lk); + if(pthread_mutex_lock(&lk->mutex) != 0) + abort(); +} + +int +canlock(Lock *lk) +{ + int r; + + if(!lk->init) + lockinit(lk); + r = pthread_mutex_trylock(&lk->mutex); + if(r == 0) + return 1; + if(r == EBUSY) + return 0; + abort(); +} + +void +unlock(Lock *lk) +{ + if(pthread_mutex_unlock(&lk->mutex) != 0) + abort(); +} blob - ed430ff8675efb8d8d4cd61c9bf9651513da8e69 blob + 9200d81cc76f80774cf6ec110c5fb2dd130da688 --- src/lib9/mkfile +++ src/lib9/mkfile @@ -67,7 +67,7 @@ LIB9OFILES=\ _exits.$O\ _p9dialparse.$O\ _p9dir.$O\ - _p9proc.$O\ + _p9proc-$SYSNAME.$O\ announce.$O\ argv0.$O\ atexit.$O\ @@ -111,7 +111,7 @@ LIB9OFILES=\ jmp.$O\ lrand.$O\ lnrand.$O\ - lock.$O\ + lock-$SYSNAME.$O\ main.$O\ malloc.$O\ malloctag.$O\ @@ -141,6 +141,7 @@ LIB9OFILES=\ strecpy.$O\ sysfatal.$O\ sysname.$O\ + tas-$OBJTYPE.$O\ time.$O\ tokenize.$O\ truerand.$O\ blob - /dev/null blob + e6f54de65c0fe4e6a4f5883d77db1ea1ee7616a0 (mode 644) --- /dev/null +++ src/lib9/lock-tas.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + +int _ntas; +static int +_xtas(void *v) +{ + int x; + + _ntas++; + x = _tas(v); + if(x != 0 && x != 0xcafebabe){ + print("bad tas value %d\n", x); + abort(); + } + return x; +} + +int +canlock(Lock *l) +{ + return !_xtas(&l->val); +} + +void +unlock(Lock *l) +{ + l->val = 0; +} + +void +lock(Lock *lk) +{ + int i; + + /* once fast */ + if(!_xtas(&lk->val)) + return; + /* a thousand times pretty fast */ + for(i=0; i<1000; i++){ + if(!_xtas(&lk->val)) + return; + sched_yield(); + } + /* now nice and slow */ + for(i=0; i<1000; i++){ + if(!_xtas(&lk->val)) + return; + usleep(100*1000); + } + /* take your time */ + while(_xtas(&lk->val)) + usleep(1000*1000); +} blob - 625c100f5568f2a845d19027c1594158840687db blob + 0eacccd78d6eaf00d9eceeda90fe96f60151a727 --- src/lib9/qlock.c +++ src/lib9/qlock.c @@ -18,6 +18,8 @@ enum static void (*procsleep)(_Procrend*) = _procsleep; static void (*procwakeup)(_Procrend*) = _procwakeup; +#define _procsleep donotcall_procsleep +#define _procwakeup donotcall_procwakeup /* this gets called by the thread library ONLY to get us to use its rendezvous */ void @@ -73,7 +75,7 @@ qlock(QLock *q) q->tail = mp; mp->state = Queuing; mp->rend.l = &q->lock; - _procsleep(&mp->rend); + procsleep(&mp->rend); unlock(&q->lock); assert(mp->state == Waking); unlock(&mp->inuse); @@ -92,7 +94,7 @@ qunlock(QLock *q) if(q->head == nil) q->tail = nil; p->state = Waking; - _procwakeup(&p->rend); + procwakeup(&p->rend); unlock(&q->lock); return; } @@ -137,7 +139,7 @@ rlock(RWLock *q) mp->next = nil; mp->state = QueuingR; mp->rend.l = &q->lock; - _procsleep(&mp->rend); + procsleep(&mp->rend); unlock(&q->lock); assert(mp->state == Waking); unlock(&mp->inuse); @@ -181,7 +183,7 @@ runlock(RWLock *q) /* wakeup waiter */ p->state = Waking; - _procwakeup(&p->rend); + procwakeup(&p->rend); unlock(&q->lock); } @@ -211,7 +213,7 @@ wlock(RWLock *q) /* wait in kernel */ mp->rend.l = &q->lock; - _procsleep(&mp->rend); + procsleep(&mp->rend); unlock(&q->lock); assert(mp->state == Waking); unlock(&mp->inuse); @@ -253,7 +255,7 @@ wunlock(RWLock *q) if(q->head == nil) q->tail = nil; p->state = Waking; - _procwakeup(&p->rend); + procwakeup(&p->rend); unlock(&q->lock); return; } @@ -269,7 +271,7 @@ wunlock(RWLock *q) q->head = p->next; q->readers++; p->state = Waking; - _procwakeup(&p->rend); + procwakeup(&p->rend); } if(q->head == nil) q->tail = nil; @@ -310,20 +312,20 @@ rsleep(Rendez *r) if(r->l->head == nil) r->l->tail = nil; t->state = Waking; - _procwakeup(&t->rend); + procwakeup(&t->rend); }else r->l->locked = 0; /* wait for a wakeup */ me->rend.l = &r->l->lock; - _procsleep(&me->rend); - + procsleep(&me->rend); assert(me->state == Waking); unlock(&me->inuse); if(!r->l->locked){ fprint(2, "rsleep: not locked after wakeup\n"); abort(); } + unlock(&r->l->lock); } int blob - e2abc3d35b70c0c394958639335e8bb058d53553 blob + 744a8fc81a5a5f91eb3df0c99c3d3d3051059176 --- src/lib9/rendez-signal.c +++ src/lib9/rendez-signal.c @@ -52,6 +52,7 @@ _procsleep(_Procrend *r) */ ignusr1(1); assert(r->asleep == 0); + lock(r->l); } void blob - 2d5074ee1fc11046fffcef403f7d5e4dfb9ba495 blob + aa92c2006454034e85b174f5ac99d739af087c84 --- src/libmux/mux.c +++ src/libmux/mux.c @@ -55,8 +55,9 @@ muxrpc(Mux *mux, void *tx) enqueue(mux, r); /* wait for our packet */ - while(mux->muxer && !r->p) + while(mux->muxer && !r->p){ rsleep(&r->r); + } /* if not done, there's no muxer: start muxing */ if(!r->p){ blob - 2c611e45929a2ee1d9e7486d569c2d8125ced681 blob + fb4c674667cb33586e8ca767c7ec208a20223ea5 --- src/libthread/386.c +++ src/libthread/386.c @@ -1,3 +1,7 @@ +#include "ucontext.c" + +#ifdef OLD + #include "threadimpl.h" /* * To use this you need some patches to Valgrind that @@ -55,3 +59,4 @@ _threadstacklimit(void *bottom, void *top) VALGRIND_SET_STACK_LIMIT(1, bottom, top); #endif } +#endif blob - a715706ab47c08fa86198623ab542a75172fd71c blob + 8768e60df715c4d78e23a552a8977bcebabf3f1b --- src/libthread/fdwait.c +++ src/libthread/fdwait.c @@ -241,15 +241,16 @@ threadread(int fd, void *a, long n) threadfdnoblock(fd); again: + /* + * Always call wait (i.e. don't optimistically try the read) + * so that the scheduler gets a chance to run other threads. + */ + _threadfdwait(fd, 'r', getcallerpc(&fd)); errno = 0; nn = read(fd, a, n); if(nn <= 0){ - if(errno == EINTR) + if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) goto again; - if(errno == EAGAIN || errno == EWOULDBLOCK){ - _threadfdwait(fd, 'r', getcallerpc(&fd)); - goto again; - } } return nn; } @@ -261,14 +262,15 @@ threadrecvfd(int fd) threadfdnoblock(fd); again: + /* + * Always call wait (i.e. don't optimistically try the recvfd) + * so that the scheduler gets a chance to run other threads. + */ + _threadfdwait(fd, 'r', getcallerpc(&fd)); nn = recvfd(fd); if(nn < 0){ - if(errno == EINTR) + if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) goto again; - if(errno == EAGAIN || errno == EWOULDBLOCK){ - _threadfdwait(fd, 'r', getcallerpc(&fd)); - goto again; - } } return nn; } @@ -280,14 +282,15 @@ threadsendfd(int fd, int sfd) threadfdnoblock(fd); again: + /* + * Always call wait (i.e. don't optimistically try the sendfd) + * so that the scheduler gets a chance to run other threads. + */ + _threadfdwait(fd, 'w', getcallerpc(&fd)); nn = sendfd(fd, sfd); if(nn < 0){ - if(errno == EINTR) + if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) goto again; - if(errno == EAGAIN || errno == EWOULDBLOCK){ - _threadfdwait(fd, 'w', getcallerpc(&fd)); - goto again; - } } return nn; } @@ -315,14 +318,15 @@ _threadwrite(int fd, const void *a, long n) threadfdnoblock(fd); again: + /* + * Always call wait (i.e. don't optimistically try the write) + * so that the scheduler gets a chance to run other threads. + */ + _threadfdwait(fd, 'w', getcallerpc(&fd)); nn = write(fd, a, n); if(nn < 0){ - if(errno == EINTR) + if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) goto again; - if(errno == EAGAIN || errno == EWOULDBLOCK){ - _threadfdwait(fd, 'w', getcallerpc(&fd)); - goto again; - } } return nn; } blob - c3cef2d029cc2216a680a713c0f668cc441cc95d blob + 5081f48f621705bc1ca9e3f0defa29d8f54e0984 --- src/libthread/label.h +++ src/libthread/label.h @@ -7,9 +7,15 @@ typedef struct Label Label; #define LABELDPC 0 -#if defined (__i386__) && (defined(__FreeBSD__) || defined(__linux__) || defined(__OpenBSD__)) +#if defined(__linux__) +#include struct Label { + ucontext_t uc; +}; +#elif defined (__i386__) && (defined(__FreeBSD__) || defined(__linux__) || defined(__OpenBSD__)) +struct Label +{ ulong pc; ulong bx; ulong sp; blob - 679a334bc6d464a9f359f94e1ab243ac9ee03c70 blob + 8cdd8ca3628c0a61243386de7aad8ea283c3bdfd --- src/libthread/main.c +++ src/libthread/main.c @@ -50,7 +50,7 @@ main(int argc, char **argv) a->argc = argc; a->argv = argv; p = _newproc(mainlauncher, a, mainstacksize, "threadmain", 0, 0); - _schedinit(p); + _scheduler(p); abort(); /* not reached */ return 0; } @@ -80,7 +80,7 @@ _schedfork(Proc *p) { int pid; lock(&p->lock); - pid = ffork(RFMEM|RFNOWAIT, _schedinit, p); + pid = ffork(RFMEM|RFNOWAIT, _scheduler, p); p->pid = pid; unlock(&p->lock); return pid; blob - 81cc34394e2c747d9008341651f765687f752a50 blob + 7f7b6daa4d1dcb0783a7518425a1000522b72b23 --- src/libthread/sched.c +++ src/libthread/sched.c @@ -3,7 +3,7 @@ #include #include "threadimpl.h" -//static Thread *runthread(Proc*); +static Thread *runthread(Proc*); static char *_psstate[] = { "Dead", @@ -21,27 +21,54 @@ psstate(int s) } void -_schedinit(void *arg) +needstack(int howmuch) { Proc *p; Thread *t; - extern void ignusr1(int), _threaddie(int); - signal(SIGTERM, _threaddie); - + + p = _threadgetproc(); + if(p == nil || (t=p->thread) == nil) + return; + if((ulong)&howmuch < (ulong)t->stk+howmuch){ /* stack overflow waiting to happen */ + fprint(2, "stack overflow: stack at 0x%lux, limit at 0x%lux, need 0x%lux\n", (ulong)&p, (ulong)t->stk, howmuch); + abort(); + } +} + +void +_scheduler(void *arg) +{ + Proc *p; + Thread *t; + p = arg; lock(&p->lock); p->pid = _threadgetpid(); _threadsetproc(p); - unlock(&p->lock); - while(_setlabel(&p->sched)) - ; - _threaddebug(DBGSCHED, "top of schedinit, _threadexitsallstatus=%p", _threadexitsallstatus); - if(_threadexitsallstatus) - _exits(_threadexitsallstatus); - lock(&p->lock); - if((t=p->thread) != nil){ + + for(;;){ + 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; + } + t->state = Running; + t->nextstate = Ready; + unlock(&p->lock); + + _swaplabel(&p->sched, &t->sched); + + lock(&p->lock); p->thread = nil; if(t->moribund){ + Moribund: if(t->moribund != 1) fprint(2, "moribund %d\n", t->moribund); assert(t->moribund == 1); @@ -65,7 +92,8 @@ _schedinit(void *arg) free(t); /* XXX how do we know there are no references? */ p->nthreads--; t = nil; - _sched(); + lock(&p->lock); + continue; } /* if(p->needexec){ @@ -78,15 +106,27 @@ _schedinit(void *arg) if(t->ret < 0){ //fprint(2, "_schedfork: %r\n"); abort(); -} + } p->newproc = nil; } t->state = t->nextstate; if(t->state == Ready) _threadready(t); + unlock(&p->lock); } - unlock(&p->lock); - _sched(); +} + +int +_sched(void) +{ + Proc *p; + Thread *t; + + p = _threadgetproc(); + t = p->thread; + assert(t != nil); + _swaplabel(&t->sched, &p->sched); + return p->nsched++; } static Thread* @@ -157,58 +197,6 @@ relock: return t; } -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 waiting to happen */ - fprint(2, "stack overflow: stack at 0x%lux, limit at 0x%lux, need 0x%lux\n", (ulong)&p, (ulong)t->stk, howmuch); - abort(); - } -} - -int -_sched(void) -{ - Proc *p; - Thread *t; - -Resched: - p = _threadgetproc(); -//fprint(2, "p %p\n", p); - if((t = p->thread) != nil){ - needstack(512); - // _threaddebug(DBGSCHED, "pausing, state=%s set %p goto %p", - // psstate(t->state), &t->sched, &p->sched); - if(_setlabel(&t->sched)==0) - _gotolabel(&p->sched); - _threadstacklimit(t->stk, t->stk+t->stksize); - return p->nsched++; - }else{ - 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 Resched; - } - t->state = Running; - t->nextstate = Ready; - _gotolabel(&t->sched); - for(;;); - } -} - long threadstack(void) { blob - d6a2390582cf3777b53311de800c9ea7b9cd3c94 blob + fea309d92788edbaf073c6f8189da97ef7e64c11 --- src/libthread/threadimpl.h +++ src/libthread/threadimpl.h @@ -167,8 +167,7 @@ struct Ioproc Ioproc *next; }; -void _gotolabel(Label*); -int _setlabel(Label*); +void _swaplabel(Label*, Label*); void _freeproc(Proc*); Proc* _newproc(void(*)(void*), void*, uint, char*, int, int); int _procsplhi(void); @@ -178,7 +177,7 @@ int _schedexec(Execargs*); void _schedexecwait(void); void _schedexit(Proc*); int _schedfork(Proc*); -void _schedinit(void*); +void _scheduler(void*); void _systhreadinit(void); void _threadassert(char*); void __threaddebug(ulong, char*, ...); blob - /dev/null blob + 60f803d3cfaeb62186deed0c6ed821d179e8e3ed (mode 644) --- /dev/null +++ src/libthread/ucontext.c @@ -0,0 +1,41 @@ +#include "threadimpl.h" + +void +_threadinitstack(Thread *t, void (*f)(void*), void *arg) +{ + sigset_t zero; + + /* do a reasonable initialization */ + memset(&t->sched.uc, 0, sizeof t->sched.uc); + sigemptyset(&zero); + sigprocmask(SIG_BLOCK, &zero, &t->sched.uc.uc_sigmask); + + /* call getcontext, because on Linux makecontext neglects floating point */ + getcontext(&t->sched.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); +} + +void +_threadinswitch(int enter) +{ + USED(enter); +} + +void +_threadstacklimit(void *bottom, void *top) +{ + USED(bottom); + USED(top); +} + +void +_swaplabel(Label *old, Label *new) +{ + if(swapcontext(&old->uc, &new->uc) < 0) + sysfatal("swapcontext: %r"); +} + blob - bed2755a82148f8cca407a464ad5ae89359f0528 blob + 87df6c14654f20eb2df69eeafc21ee1f5eabd47c --- src/mkfile +++ src/mkfile @@ -9,6 +9,14 @@ DIRS=\