diff options
author | Petter Rodhelind <petter.rodhelind@gmail.com> | 2020-02-11 13:40:11 +0100 |
---|---|---|
committer | Petter Rodhelind <petter.rodhelind@gmail.com> | 2020-02-11 13:40:11 +0100 |
commit | 9c79e48c93c0c4d14aabcb490fab048d68934cb2 (patch) | |
tree | 1d57d3fd193621a2357473bb65b92190914c5736 /src/libthread/NetBSD.c | |
parent | 02d7aa8915f9c3a3288dab01f321eb94ba219e3b (diff) | |
parent | 0237dec768a4ee36ae9e18ce8566d2c999d78410 (diff) | |
download | plan9port-9c79e48c93c0c4d14aabcb490fab048d68934cb2.tar.gz plan9port-9c79e48c93c0c4d14aabcb490fab048d68934cb2.tar.bz2 plan9port-9c79e48c93c0c4d14aabcb490fab048d68934cb2.zip |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src/libthread/NetBSD.c')
-rw-r--r-- | src/libthread/NetBSD.c | 438 |
1 files changed, 437 insertions, 1 deletions
diff --git a/src/libthread/NetBSD.c b/src/libthread/NetBSD.c index 37dabe9c..2b14146b 100644 --- a/src/libthread/NetBSD.c +++ b/src/libthread/NetBSD.c @@ -1 +1,437 @@ -#include "Linux.c" +#include "threadimpl.h" + +#undef exits +#undef _exits + +static int +timefmt(Fmt *fmt) +{ + static char *mon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + vlong ns; + Tm tm; + ns = nsec(); + tm = *localtime(time(0)); + return fmtprint(fmt, "%s %2d %02d:%02d:%02d.%03d", + mon[tm.mon], tm.mday, tm.hour, tm.min, tm.sec, + (int)(ns%1000000000)/1000000); +} + +/* + * spin locks + */ +extern int _tas(int*); + +void +_threadunlock(Lock *l, ulong pc) +{ + USED(pc); + + l->held = 0; +} + +int +_threadlock(Lock *l, int block, ulong pc) +{ + int i; +static int first=1; +if(first) {first=0; fmtinstall('\001', timefmt);} + + USED(pc); + + /* once fast */ + if(!_tas(&l->held)) + return 1; + if(!block) + return 0; + + /* a thousand times pretty fast */ + for(i=0; i<1000; i++){ + if(!_tas(&l->held)) + return 1; + sched_yield(); + } + /* now increasingly slow */ + for(i=0; i<10; i++){ + if(!_tas(&l->held)) + return 1; + usleep(1); + } +fprint(2, "%\001 %s: lock loop1 %p from %lux\n", argv0, l, pc); + for(i=0; i<10; i++){ + if(!_tas(&l->held)) + return 1; + usleep(10); + } +fprint(2, "%\001 %s: lock loop2 %p from %lux\n", argv0, l, pc); + for(i=0; i<10; i++){ + if(!_tas(&l->held)) + return 1; + usleep(100); + } +fprint(2, "%\001 %s: lock loop3 %p from %lux\n", argv0, l, pc); + for(i=0; i<10; i++){ + if(!_tas(&l->held)) + return 1; + usleep(1000); + } +fprint(2, "%\001 %s: lock loop4 %p from %lux\n", argv0, l, pc); + for(i=0; i<10; i++){ + if(!_tas(&l->held)) + return 1; + usleep(10*1000); + } +fprint(2, "%\001 %s: lock loop5 %p from %lux\n", argv0, l, pc); + for(i=0; i<1000; i++){ + if(!_tas(&l->held)) + return 1; + usleep(100*1000); + } +fprint(2, "%\001 %s: lock loop6 %p from %lux\n", argv0, l, pc); + /* take your time */ + while(_tas(&l->held)) + usleep(1000*1000); + return 1; +} + +/* + * sleep and wakeup + */ +static void +ign(int x) +{ + USED(x); +} + +static void /*__attribute__((constructor))*/ +ignusr1(int restart) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof sa); + sa.sa_handler = ign; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGUSR1); + if(restart) + sa.sa_flags = SA_RESTART; + sigaction(SIGUSR1, &sa, nil); +} + +void +_procsleep(_Procrendez *r) +{ + sigset_t mask; + + /* + * Go to sleep. + * + * Block USR1, set the handler to interrupt system calls, + * unlock the vouslock so our waker can wake us, + * and then suspend. + */ +again: + r->asleep = 1; + r->pid = getpid(); + + sigprocmask(SIG_SETMASK, nil, &mask); + sigaddset(&mask, SIGUSR1); + sigprocmask(SIG_SETMASK, &mask, nil); + ignusr1(0); + unlock(r->l); + sigdelset(&mask, SIGUSR1); + sigsuspend(&mask); + + /* + * We're awake. Make USR1 not interrupt system calls. + */ + lock(r->l); + ignusr1(1); + if(r->asleep && r->pid == getpid()){ + /* Didn't really wake up - signal from something else */ + goto again; + } +} + +void +_procwakeupandunlock(_Procrendez *r) +{ + int pid; + + pid = 0; + if(r->asleep){ + r->asleep = 0; + assert(r->pid >= 1); + pid = r->pid; + } + assert(r->l); + unlock(r->l); + if(pid) + kill(pid, SIGUSR1); +} + +/* + * process creation and exit + */ +typedef struct Stackfree Stackfree; +struct Stackfree +{ + Stackfree *next; + int pid; + int pid1; +}; +static Lock stacklock; +static Stackfree *stackfree; + +static void +delayfreestack(uchar *stk, int pid, int pid1) +{ + Stackfree *sf; + + sf = (Stackfree*)stk; + sf->pid = pid; + sf->pid1 = pid1; + lock(&stacklock); + sf->next = stackfree; + stackfree = sf; + unlock(&stacklock); +} + +static void +dofreestacks(void) +{ + Stackfree *sf, *last, *next; + + if(stackfree==nil || !canlock(&stacklock)) + return; + + for(last=nil,sf=stackfree; sf; last=sf,sf=next){ + next = sf->next; + if(sf->pid >= 1 && kill(sf->pid, 0) < 0 && errno == ESRCH) + if(sf->pid1 >= 1 && kill(sf->pid1, 0) < 0 && errno == ESRCH){ + free(sf); + if(last) + last->next = next; + else + stackfree = next; + sf = last; + } + } + unlock(&stacklock); +} + +static int +startprocfn(void *v) +{ + void **a; + uchar *stk; + void (*fn)(void*); + Proc *p; + int pid0, pid1; + + a = (void**)v; + fn = a[0]; + p = a[1]; + stk = a[2]; + pid0 = (int)a[4]; + pid1 = getpid(); + free(a); + p->osprocid = pid1; + + (*fn)(p); + + delayfreestack(stk, pid0, pid1); + _exit(0); + return 0; +} + +/* + * indirect through here so that parent need not wait for child zombie + * + * slight race - if child exits and then another process starts before we + * manage to exit, we'll be running on a freed stack. + */ +static int +trampnowait(void *v) +{ + void **a; + int *kidpid; + + a = (void*)v; + kidpid = a[3]; + a[4] = (void*)getpid(); + *kidpid = clone(startprocfn, a[2]+65536-512, CLONE_VM|CLONE_FILES, a); + _exit(0); + return 0; +} + +void +_procstart(Proc *p, void (*fn)(Proc*)) +{ + void **a; + uchar *stk; + int pid, kidpid, status; + + dofreestacks(); + a = malloc(5*sizeof a[0]); + if(a == nil) + sysfatal("_procstart malloc: %r"); + stk = malloc(65536); + if(stk == nil) + sysfatal("_procstart malloc stack: %r"); + + a[0] = fn; + a[1] = p; + a[2] = stk; + a[3] = &kidpid; + kidpid = -1; + + pid = clone(trampnowait, stk+65536-16, CLONE_VM|CLONE_FILES, a); + if(pid > 0) + if(wait4(pid, &status, __WALL, 0) < 0) + fprint(2, "ffork wait4: %r\n"); + if(pid < 0 || kidpid < 0){ + fprint(2, "_procstart clone: %r\n"); + abort(); + } +} + +static char *threadexitsmsg; +void +sigusr2handler(int s) +{ +/* fprint(2, "%d usr2 %d\n", time(0), getpid()); */ + if(threadexitsmsg) + _exits(threadexitsmsg); +} + +void +threadexitsall(char *msg) +{ + static int pid[1024]; + int i, npid, mypid; + Proc *p; + + if(msg == nil) + msg = ""; + + /* + * Only one guy, ever, gets to run this. + * If two guys do it, inevitably they end up + * tripping over each other in the underlying + * C library exit() implementation, which is + * trying to run the atexit handlers and apparently + * not thread safe. This has been observed on + * both Linux and OpenBSD. Sigh. + */ + { + static Lock onelock; + if(!canlock(&onelock)) + _exits(threadexitsmsg); + threadexitsmsg = msg; + } + + mypid = getpid(); + lock(&_threadprocslock); + npid = 0; + for(p=_threadprocs; p; p=p->next) + if(p->osprocid != mypid && p->osprocid >= 1) + pid[npid++] = p->osprocid; + for(i=0; i<npid; i++) + kill(pid[i], SIGUSR2); + unlock(&_threadprocslock); + exits(msg); +} + +/* + * per-process data, indexed by pid + * + * could use modify_ldt and a segment register + * to avoid the many calls to getpid(), but i don't + * care -- this is compatibility code. linux 2.6 with + * nptl is a good enough pthreads to avoid this whole file. + */ +typedef struct Perproc Perproc; +struct Perproc +{ + int pid; + Proc *proc; +}; + +static Lock perlock; +static Perproc perproc[1024]; +#define P ((Proc*)-1) + +static Perproc* +myperproc(void) +{ + int i, pid, h; + Perproc *p; + + pid = getpid(); + h = pid%nelem(perproc); + for(i=0; i<nelem(perproc); i++){ + p = &perproc[(i+h)%nelem(perproc)]; + if(p->pid == pid) + return p; + if(p->pid == 0){ + print("found 0 at %d (h=%d)\n", (i+h)%nelem(perproc), h); + break; + } + } + fprint(2, "myperproc %d (%s): cannot find self\n", pid, argv0); + abort(); + return nil; +} + +static Perproc* +newperproc(void) +{ + int i, pid, h; + Perproc *p; + + lock(&perlock); + pid = getpid(); + h = pid%nelem(perproc); + for(i=0; i<nelem(perproc); i++){ + p = &perproc[(i+h)%nelem(perproc)]; + if(p->pid == pid || p->pid == -1 || p->pid == 0){ + p->pid = pid; + unlock(&perlock); + return p; + } + } + fprint(2, "newperproc %d: out of procs\n", pid); + abort(); + return nil; +} + +Proc* +_threadproc(void) +{ + return myperproc()->proc; +} + +void +_threadsetproc(Proc *p) +{ + Perproc *pp; + + if(p) + p->osprocid = getpid(); + pp = newperproc(); + pp->proc = p; + if(p == nil) + pp->pid = -1; +} + +void +_pthreadinit(void) +{ + signal(SIGUSR2, sigusr2handler); +} + +void +_threadpexit(void) +{ + _exit(0); +} |