diff options
author | rsc <devnull@localhost> | 2004-12-28 03:50:11 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2004-12-28 03:50:11 +0000 |
commit | 31cf0ab11fd456c828ae7e2473dc6b73789bd20e (patch) | |
tree | 1723e069decefd732318000fb8885bf58c40da92 | |
parent | 93eb807ac2b7c23cae2b0999957532b5b666f437 (diff) | |
download | plan9port-31cf0ab11fd456c828ae7e2473dc6b73789bd20e.tar.gz plan9port-31cf0ab11fd456c828ae7e2473dc6b73789bd20e.tar.bz2 plan9port-31cf0ab11fd456c828ae7e2473dc6b73789bd20e.zip |
attempt at FreeBSD pre-5 support
-rw-r--r-- | src/libthread/FreeBSD.c | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/src/libthread/FreeBSD.c b/src/libthread/FreeBSD.c new file mode 100644 index 00000000..77c4869a --- /dev/null +++ b/src/libthread/FreeBSD.c @@ -0,0 +1,356 @@ +#include "u.h" +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sched.h> +#include <signal.h> +#include "libc.h" +#include "thread.h" +#include "threadimpl.h" + +extern int __isthreaded; + +/* + * 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; + + 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 nice and slow */ + for(i=0; i<1000; i++){ + if(!_tas(&l->held)) + return 1; + usleep(100*1000); + } + /* take your time */ + while(_tas(&l->held)) + usleep(1000*1000); + return 1; +} + +/* + * For FreeBSD libc. + */ + +typedef struct { + volatile long access_lock; + volatile long lock_owner; + volatile char *fname; + volatile int lineno; +} spinlock_t; + +void +_spinlock(spinlock_t *lk) +{ + lock((Lock*)&lk->access_lock); +} + __isthreaded = 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 +_procwakeup(_Procrendez *r) +{ + if(r->asleep){ + r->asleep = 0; + assert(r->pid >= 1); + kill(r->pid, SIGUSR1); + } +} + +/* + * process creation and exit + */ +typedef struct Stackfree Stackfree; +struct Stackfree +{ + Stackfree *next; + int pid; +}; +static Lock stacklock; +static Stackfree *stackfree; + +static void +delayfreestack(uchar *stk) +{ + Stackfree *sf; + + sf = (Stackfree*)stk; + sf->pid = getpid(); + 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){ + 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; + + a = (void**)v; + fn = a[0]; + p = a[1]; + stk = a[2]; + free(a); + p->osprocid = getpid(); + + (*fn)(p); + + delayfreestack(stk); + _exit(0); + return 0; +} + +void +_procstart(Proc *p, void (*fn)(Proc*)) +{ + void **a; + uchar *stk; + int pid; + + dofreestacks(); + a = malloc(3*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; + + pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, stk+65536-64, startprocfn, a); + if(pid < 0){ + fprint(2, "_procstart rfork_thread: %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 = ""; + mypid = getpid(); + lock(&_threadprocslock); + threadexitsmsg = msg; + 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: cannot find self\n", pid); + 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); +} + |