diff options
Diffstat (limited to 'src/libthread')
32 files changed, 578 insertions, 1564 deletions
diff --git a/src/libthread/OpenBSD-386.c b/src/libthread/386-ucontext.c index 89bfedcd..3afa9513 100644 --- a/src/libthread/OpenBSD-386.c +++ b/src/libthread/386-ucontext.c @@ -5,7 +5,7 @@ makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) { int *sp; - sp = (int*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/4; + sp = USPALIGN(ucp, 4); sp -= argc; memmove(sp, &argc+1, argc*sizeof(int)); *--sp = 0; /* return address */ diff --git a/src/libthread/BSD.c b/src/libthread/BSD.c deleted file mode 100644 index 737c0a63..00000000 --- a/src/libthread/BSD.c +++ /dev/null @@ -1,403 +0,0 @@ -#undef exits -#undef _exits - -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; - sleep(0); - } - /* increasingly slow */ - for(i=0; i<10; i++){ - if(!_tas(&l->held)) - return 1; - usleep(1); - } - for(i=0; i<10; i++){ - if(!_tas(&l->held)) - return 1; - usleep(10); - } - for(i=0; i<10; i++){ - if(!_tas(&l->held)) - return 1; - usleep(100); - } - for(i=0; i<10; i++){ - if(!_tas(&l->held)) - return 1; - usleep(1000); - } - for(i=0; i<10; i++){ - if(!_tas(&l->held)) - return 1; - usleep(10*1000); - } - /* 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 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); -} - -void -_spinunlock(spinlock_t *lk) -{ - unlock((Lock*)&lk->access_lock); -} - - - -/* - * 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); - } -} - -void -_procwakeupandunlock(_Procrendez *r) -{ - _procwakeup(r); - unlock(r->l); -} - - -/* - * 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 = ""; - - /* - * 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; - } - - 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 - */ -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 -_threadpexit(void) -{ - _exit(0); -} diff --git a/src/libthread/COPYRIGHT b/src/libthread/COPYRIGHT index f4ee354e..d0820965 100644 --- a/src/libthread/COPYRIGHT +++ b/src/libthread/COPYRIGHT @@ -45,9 +45,9 @@ Contains parts of an earlier library that has: === -The above notices do *NOT* apply to Linux-sparc64-context.S -or to Linux-sparc64-swapcontext.c. Those are functions from +The above notices do *NOT* apply to Linux-sparc64-context.S +or to sparc64-ucontext.c. Those are functions from the GNU C library and are provided for systems that use the GNU C -library but somehow are missing those functions. They are +library but somehow are missing those functions. They are distributed under the Lesser GPL; see COPYING.SPARC64-CONTEXT. diff --git a/src/libthread/Darwin-386-asm.s b/src/libthread/Darwin-386-asm.s deleted file mode 100644 index 46c96e94..00000000 --- a/src/libthread/Darwin-386-asm.s +++ /dev/null @@ -1,52 +0,0 @@ -.globl _tas -_tas: - movl $0xCAFEBABE, %eax - movl 4(%esp), %ecx - xchgl %eax, 0(%ecx) - ret - -.globl _getmcontext -_getmcontext: - movl 4(%esp), %eax - - movl %fs, 8(%eax) - movl %es, 12(%eax) - movl %ds, 16(%eax) - movl %ss, 76(%eax) - movl %edi, 20(%eax) - movl %esi, 24(%eax) - movl %ebp, 28(%eax) - movl %ebx, 36(%eax) - movl %edx, 40(%eax) - movl %ecx, 44(%eax) - - movl $1, 48(%eax) /* %eax */ - movl (%esp), %ecx /* %eip */ - movl %ecx, 60(%eax) - leal 4(%esp), %ecx /* %esp */ - movl %ecx, 72(%eax) - - movl 44(%eax), %ecx /* restore %ecx */ - movl $0, %eax - ret - -.globl _setmcontext -_setmcontext: - movl 4(%esp), %eax - - movl 8(%eax), %fs - movl 12(%eax), %es - movl 16(%eax), %ds - movl 76(%eax), %ss - movl 20(%eax), %edi - movl 24(%eax), %esi - movl 28(%eax), %ebp - movl 36(%eax), %ebx - movl 40(%eax), %edx - movl 44(%eax), %ecx - - movl 72(%eax), %esp - pushl 60(%eax) /* new %eip */ - movl 48(%eax), %eax - ret - diff --git a/src/libthread/Darwin-386.c b/src/libthread/Darwin-386.c deleted file mode 100644 index b138e420..00000000 --- a/src/libthread/Darwin-386.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "threadimpl.h" - -void -makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) -{ - int *sp; - - sp = (int*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/4; - sp -= argc; - /* - * Stack pointer at call instruction (before return address - * gets pushed) must be 16-byte aligned. - */ - if((uintptr)sp%4) - abort(); - while((uintptr)sp%16) - sp--; - memmove(sp, &argc+1, argc*sizeof(int)); - *--sp = 0; /* return address */ - ucp->uc_mcontext.mc_eip = (long)func; - ucp->uc_mcontext.mc_esp = (int)sp; -} - -int -swapcontext(ucontext_t *oucp, ucontext_t *ucp) -{ - if(getcontext(oucp) == 0) - setcontext(ucp); - return 0; -} diff --git a/src/libthread/Darwin-power-asm.s b/src/libthread/Darwin-power-asm.s deleted file mode 100644 index a064b5ea..00000000 --- a/src/libthread/Darwin-power-asm.s +++ /dev/null @@ -1,83 +0,0 @@ -/* get FPR and VR use flags with sc 0x7FF3 */ -/* get vsave with mfspr reg, 256 */ - -.text -.align 2 - -.globl __getmcontext - -__getmcontext: /* xxx: instruction scheduling */ - mflr r0 - mfcr r5 - mfctr r6 - mfxer r7 - stw r0, 0*4(r3) - stw r5, 1*4(r3) - stw r6, 2*4(r3) - stw r7, 3*4(r3) - - stw r1, 4*4(r3) - stw r2, 5*4(r3) - li r5, 1 /* return value for setmcontext */ - stw r5, 6*4(r3) - - stw r13, (0+7)*4(r3) /* callee-save GPRs */ - stw r14, (1+7)*4(r3) /* xxx: block move */ - stw r15, (2+7)*4(r3) - stw r16, (3+7)*4(r3) - stw r17, (4+7)*4(r3) - stw r18, (5+7)*4(r3) - stw r19, (6+7)*4(r3) - stw r20, (7+7)*4(r3) - stw r21, (8+7)*4(r3) - stw r22, (9+7)*4(r3) - stw r23, (10+7)*4(r3) - stw r24, (11+7)*4(r3) - stw r25, (12+7)*4(r3) - stw r26, (13+7)*4(r3) - stw r27, (14+7)*4(r3) - stw r28, (15+7)*4(r3) - stw r29, (16+7)*4(r3) - stw r30, (17+7)*4(r3) - stw r31, (18+7)*4(r3) - - li r3, 0 /* return */ - blr - -.globl __setmcontext - -__setmcontext: - lwz r13, (0+7)*4(r3) /* callee-save GPRs */ - lwz r14, (1+7)*4(r3) /* xxx: block move */ - lwz r15, (2+7)*4(r3) - lwz r16, (3+7)*4(r3) - lwz r17, (4+7)*4(r3) - lwz r18, (5+7)*4(r3) - lwz r19, (6+7)*4(r3) - lwz r20, (7+7)*4(r3) - lwz r21, (8+7)*4(r3) - lwz r22, (9+7)*4(r3) - lwz r23, (10+7)*4(r3) - lwz r24, (11+7)*4(r3) - lwz r25, (12+7)*4(r3) - lwz r26, (13+7)*4(r3) - lwz r27, (14+7)*4(r3) - lwz r28, (15+7)*4(r3) - lwz r29, (16+7)*4(r3) - lwz r30, (17+7)*4(r3) - lwz r31, (18+7)*4(r3) - - lwz r1, 4*4(r3) - lwz r2, 5*4(r3) - - lwz r0, 0*4(r3) - mtlr r0 - lwz r0, 1*4(r3) - mtcr r0 /* mtcrf 0xFF, r0 */ - lwz r0, 2*4(r3) - mtctr r0 - lwz r0, 3*4(r3) - mtxer r0 - - lwz r3, 6*4(r3) - blr diff --git a/src/libthread/Darwin-x86_64-swapcontext.c b/src/libthread/Darwin-x86_64-swapcontext.c deleted file mode 100644 index 27931456..00000000 --- a/src/libthread/Darwin-x86_64-swapcontext.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "threadimpl.h" - -void -makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) -{ - uintptr *sp; - va_list arg; - -//fprint(2, "makecontext %d\n", argc); - if(argc != 2) - sysfatal("libthread: makecontext misused"); - va_start(arg, argc); - uc->mc.di = va_arg(arg, uint); - uc->mc.si = va_arg(arg, uint); -//fprint(2, "%ux %ux\n", uc->mc.di, uc->mc.si); - va_end(arg); - - sp = (uintptr*)((char*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size); - *--sp = 0; // fn's return address - *--sp = (uintptr)fn; // return address of setcontext - uc->mc.sp = (uintptr)sp; -} - -int -swapcontext(ucontext_t *oucp, ucontext_t *ucp) -{ - if(getcontext(oucp) == 0) - setcontext(ucp); - return 0; -} diff --git a/src/libthread/FreeBSD-386-asm.s b/src/libthread/FreeBSD-386-asm.s deleted file mode 100644 index 42169853..00000000 --- a/src/libthread/FreeBSD-386-asm.s +++ /dev/null @@ -1,52 +0,0 @@ -.globl _tas -_tas: - movl $0xCAFEBABE, %eax - movl 4(%esp), %ecx - xchgl %eax, 0(%ecx) - ret - -.globl getmcontext -getmcontext: - movl 4(%esp), %eax - - movl %fs, 8(%eax) - movl %es, 12(%eax) - movl %ds, 16(%eax) - movl %ss, 76(%eax) - movl %edi, 20(%eax) - movl %esi, 24(%eax) - movl %ebp, 28(%eax) - movl %ebx, 36(%eax) - movl %edx, 40(%eax) - movl %ecx, 44(%eax) - - movl $1, 48(%eax) /* %eax */ - movl (%esp), %ecx /* %eip */ - movl %ecx, 60(%eax) - leal 4(%esp), %ecx /* %esp */ - movl %ecx, 72(%eax) - - movl 44(%eax), %ecx /* restore %ecx */ - movl $0, %eax - ret - -.globl setmcontext -setmcontext: - movl 4(%esp), %eax - - movl 8(%eax), %fs - movl 12(%eax), %es - movl 16(%eax), %ds - movl 76(%eax), %ss - movl 20(%eax), %edi - movl 24(%eax), %esi - movl 28(%eax), %ebp - movl 36(%eax), %ebx - movl 40(%eax), %edx - movl 44(%eax), %ecx - - movl 72(%eax), %esp - pushl 60(%eax) /* new %eip */ - movl 48(%eax), %eax - ret - diff --git a/src/libthread/FreeBSD.c b/src/libthread/FreeBSD.c deleted file mode 100644 index 5c282465..00000000 --- a/src/libthread/FreeBSD.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "threadimpl.h" - -#include "BSD.c" - -/* - * FreeBSD 4 and earlier needs the context functions. - */ -void -makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) -{ - int *sp; - - sp = (int*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/4; - sp -= argc; - memmove(sp, &argc+1, argc*sizeof(int)); - *--sp = 0; /* return address */ - ucp->uc_mcontext.mc_eip = (long)func; - ucp->uc_mcontext.mc_esp = (int)sp; -} - -int -swapcontext(ucontext_t *oucp, ucontext_t *ucp) -{ - if(getcontext(oucp) == 0) - setcontext(ucp); - return 0; -} - -void -_pthreadinit(void) -{ - __isthreaded = 1; - signal(SIGUSR2, sigusr2handler); -} diff --git a/src/libthread/Linux-386-asm.s b/src/libthread/Linux-386-asm.s deleted file mode 100644 index 197f12b5..00000000 --- a/src/libthread/Linux-386-asm.s +++ /dev/null @@ -1,7 +0,0 @@ -.globl _tas -_tas: - movl $0xCAFEBABE, %eax - movl 4(%esp), %ecx - xchgl %eax, 0(%ecx) - ret - diff --git a/src/libthread/Linux-arm-asm.s b/src/libthread/Linux-arm-asm.s index 5a285bf2..9bd54f8a 100644 --- a/src/libthread/Linux-arm-asm.s +++ b/src/libthread/Linux-arm-asm.s @@ -1,16 +1,5 @@ - -.globl _tas -_tas: - mov r3, #0xCA000000 - add r3, r3, #0xFE0000 - add r3, r3, #0xBA00 - add r3, r3, #0xBE - swp r3, r3, [r0] - mov r0, r3 - mov pc, lr - -.globl getmcontext -getmcontext: +.globl mygetmcontext +mygetmcontext: str r1, [r0,#4] str r2, [r0,#8] str r3, [r0,#12] @@ -32,8 +21,8 @@ getmcontext: mov r0, #0 mov pc, lr -.globl setmcontext -setmcontext: +.globl mysetmcontext +mysetmcontext: ldr r1, [r0,#4] ldr r2, [r0,#8] ldr r3, [r0,#12] @@ -50,4 +39,3 @@ setmcontext: ldr r14, [r0,#56] ldr r0, [r0] mov pc, lr - diff --git a/src/libthread/Linux-arm-context.s b/src/libthread/Linux-arm-context.s deleted file mode 100644 index 9bd54f8a..00000000 --- a/src/libthread/Linux-arm-context.s +++ /dev/null @@ -1,41 +0,0 @@ -.globl mygetmcontext -mygetmcontext: - str r1, [r0,#4] - str r2, [r0,#8] - str r3, [r0,#12] - str r4, [r0,#16] - str r5, [r0,#20] - str r6, [r0,#24] - str r7, [r0,#28] - str r8, [r0,#32] - str r9, [r0,#36] - str r10, [r0,#40] - str r11, [r0,#44] - str r12, [r0,#48] - str r13, [r0,#52] - str r14, [r0,#56] - /* store 1 as r0-to-restore */ - mov r1, #1 - str r1, [r0] - /* return 0 */ - mov r0, #0 - mov pc, lr - -.globl mysetmcontext -mysetmcontext: - ldr r1, [r0,#4] - ldr r2, [r0,#8] - ldr r3, [r0,#12] - ldr r4, [r0,#16] - ldr r5, [r0,#20] - ldr r6, [r0,#24] - ldr r7, [r0,#28] - ldr r8, [r0,#32] - ldr r9, [r0,#36] - ldr r10, [r0,#40] - ldr r11, [r0,#44] - ldr r12, [r0,#48] - ldr r13, [r0,#52] - ldr r14, [r0,#56] - ldr r0, [r0] - mov pc, lr diff --git a/src/libthread/Linux-power-asm.s b/src/libthread/Linux-power-asm.s deleted file mode 100644 index d6e21c15..00000000 --- a/src/libthread/Linux-power-asm.s +++ /dev/null @@ -1,16 +0,0 @@ - .globl _tas -_tas: - li %r0, 0 - mr %r4, %r3 - lis %r5, 0xcafe - ori %r5, %r5, 0xbabe -1: - lwarx %r3, %r0, %r4 - cmpwi %r3, 0 - bne 2f - stwcx. %r5, %r0, %r4 - bne- 1b -2: - sync - blr - diff --git a/src/libthread/Linux-sparc64-asm.s b/src/libthread/Linux-sparc64-asm.s deleted file mode 100644 index 422a1b24..00000000 --- a/src/libthread/Linux-sparc64-asm.s +++ /dev/null @@ -1,16 +0,0 @@ -! Actually sparc32 assembly. -! Debian's sparc64 port is a 32-bit user space. - - .section ".text", #alloc, #execinstr - .align 8 - .skip 16 - .global _tas -! .type _tas,2 -_tas: - or %g0,1,%o1 - swap [%o0],%o1 ! o0 points to lock; key is first word - retl - mov %o1, %o0 - - .size _tas,(.-_tas) - diff --git a/src/libthread/Linux.c b/src/libthread/Linux.c deleted file mode 100644 index 31577e87..00000000 --- a/src/libthread/Linux.c +++ /dev/null @@ -1,462 +0,0 @@ -#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); -} - -#ifdef __arm__ -void -makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) -{ - int i, *sp; - va_list arg; - - sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4; - va_start(arg, argc); - for(i=0; i<4 && i<argc; i++) - uc->uc_mcontext.gregs[i] = va_arg(arg, uint); - va_end(arg); - uc->uc_mcontext.gregs[13] = (uint)sp; - uc->uc_mcontext.gregs[14] = (uint)fn; -} - -int -swapcontext(ucontext_t *oucp, const ucontext_t *ucp) -{ - if(getcontext(oucp) == 0) - setcontext(ucp); - return 0; -} -#endif 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); +} diff --git a/src/libthread/OpenBSD-386-asm.s b/src/libthread/OpenBSD-386-asm.s index dad1b536..ed18d2f0 100644 --- a/src/libthread/OpenBSD-386-asm.s +++ b/src/libthread/OpenBSD-386-asm.s @@ -1 +1,45 @@ -#include "FreeBSD-386-asm.s" +.globl getmcontext +getmcontext: + movl 4(%esp), %eax + + movl %fs, 8(%eax) + movl %es, 12(%eax) + movl %ds, 16(%eax) + movl %ss, 76(%eax) + movl %edi, 20(%eax) + movl %esi, 24(%eax) + movl %ebp, 28(%eax) + movl %ebx, 36(%eax) + movl %edx, 40(%eax) + movl %ecx, 44(%eax) + + movl $1, 48(%eax) /* %eax */ + movl (%esp), %ecx /* %eip */ + movl %ecx, 60(%eax) + leal 4(%esp), %ecx /* %esp */ + movl %ecx, 72(%eax) + + movl 44(%eax), %ecx /* restore %ecx */ + movl $0, %eax + ret + +.globl setmcontext +setmcontext: + movl 4(%esp), %eax + + movl 8(%eax), %fs + movl 12(%eax), %es + movl 16(%eax), %ds + movl 76(%eax), %ss + movl 20(%eax), %edi + movl 24(%eax), %esi + movl 28(%eax), %ebp + movl 36(%eax), %ebx + movl 40(%eax), %edx + movl 44(%eax), %ecx + + movl 72(%eax), %esp + pushl 60(%eax) /* new %eip */ + movl 48(%eax), %eax + ret + diff --git a/src/libthread/OpenBSD-power-asm.S b/src/libthread/OpenBSD-power-asm.S index 03b46e7b..36035eb5 100644 --- a/src/libthread/OpenBSD-power-asm.S +++ b/src/libthread/OpenBSD-power-asm.S @@ -1,18 +1,3 @@ -ENTRY(_tas) - li %r0, 0 - mr %r4, %r3 - lis %r5, 0xcafe - ori %r5, %r5, 0xbabe -1: - lwarx %r3, %r0, %r4 - cmpwi %r3, 0 - bne 2f - stwcx. %r5, %r0, %r4 - bne- 1b -2: - sync - blr - ENTRY(_getmcontext) /* xxx: instruction scheduling */ mflr %r0 mfcr %r5 diff --git a/src/libthread/OpenBSD-power.c b/src/libthread/OpenBSD-power.c deleted file mode 100644 index eab711f2..00000000 --- a/src/libthread/OpenBSD-power.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "threadimpl.h" - -void -makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) -{ - ulong *sp, *tos; - va_list arg; - - tos = (ulong*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(ulong); - sp = (ulong*)((ulong)(tos-16) & ~15); - ucp->mc.pc = (long)func; - ucp->mc.sp = (long)sp; - va_start(arg, argc); - ucp->mc.r3 = va_arg(arg, long); - va_end(arg); -} - -int -swapcontext(ucontext_t *oucp, ucontext_t *ucp) -{ - if(getcontext(oucp) == 0) - setcontext(ucp); - return 0; -} diff --git a/src/libthread/OpenBSD.c b/src/libthread/OpenBSD.c deleted file mode 100644 index 80a50977..00000000 --- a/src/libthread/OpenBSD.c +++ /dev/null @@ -1,145 +0,0 @@ -#include "threadimpl.h" -#include "BSD.c" - -#include <dlfcn.h> - -struct thread_tag { - struct thread_tag *next; - spinlock_t l; - volatile int key; - void *data; -}; - -static spinlock_t mlock; -static spinlock_t dl_lock; -static spinlock_t tag_lock; -static struct thread_tag *thread_tag_store = nil; -static uint nextkey = 0; - -void -_thread_malloc_lock(void) -{ - _spinlock(&mlock); -} - -void -_thread_malloc_unlock(void) -{ - _spinunlock(&mlock); -} - -void -_thread_malloc_init(void) -{ -} - -/* - * for ld.so - */ -void -_thread_dl_lock(int t) -{ - if(t) - _spinunlock(&dl_lock); - else - _spinlock(&dl_lock); -} - -/* - * for libc - */ -static void -_thread_tag_init(void **tag) -{ - struct thread_tag *t; - - _spinlock(&tag_lock); - if(*tag == nil) { - t = malloc(sizeof (*t)); - if(t != nil) { - memset(&t->l, 0, sizeof(t->l)); - t->key = nextkey++; - *tag = t; - } - } - _spinunlock(&tag_lock); -} - -void -_thread_tag_lock(void **tag) -{ - struct thread_tag *t; - - if(*tag == nil) - _thread_tag_init(tag); - t = *tag; - _spinlock(&t->l); -} - -void -_thread_tag_unlock(void **tag) -{ - struct thread_tag *t; - - if(*tag == nil) - _thread_tag_init(tag); - t = *tag; - _spinunlock(&t->l); -} - -static void * -_thread_tag_insert(struct thread_tag *t, void *v) -{ - t->data = v; - t->next = thread_tag_store; - thread_tag_store = t; - return t; -} - -static void * -_thread_tag_lookup(struct thread_tag *tag, int size) -{ - struct thread_tag *t; - void *p; - - _spinlock(&tag->l); - for(t = thread_tag_store; t != nil; t = t->next) - if(t->key == tag->key) - break; - if(t == nil) { - p = malloc(size); - if(p == nil) { - _spinunlock(&tag->l); - return nil; - } - _thread_tag_insert(tag, p); - } - _spinunlock(&tag->l); - return tag->data; -} - -void * -_thread_tag_storage(void **tag, void *storage, size_t n, void *err) -{ - struct thread_tag *t; - void *r; - - if(*tag == nil) - _thread_tag_init(tag); - t = *tag; - - r = _thread_tag_lookup(t, n); - if(r == nil) - r = err; - else - memcpy(r, storage, n); - return r; -} - -void -_pthreadinit(void) -{ - __isthreaded = 1; - dlctl(nil, DL_SETTHREADLCK, _thread_dl_lock); - signal(SIGUSR2, sigusr2handler); -} diff --git a/src/libthread/README.Linux b/src/libthread/README.Linux deleted file mode 100644 index 04c491a8..00000000 --- a/src/libthread/README.Linux +++ /dev/null @@ -1,40 +0,0 @@ -Thread support on Linux is confused by the recent thread local storage (TLS) -support that has been put into the ELF tool chain. The TLS libraries are -installed in /lib/tls on most Linux systems. - -We provide two different implementations of the os-dependent parts -of libthread for Linux. The first is intended for use on Linux 2.4 and earlier -kernels, which do not support TLS. It is in Linux.c and Linuxasm.c and -does not use the pthread interface. The second is intended for Linux 2.6 -and later kernels, which do support TLS. It is in pthread.c and uses the -standard pthread interface. It expects to be linked against the TLS-aware -thread library aka NPTL. - -If you use Linux.c and Linuxasm.c with TLS libraries, they do not -set up the TLS properly so you will get incorrect programs. -For example, there will only be one errno among all the procs -in your program instead of one per proc. The pthread NPTL -implementation is needed to use the TLS libraries properly. - -If you use pthread.c without TLS libraries (i.e., with the old Linux -pthread library known as LinuxThreads), then you will also get -incorrect programs, although more obviously so. The LinuxThreads -library assumes it can look at the stack pointer to distinguish between -threads, but libthread does its own stack management, breaking this -assumption. If you run a pthread-compiled program with the -LinuxThreads library, LinuxThreads itself will cause a segmentation -fault in __pthread_getspecific() the first time it is called from a -non-standard stack. - -So, it is important that you compile binaries that match your -system's choice of TLS vs. not-TLS libraries. The hard part is figuring -out which your system has chosen. Plan9port looks at the kernel -version you are running and assumes that on kernels that support -TLS (2.6+) you will be using TLS. - -Apparently Gentoo and maybe other distributions do not follow this rule. -They use non-TLS libraries even on kernels that can support TLS. -To accomodate them, you can add a line SYSVERSION=2.4 to $PLAN9/config -to force the build to think you are running an old kernel. -The INSTALL script sets up this file automatically on Linux systems. - diff --git a/src/libthread/Linux-arm-swapcontext.c b/src/libthread/arm-ucontext.c index fc0dfdda..512ca973 100644 --- a/src/libthread/Linux-arm-swapcontext.c +++ b/src/libthread/arm-ucontext.c @@ -6,7 +6,7 @@ makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) int i, *sp; va_list arg; - sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4; + sp = USPALIGN(uc, 4); va_start(arg, argc); for(i=0; i<4 && i<argc; i++) (&uc->uc_mcontext.arm_r0)[i] = va_arg(arg, uint); diff --git a/src/libthread/mkfile b/src/libthread/mkfile index f621ac60..8a77a316 100644 --- a/src/libthread/mkfile +++ b/src/libthread/mkfile @@ -16,8 +16,6 @@ OFILES=\ <$PLAN9/src/mksyslib HFILES=thread.h threadimpl.h -FreeBSD.$O: BSD.c -NetBSD.$O: Linux.c tprimes: test/tprimes.$O 9l -o $target test/$target.$O @@ -26,6 +24,8 @@ tspawn: test/tspawn.$O tspawnloop: test/tspawnloop.$O 9l -o $target test/$target.$O +tprimes tspawn tspawnloop: $PLAN9/lib/$LIB + %.$O: %.c $CC -o $target $CFLAGS -I. $stem.c @@ -39,8 +39,8 @@ OpenBSD-%-asm.$O: OpenBSD-%-asm.S Linux-sparc64-context.$O: Linux-sparc64-context.S $CC -m64 -mcpu=v9 $CFLAGS Linux-sparc64-context.S -Linux-sparc64-swapcontext.$O: Linux-sparc64-swapcontext.c - $CC -m64 -mcpu=v9 $CFLAGS Linux-sparc64-swapcontext.c +sparc64-ucontext.$O: sparc64-ucontext.c + $CC -m64 -mcpu=v9 $CFLAGS sparc64-ucontext.c test:V: tprimes tspawn primes 1 10007 >p1.txt @@ -52,4 +52,5 @@ test:V: tprimes tspawn echo tspawn should take 3 seconds, not 6 $PLAN9/bin/time ./tspawn sleep 3 >/dev/null -CLEANFILES=p1.txt p2.txt tp1.txt tp2.txt test/*.$O +CLEANFILES=p1.txt p2.txt tp1.txt tp2.txt test/*.$O tprimes tspawn + diff --git a/src/libthread/Darwin-power.c b/src/libthread/power-ucontext.c index 3f2bf566..32a8e931 100644 --- a/src/libthread/Darwin-power.c +++ b/src/libthread/power-ucontext.c @@ -6,12 +6,14 @@ makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) ulong *sp, *tos; va_list arg; - tos = (ulong*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(ulong); - sp = tos - 16; + if(argc != 2) + sysfatal("libthread: makecontext misused"); + sp = USPALIGN(ucp, 16); ucp->mc.pc = (long)func; ucp->mc.sp = (long)sp; va_start(arg, argc); ucp->mc.r3 = va_arg(arg, long); + ucp->mc.r4 = va_arg(arg, long); va_end(arg); } diff --git a/src/libthread/sparc-ucontext.h b/src/libthread/sparc-ucontext.h deleted file mode 100644 index 36b88171..00000000 --- a/src/libthread/sparc-ucontext.h +++ /dev/null @@ -1,23 +0,0 @@ -#define setcontext(u) _setmcontext(&(u)->mc) -#define getcontext(u) _getmcontext(&(u)->mc) -typedef struct mcontext mcontext_t; -typedef struct ucontext ucontext_t; -struct mcontext -{ - int r[16]; -}; - -struct ucontext -{ - struct { - void *ss_sp; - uint ss_size; - } uc_stack; - sigset_t uc_sigmask; - mcontext_t mc; -}; - -void makecontext(ucontext_t*, void(*)(void), int, ...); -int swapcontext(ucontext_t*, ucontext_t*); -int _getmcontext(mcontext_t*); -void _setmcontext(mcontext_t*); diff --git a/src/libthread/Linux-sparc64-swapcontext.c b/src/libthread/sparc64-ucontext.c index e4800c19..e4800c19 100644 --- a/src/libthread/Linux-sparc64-swapcontext.c +++ b/src/libthread/sparc64-ucontext.c diff --git a/src/libthread/stkmalloc.c b/src/libthread/stkmalloc.c new file mode 100644 index 00000000..083aea1b --- /dev/null +++ b/src/libthread/stkmalloc.c @@ -0,0 +1,13 @@ +#include "threadimpl.h" + +void* +_threadstkalloc(int n) +{ + return malloc(n); +} + +void +_threadstkfree(void *v, int n) +{ + free(v); +} diff --git a/src/libthread/stkmmap.c b/src/libthread/stkmmap.c new file mode 100644 index 00000000..f4a24630 --- /dev/null +++ b/src/libthread/stkmmap.c @@ -0,0 +1,25 @@ +#include <u.h> +#include <sys/mman.h> +#include "threadimpl.h" + +#ifndef MAP_STACK +#define MAP_STACK 0 +#endif + +void* +_threadstkalloc(int n) +{ + void *p; + + p = mmap(nil, n, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_STACK, -1, 0); + if(p == (void*)-1) + return nil; + return p; +} + +void +_threadstkfree(void *v, int n) +{ + if(n > 0) + munmap(v, n); +} diff --git a/src/libthread/sysofiles.sh b/src/libthread/sysofiles.sh index 9eeea606..20811cdf 100644 --- a/src/libthread/sysofiles.sh +++ b/src/libthread/sysofiles.sh @@ -2,40 +2,27 @@ test -f $PLAN9/config && . $PLAN9/config -tag="$OBJTYPE-$SYSNAME-${SYSVERSION:-`uname -r`}-${CC9:-cc}" -case "$tag" in -*-Linux-2.[0-5]*) - # will have to fix this for linux power pc - echo ${SYSNAME}-${OBJTYPE}-asm.o $SYSNAME.o +case "$SYSNAME" in +NetBSD) + echo ${SYSNAME}-${OBJTYPE}-asm.o $SYSNAME.o stkmalloc.o ;; -*-FreeBSD-[0-4].*) - echo ${SYSNAME}-${OBJTYPE}-asm.o $SYSNAME.o - ;; -*-NetBSD-*) - echo ${SYSNAME}-${OBJTYPE}-asm.o $SYSNAME.o - ;; -*-Darwin-10.[5-6].* | *-Darwin-[89].*) - echo ${SYSNAME}-${OBJTYPE}-asm.o $SYSNAME-${OBJTYPE}.o pthread.o - ;; -*-OpenBSD-*) - echo ${SYSNAME}-${OBJTYPE}-asm.o ${SYSNAME}-${OBJTYPE}.o pthread.o +OpenBSD) + echo pthread.o stkmmap.o ;; *) - echo pthread.o + echo pthread.o stkmalloc.o esac -case "$OBJTYPE-$SYSNAME" in -sparc64-Linux) - # Debian glibc doesn't supply swapcontext, makecontext - # so we supply our own copy from the latest glibc. - echo Linux-sparc64-context.o Linux-sparc64-swapcontext.o - ;; -arm-Linux) - # ARM doesn't supply them either. - echo Linux-arm-context.o Linux-arm-swapcontext.o - ;; -x86_64-Darwin) - echo Darwin-x86_64-asm.o Darwin-x86_64-swapcontext.o +# Various libc don't supply swapcontext, makecontext, so we do. +case "$SYSNAME-$OBJTYPE" in +Darwin-x86_64 | Linux-arm | Linux-sparc64 | NetBSD-arm | OpenBSD-386 | OpenBSD-power | OpenBSD-x86_64) + echo $OBJTYPE-ucontext.o ;; esac +# A few libc don't supply setcontext, getcontext, so we do. +case "$SYSNAME-$OBJTYPE" in +Darwin-x86_64 | Linux-arm | Linux-sparc64 | OpenBSD-386 | OpenBSD-power | OpenBSD-x86_64) + echo $SYSNAME-$OBJTYPE-asm.o + ;; +esac diff --git a/src/libthread/thread.c b/src/libthread/thread.c index d041efcc..f657b5b2 100644 --- a/src/libthread/thread.c +++ b/src/libthread/thread.c @@ -109,7 +109,7 @@ threadalloc(void (*fn)(void*), void *arg, uint stack) ulong z; /* allocate the task and stack together */ - t = malloc(sizeof *t+stack); + t = malloc(sizeof *t); if(t == nil) sysfatal("threadalloc malloc: %r"); memset(t, 0, sizeof *t); @@ -122,7 +122,9 @@ threadalloc(void (*fn)(void*), void *arg, uint stack) /* do a reasonable initialization */ if(stack == 0) return t; - t->stk = (uchar*)(t+1); + t->stk = _threadstkalloc(stack); + if(t->stk == nil) + sysfatal("threadalloc malloc stack: %r"); t->stksize = stack; memset(&t->context.uc, 0, sizeof t->context.uc); sigemptyset(&zero); @@ -134,10 +136,16 @@ threadalloc(void (*fn)(void*), void *arg, uint stack) sysfatal("threadalloc getcontext: %r"); //print("makecontext sp=%p t=%p startfn=%p\n", (char*)t->stk+t->stksize, t, t->startfn); - /* call makecontext to do the real work. */ - /* leave a few words open on both ends */ - t->context.uc.uc_stack.ss_sp = (void*)(t->stk+8); - t->context.uc.uc_stack.ss_size = t->stksize-64; + /* + * Call makecontext to do the real work. + * To avoid various mistakes on other system software, + * debuggers, and so on, don't get too close to both + * ends of the stack. Just staying away is much easier + * than debugging everything (outside our control) + * that has off-by-one errors. + */ + t->context.uc.uc_stack.ss_sp = (void*)(t->stk+64); + t->context.uc.uc_stack.ss_size = t->stksize-2*64; #if defined(__sun__) && !defined(__MAKECONTEXT_V2_SOURCE) /* sigh */ /* can avoid this with __MAKECONTEXT_V2_SOURCE but only on SunOS 5.9 */ t->context.uc.uc_stack.ss_sp = @@ -353,6 +361,7 @@ Top: delthreadinproc(p, t); p->nthread--; /*print("nthread %d\n", p->nthread); */ + _threadstkfree(t->stk, t->stksize); free(t); } @@ -509,6 +518,8 @@ needstack(int n) _Thread *t; t = proc()->thread; + if(t->stk == nil) + return; if((char*)&t <= (char*)t->stk || (char*)&t - (char*)t->stk < 256+n){ diff --git a/src/libthread/threadimpl.h b/src/libthread/threadimpl.h index 76ca57e5..cceb1b8e 100644 --- a/src/libthread/threadimpl.h +++ b/src/libthread/threadimpl.h @@ -15,15 +15,6 @@ #include "libc.h" #include "thread.h" -#if defined(__FreeBSD__) && __FreeBSD__ < 5 -extern int getmcontext(mcontext_t*); -extern void setmcontext(mcontext_t*); -#define setcontext(u) setmcontext(&(u)->uc_mcontext) -#define getcontext(u) getmcontext(&(u)->uc_mcontext) -extern int swapcontext(ucontext_t*, ucontext_t*); -extern void makecontext(ucontext_t*, void(*)(), int, ...); -#endif - #if defined(__APPLE__) /* * OS X before 10.5 (Leopard) does not provide @@ -64,20 +55,6 @@ extern void makecontext(ucontext_t*, void(*)(), int, ...); extern pid_t rfork_thread(int, void*, int(*)(void*), void*); #endif -/* THIS DOES NOT WORK! Don't do this! -(At least, not on Solaris. Maybe this is right for Linux, -in which case it should say if defined(__linux__) && defined(__sun__), -but surely the latter would be defined(__sparc__). - -#if defined(__sun__) -# define mcontext libthread_mcontext -# define mcontext_t libthread_mcontext_t -# define ucontext libthread_ucontext -# define ucontext_t libthread_ucontext_t -# include "sparc-ucontext.h" -#endif -*/ - #if defined(__arm__) int mygetmcontext(ulong*); void mysetmcontext(const ulong*); @@ -209,3 +186,8 @@ extern void _threadsetupdaemonize(void); extern void _threaddodaemonize(char*); extern void _threadpexit(void); extern void _threaddaemonize(void); +extern void *_threadstkalloc(int); +extern void _threadstkfree(void*, int); + +#define USPALIGN(ucp, align) \ + (void*)((((uintptr)(ucp)->uc_stack.ss_sp+(ucp)->uc_stack.ss_size)-(align))&~((align)-1)) diff --git a/src/libthread/OpenBSD-x86_64.c b/src/libthread/x86_64-ucontext.c index 27931456..5d1aaefc 100644 --- a/src/libthread/OpenBSD-x86_64.c +++ b/src/libthread/x86_64-ucontext.c @@ -6,16 +6,14 @@ makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) uintptr *sp; va_list arg; -//fprint(2, "makecontext %d\n", argc); if(argc != 2) sysfatal("libthread: makecontext misused"); va_start(arg, argc); uc->mc.di = va_arg(arg, uint); uc->mc.si = va_arg(arg, uint); -//fprint(2, "%ux %ux\n", uc->mc.di, uc->mc.si); va_end(arg); - sp = (uintptr*)((char*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size); + sp = USPALIGN(uc, 16); *--sp = 0; // fn's return address *--sp = (uintptr)fn; // return address of setcontext uc->mc.sp = (uintptr)sp; |