From f1e8706de85f43523a789f4411b5727ed5211ec3 Mon Sep 17 00:00:00 2001 From: rsc Date: Mon, 27 Dec 2004 16:52:46 +0000 Subject: linux pre-2.6 implementation --- src/libthread/Linux.c | 348 +++++++++++++++++++++++++++++++++++++++++++++++ src/libthread/Linuxasm.s | 7 + 2 files changed, 355 insertions(+) create mode 100644 src/libthread/Linux.c create mode 100644 src/libthread/Linuxasm.s (limited to 'src/libthread') diff --git a/src/libthread/Linux.c b/src/libthread/Linux.c new file mode 100644 index 00000000..4be2c481 --- /dev/null +++ b/src/libthread/Linux.c @@ -0,0 +1,348 @@ +#include "u.h" +#include +#include +#include +#include +#include +#include "libc.h" +#include "thread.h" +#include "threadimpl.h" + +/* + * 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; +} + +/* + * 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. + */ + 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. + */ + ignusr1(1); + assert(r->asleep == 0); + lock(r->l); +} + +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); + last->next = next; + sf = last; + } + } +} + +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; +} + +/* + * 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; + + a = (void*)v; + *(int*)a[3] = 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(4*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) +{ + 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; ipid == 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; ipid == 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; + + p->osprocid = getpid(); + pp = newperproc(); + pp->proc = p; + if(p == nil) + pp->pid = -1; +} + +void +_pthreadinit(void) +{ + signal(SIGUSR2, sigusr2handler); +} + diff --git a/src/libthread/Linuxasm.s b/src/libthread/Linuxasm.s new file mode 100644 index 00000000..197f12b5 --- /dev/null +++ b/src/libthread/Linuxasm.s @@ -0,0 +1,7 @@ +.globl _tas +_tas: + movl $0xCAFEBABE, %eax + movl 4(%esp), %ecx + xchgl %eax, 0(%ecx) + ret + -- cgit v1.2.3