New thread library

This commit is contained in:
rsc 2004-12-25 21:56:33 +00:00
parent 7788fd5409
commit 1544f90960
70 changed files with 1041 additions and 4684 deletions

View file

@ -1,62 +0,0 @@
#if defined(__linux__)
#include "ucontext.c"
#else
#include "threadimpl.h"
/*
* To use this you need some patches to Valgrind that
* let it help out with detecting stack overflow.
*/
#ifdef USEVALGRIND
#include <valgrind/memcheck.h>
#endif
static void
launcher386(void (*f)(void *arg), void *arg)
{
Proc *p;
Thread *t;
p = _threadgetproc();
t = p->thread;
_threadstacklimit(t->stk, t->stk+t->stksize);
(*f)(arg);
threadexits(nil);
}
void
_threadinitstack(Thread *t, void (*f)(void*), void *arg)
{
ulong *tos;
tos = (ulong*)&t->stk[t->stksize&~7];
*--tos = (ulong)arg;
*--tos = (ulong)f;
t->sched.pc = (ulong)launcher386;
t->sched.sp = (ulong)tos - 8; /* old PC and new PC */
}
void
_threadinswitch(int enter)
{
USED(enter);
#ifdef USEVALGRIND
if(enter)
VALGRIND_SET_STACK_LIMIT(0, 0, 0);
else
VALGRIND_SET_STACK_LIMIT(0, 0, 1);
#endif
}
void
_threadstacklimit(void *bottom, void *top)
{
USED(bottom);
USED(top);
#ifdef USEVALGRIND
VALGRIND_SET_STACK_LIMIT(1, bottom, top);
#endif
}
#endif

View file

@ -1,18 +0,0 @@
.globl _xinc
_xinc:
movl 4(%esp), %eax
lock incl 0(%eax)
ret
.globl _xdec
_xdec:
movl 4(%esp), %eax
lock decl 0(%eax)
jz iszero
movl %eax, 1
ret
iszero:
movl %eax, 0
ret

View file

@ -1,185 +0,0 @@
#include <u.h>
#include <errno.h>
#include <sched.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include "threadimpl.h"
#define procid() getpid()
#define procexited(id) (kill((id), 0) < 0 && errno==ESRCH)
static int multi;
static Proc *theproc;
/*
* Run all execs forked from a special exec proc.
*/
#include "execproc.ch"
/*
* Use _exits to exit one proc, and signals to tear everyone else down.
*/
#include "exit-getpid.ch"
/*
* Use table for _threadmultiproc, _threadsetproc, _threadgetproc.
*/
#include "proctab.ch"
/*
* Use per-process stack allocation code.
*/
#include "procstack.ch"
/*
* Implement _threadstartproc using clone.
*
* Cannot use this on newer kernels (2.6+) because
* on those kernels clone allows you to set up a per-thread
* segment using %gs, and the C library and compilers
* assume that you've done this. I don't want to learn
* how to do this (the code below is scary enough),
* so on those more recent kernels we use nptl (the
* pthreads implementation), which is finally good enough.
*/
/*
* Trampoline to run f(arg).
*/
static int
tramp(void *v)
{
void (*fn)(void*), *arg;
void **v2;
void *p;
v2 = v;
fn = v2[0];
arg = v2[1];
p = v2[2];
free(v2);
fn(arg);
abort(); /* not reached! */
return 0;
}
/*
* Trampnowait runs in the child, and starts a granchild to run f(arg).
* When trampnowait proc exits, the connection between the
* grandchild running f(arg) and the parent/grandparent is lost, so the
* grandparent need not worry about cleaning up a zombie
* when the grandchild finally exits.
*/
static int
trampnowait(void *v)
{
int pid;
int cloneflag;
void **v2;
int *pidp;
void *p;
v2 = v;
cloneflag = (int)v2[4];
pidp = v2[3];
p = v2[2];
pid = clone(tramp, p+fforkstacksize-512, cloneflag, v);
*pidp = pid;
_exit(0);
return 0;
}
static int
ffork(int flags, void (*fn)(void*), void *arg)
{
void **v;
char *p;
int cloneflag, pid, thepid, status, nowait;
p = mallocstack();
v = malloc(sizeof(void*)*5);
if(p==nil || v==nil){
freestack(p);
free(v);
return -1;
}
cloneflag = 0;
flags &= ~RFPROC;
if(flags&RFMEM){
cloneflag |= CLONE_VM;
flags &= ~RFMEM;
}
if(!(flags&RFFDG))
cloneflag |= CLONE_FILES;
else
flags &= ~RFFDG;
nowait = flags&RFNOWAIT;
// if(!(flags&RFNOWAIT))
// cloneflag |= SIGCHLD;
// else
flags &= ~RFNOWAIT;
if(flags){
fprint(2, "unknown rfork flags %x\n", flags);
freestack(p);
free(v);
return -1;
}
v[0] = fn;
v[1] = arg;
v[2] = p;
v[3] = &thepid;
v[4] = (void*)cloneflag;
thepid = -1;
pid = clone(nowait ? trampnowait : tramp, p+fforkstacksize-16, cloneflag, v);
if(pid > 0 && nowait){
if(wait4(pid, &status, __WALL, 0) < 0)
fprint(2, "ffork wait4: %r\n");
}else
thepid = pid;
if(thepid == -1)
freestack(p);
else{
((Stack*)p)->pid = thepid;
}
return thepid;
}
/*
* Called to start a new proc.
*/
void
_threadstartproc(Proc *p)
{
int pid;
Proc *np;
np = p->newproc;
pid = ffork(RFPROC|RFMEM|RFNOWAIT, _threadscheduler, np);
if(pid == -1)
sysfatal("starting new proc: %r");
np->pid = pid;
}
/*
* Called to associate p with the current pthread.
*/
void
_threadinitproc(Proc *p)
{
sigset_t mask;
p->pid = getpid();
sigemptyset(&mask);
sigaddset(&mask, WAITSIG);
sigprocmask(SIG_BLOCK, &mask, nil);
_threadsetproc(p);
}
/*
* Called from mainlauncher before threadmain.
*/
void
_threadmaininit(void)
{
}

View file

@ -1,19 +0,0 @@
/*
* The authors of this software are Russ Cox, Sape Mullender, and Rob Pike.
* Copyright (c) 2003 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
This is a Unix port of the Plan 9 thread library.
Please send comments about the packaging
to Russ Cox <rsc@post.harvard.edu>.

View file

@ -1,39 +0,0 @@
#include "threadimpl.h"
static void
launcherpower(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7,
void (*f)(void *arg), void *arg)
{
(*f)(arg);
threadexits(nil);
}
void
_threadinitstack(Thread *t, void (*f)(void*), void *arg)
{
ulong *tos, *stk;
tos = (ulong*)&t->stk[t->stksize&~7];
stk = tos;
--stk;
--stk;
--stk;
--stk;
*--stk = (ulong)arg;
*--stk = (ulong)f;
t->sched.pc = (ulong)launcherpower+LABELDPC;
t->sched.sp = (ulong)tos-80;
}
void
_threadinswitch(int enter)
{
USED(enter);
}
void
_threadstacklimit(void *addr, void *addr2)
{
USED(addr);
}

View file

@ -1,19 +0,0 @@
/*
* The authors of this software are Russ Cox, Sape Mullender, and Rob Pike.
* Copyright (c) 2003 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
This is a Unix port of the Plan 9 thread library.
Please send comments about the packaging
to Russ Cox <rsc@post.harvard.edu>.

View file

@ -1,80 +0,0 @@
/* get FPR and VR use flags with sc 0x7FF3 */
/* get vsave with mfspr reg, 256 */
.text
.align 2
.globl __setlabel
__setlabel: /* 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)
stw r13, (0+6)*4(r3) /* callee-save GPRs */
stw r14, (1+6)*4(r3) /* xxx: block move */
stw r15, (2+6)*4(r3)
stw r16, (3+6)*4(r3)
stw r17, (4+6)*4(r3)
stw r18, (5+6)*4(r3)
stw r19, (6+6)*4(r3)
stw r20, (7+6)*4(r3)
stw r21, (8+6)*4(r3)
stw r22, (9+6)*4(r3)
stw r23, (10+6)*4(r3)
stw r24, (11+6)*4(r3)
stw r25, (12+6)*4(r3)
stw r26, (13+6)*4(r3)
stw r27, (14+6)*4(r3)
stw r28, (15+6)*4(r3)
stw r29, (16+6)*4(r3)
stw r30, (17+6)*4(r3)
stw r31, (18+6)*4(r3)
li r3, 0 /* return */
blr
.globl __gotolabel
__gotolabel:
lwz r13, (0+6)*4(r3) /* callee-save GPRs */
lwz r14, (1+6)*4(r3) /* xxx: block move */
lwz r15, (2+6)*4(r3)
lwz r16, (3+6)*4(r3)
lwz r17, (4+6)*4(r3)
lwz r18, (5+6)*4(r3)
lwz r19, (6+6)*4(r3)
lwz r20, (7+6)*4(r3)
lwz r21, (8+6)*4(r3)
lwz r22, (9+6)*4(r3)
lwz r23, (10+6)*4(r3)
lwz r24, (11+6)*4(r3)
lwz r25, (12+6)*4(r3)
lwz r26, (13+6)*4(r3)
lwz r27, (14+6)*4(r3)
lwz r28, (15+6)*4(r3)
lwz r29, (16+6)*4(r3)
lwz r30, (17+6)*4(r3)
lwz r31, (18+6)*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
li r3, 1
blr

View file

@ -1,55 +0,0 @@
.globl _setlabel
.type _setlabel,@function
_setlabel:
movl 4(%esp), %eax
movl 0(%esp), %edx
movl %edx, 0(%eax)
movl %ebx, 4(%eax)
movl %esp, 8(%eax)
movl %ebp, 12(%eax)
movl %esi, 16(%eax)
movl %edi, 20(%eax)
xorl %eax, %eax
ret
.globl _gotolabel
.type _gotolabel,@function
_gotolabel:
pushl $1
call _threadinswitch
popl %eax
movl 4(%esp), %edx
movl 0(%edx), %ecx
movl 4(%edx), %ebx
movl 8(%edx), %esp
movl 12(%edx), %ebp
movl 16(%edx), %esi
movl 20(%edx), %edi
movl %ecx, 0(%esp)
pushl $0
call _threadinswitch
popl %eax
xorl %eax, %eax
incl %eax
ret
# .globl _xinc
# _xinc:
# movl 4(%esp), %eax
# lock incl 0(%eax)
# ret
#
# .globl _xdec
# _xdec:
# movl 4(%esp), %eax
# lock decl 0(%eax)
# jz iszero
# movl $1, %eax
# ret
# iszero:
# movl $0, %eax
# ret
#

View file

@ -1 +0,0 @@
.include "asm-FreeBSD-386.s"

View file

@ -1,80 +0,0 @@
/* get FPR and VR use flags with sc 0x7FF3 */
/* get vsave with mfspr reg, 256 */
.text
.align 2
.globl _setlabel
_setlabel: /* xxx: instruction scheduling */
mflr 0
mfcr 5
mfctr 6
mfxer 7
stw 0, 0*4(3)
stw 5, 1*4(3)
stw 6, 2*4(3)
stw 7, 3*4(3)
stw 1, 4*4(3)
stw 2, 5*4(3)
stw 13, (0+6)*4(3) /* callee-save GPRs */
stw 14, (1+6)*4(3) /* xxx: block move */
stw 15, (2+6)*4(3)
stw 16, (3+6)*4(3)
stw 17, (4+6)*4(3)
stw 18, (5+6)*4(3)
stw 19, (6+6)*4(3)
stw 20, (7+6)*4(3)
stw 21, (8+6)*4(3)
stw 22, (9+6)*4(3)
stw 23, (10+6)*4(3)
stw 24, (11+6)*4(3)
stw 25, (12+6)*4(3)
stw 26, (13+6)*4(3)
stw 27, (14+6)*4(3)
stw 28, (15+6)*4(3)
stw 29, (16+6)*4(3)
stw 30, (17+6)*4(3)
stw 31, (18+6)*4(3)
li 3, 0 /* return */
blr
.globl _gotolabel
_gotolabel:
lwz 13, (0+6)*4(3) /* callee-save GPRs */
lwz 14, (1+6)*4(3) /* xxx: block move */
lwz 15, (2+6)*4(3)
lwz 16, (3+6)*4(3)
lwz 17, (4+6)*4(3)
lwz 18, (5+6)*4(3)
lwz 19, (6+6)*4(3)
lwz 20, (7+6)*4(3)
lwz 21, (8+6)*4(3)
lwz 22, (9+6)*4(3)
lwz 23, (10+6)*4(3)
lwz 24, (11+6)*4(3)
lwz 25, (12+6)*4(3)
lwz 26, (13+6)*4(3)
lwz 27, (14+6)*4(3)
lwz 28, (15+6)*4(3)
lwz 29, (16+6)*4(3)
lwz 30, (17+6)*4(3)
lwz 31, (18+6)*4(3)
lwz 1, 4*4(3)
lwz 2, 5*4(3)
lwz 0, 0*4(3)
mtlr 0
lwz 0, 1*4(3)
mtcr 0 /* mtcrf 0xFF, r0 */
lwz 0, 2*4(3)
mtctr 0
lwz 0, 3*4(3)
mtxer 0
li 3, 1
blr

View file

@ -1,49 +0,0 @@
.globl _setlabel
.type _setlabel,@function
_setlabel:
movl 4(%esp), %eax
movl 0(%esp), %edx
movl %edx, 0(%eax)
movl %ebx, 4(%eax)
movl %esp, 8(%eax)
movl %ebp, 12(%eax)
movl %esi, 16(%eax)
movl %edi, 20(%eax)
xorl %eax, %eax
ret
.globl _gotolabel
.type _gotolabel,@function
_gotolabel:
movl 4(%esp), %edx
movl 0(%edx), %ecx
movl 4(%edx), %ebx
movl 8(%edx), %esp
movl 12(%edx), %ebp
movl 16(%edx), %esi
movl 20(%edx), %edi
xorl %eax, %eax
incl %eax
movl %ecx, 0(%esp)
ret
# .globl _xinc
# _xinc:
# movl 4(%esp), %eax
# lock incl 0(%eax)
# ret
#
# .globl _xdec
# _xdec:
# movl 4(%esp), %eax
# lock decl 0(%eax)
# jz iszero
# movl $1, %eax
# ret
# iszero:
# movl $0, %eax
# ret
#

View file

@ -1,51 +0,0 @@
.globl _setlabel
.type _setlabel,#function
_setlabel:
ta 3
st %i0, [%o0]
st %i1, [%o0+4]
st %i2, [%o0+8]
st %i3, [%o0+12]
st %i4, [%o0+16]
st %i5, [%o0+20]
st %i6, [%o0+24]
st %i7, [%o0+28]
st %l0, [%o0+32]
st %l1, [%o0+36]
st %l2, [%o0+40]
st %l3, [%o0+44]
st %l4, [%o0+48]
st %l5, [%o0+52]
st %l6, [%o0+56]
st %l7, [%o0+60]
st %sp, [%o0+64]
st %o7, [%o0+68]
jmpl %o7+8, %g0
or %g0, %g0, %o0
.globl _gotolabel
.type _gotolabel,#function
_gotolabel:
ta 3
ld [%o0], %i0
ld [%o0+4], %i1
ld [%o0+8], %i2
ld [%o0+12], %i3
ld [%o0+16], %i4
ld [%o0+20], %i5
ld [%o0+24], %i6
ld [%o0+28], %i7
ld [%o0+32], %l0
ld [%o0+36], %l1
ld [%o0+40], %l2
ld [%o0+44], %l3
ld [%o0+48], %l4
ld [%o0+52], %l5
ld [%o0+56], %l6
ld [%o0+60], %l7
ld [%o0+64], %sp
ld [%o0+68], %o7
jmpl %o7+8, %g0
or %g0, 1, %o0

View file

@ -1,500 +0,0 @@
#include "threadimpl.h"
static Lock chanlock; /* central channel access lock */
static void enqueue(Alt*, Thread*);
static void dequeue(Alt*);
static int altexec(Alt*);
int _threadhighnentry;
int _threadnalt;
static void
setuserpc(ulong pc)
{
Thread *t;
t = _threadgetproc()->thread;
if(t)
t->userpc = pc;
}
static int
canexec(Alt *a)
{
int i, otherop;
Channel *c;
c = a->c;
/* are there senders or receivers blocked? */
otherop = (CHANSND+CHANRCV) - a->op;
for(i=0; i<c->nentry; i++)
if(c->qentry[i] && c->qentry[i]->op==otherop && c->qentry[i]->thread->altc==nil){
_threaddebug(DBGCHAN, "can rendez alt %p chan %p", a, c);
return 1;
}
/* is there room in the channel? */
if((a->op==CHANSND && c->n < c->s)
|| (a->op==CHANRCV && c->n > 0)){
_threaddebug(DBGCHAN, "can buffer alt %p chan %p", a, c);
return 1;
}
return 0;
}
static void
_chanfree(Channel *c)
{
int i, inuse;
inuse = 0;
for(i = 0; i < c->nentry; i++)
if(c->qentry[i])
inuse = 1;
if(inuse)
c->freed = 1;
else{
if(c->qentry)
free(c->qentry);
free(c);
}
}
void
chanfree(Channel *c)
{
lock(&chanlock);
_chanfree(c);
unlock(&chanlock);
}
int
chaninit(Channel *c, int elemsize, int elemcnt)
{
if(elemcnt < 0 || elemsize <= 0 || c == nil)
return -1;
memset(c, 0, sizeof *c);
c->e = elemsize;
c->s = elemcnt;
_threaddebug(DBGCHAN, "chaninit %p", c);
return 1;
}
Channel*
chancreate(int elemsize, int elemcnt)
{
Channel *c;
if(elemcnt < 0 || elemsize <= 0)
return nil;
c = _threadmalloc(sizeof(Channel)+elemsize*elemcnt, 1);
c->e = elemsize;
c->s = elemcnt;
_threaddebug(DBGCHAN, "chancreate %p", c);
return c;
}
static int
_alt(Alt *alts)
{
Alt *a, *xa;
Channel *c;
int n;
Thread *t;
/*
* The point of going splhi here is that note handlers
* might reasonably want to use channel operations,
* but that will hang if the note comes while we hold the
* chanlock. Instead, we delay the note until we've dropped
* the lock.
*/
/*
* T might be nil here -- the scheduler sends on threadwaitchan
* directly (in non-blocking mode, of course!).
*/
t = _threadgetproc()->thread;
if((t && t->moribund) || _threadexitsallstatus)
yield(); /* won't return */
lock(&chanlock);
/* test whether any channels can proceed */
n = 0;
a = nil;
for(xa=alts; xa->op!=CHANEND && xa->op!=CHANNOBLK; xa++){
xa->entryno = -1;
if(xa->op == CHANNOP)
continue;
c = xa->c;
if(c==nil){
unlock(&chanlock);
return -1;
}
if(canexec(xa))
if(nrand(++n) == 0)
a = xa;
}
if(a==nil){
/* nothing can proceed */
if(xa->op == CHANNOBLK){
unlock(&chanlock);
_threadnalt++;
return xa - alts;
}
/* enqueue on all channels. */
t->altc = nil;
for(xa=alts; xa->op!=CHANEND; xa++){
if(xa->op==CHANNOP)
continue;
enqueue(xa, t);
}
/*
* wait for successful rendezvous.
* we can't just give up if the rendezvous
* is interrupted -- someone else might come
* along and try to rendezvous with us, so
* we need to be here.
*
* actually, now we're assuming no interrupts.
*/
/*Again:*/
t->alt = alts;
t->chan = Chanalt;
t->altrend.l = &chanlock;
_threadsleep(&t->altrend);
/* dequeue from channels, find selected one */
a = nil;
c = t->altc;
for(xa=alts; xa->op!=CHANEND; xa++){
if(xa->op==CHANNOP)
continue;
if(xa->c == c)
a = xa;
dequeue(xa);
}
unlock(&chanlock);
if(a == nil){ /* we were interrupted */
assert(c==(Channel*)~0);
return -1;
}
}else{
altexec(a); /* unlocks chanlock, does splx */
}
if(t)
t->chan = Channone;
return a - alts;
}
int
alt(Alt *alts)
{
setuserpc(getcallerpc(&alts));
return _alt(alts);
}
static int
runop(int op, Channel *c, void *v, int nb)
{
int r;
Alt a[2];
/*
* we could do this without calling alt,
* but the only reason would be performance,
* and i'm not convinced it matters.
*/
a[0].op = op;
a[0].c = c;
a[0].v = v;
a[1].op = CHANEND;
if(nb)
a[1].op = CHANNOBLK;
switch(r=_alt(a)){
case -1: /* interrupted */
return -1;
case 1: /* nonblocking, didn't accomplish anything */
assert(nb);
return 0;
case 0:
return 1;
default:
fprint(2, "ERROR: channel alt returned %d\n", r);
abort();
return -1;
}
}
int
recv(Channel *c, void *v)
{
setuserpc(getcallerpc(&c));
return runop(CHANRCV, c, v, 0);
}
int
nbrecv(Channel *c, void *v)
{
setuserpc(getcallerpc(&c));
return runop(CHANRCV, c, v, 1);
}
int
send(Channel *c, void *v)
{
setuserpc(getcallerpc(&c));
return runop(CHANSND, c, v, 0);
}
int
nbsend(Channel *c, void *v)
{
setuserpc(getcallerpc(&c));
return runop(CHANSND, c, v, 1);
}
static void
channelsize(Channel *c, int sz)
{
if(c->e != sz){
fprint(2, "expected channel with elements of size %d, got size %d\n",
sz, c->e);
abort();
}
}
int
sendul(Channel *c, ulong v)
{
setuserpc(getcallerpc(&c));
channelsize(c, sizeof(ulong));
return send(c, &v);
}
ulong
recvul(Channel *c)
{
ulong v;
setuserpc(getcallerpc(&c));
channelsize(c, sizeof(ulong));
if(runop(CHANRCV, c, &v, 0) < 0)
return ~0;
return v;
}
int
sendp(Channel *c, void *v)
{
setuserpc(getcallerpc(&c));
channelsize(c, sizeof(void*));
return runop(CHANSND, c, &v, 0);
}
void*
recvp(Channel *c)
{
void *v;
setuserpc(getcallerpc(&c));
channelsize(c, sizeof(void*));
if(runop(CHANRCV, c, &v, 0) < 0)
return nil;
return v;
}
int
nbsendul(Channel *c, ulong v)
{
setuserpc(getcallerpc(&c));
channelsize(c, sizeof(ulong));
return runop(CHANSND, c, &v, 1);
}
ulong
nbrecvul(Channel *c)
{
ulong v;
setuserpc(getcallerpc(&c));
channelsize(c, sizeof(ulong));
if(runop(CHANRCV, c, &v, 1) == 0)
return 0;
return v;
}
int
nbsendp(Channel *c, void *v)
{
setuserpc(getcallerpc(&c));
channelsize(c, sizeof(void*));
return runop(CHANSND, c, &v, 1);
}
void*
nbrecvp(Channel *c)
{
void *v;
setuserpc(getcallerpc(&c));
channelsize(c, sizeof(void*));
if(runop(CHANRCV, c, &v, 1) == 0)
return nil;
return v;
}
static int
emptyentry(Channel *c)
{
int i, extra;
assert((c->nentry==0 && c->qentry==nil) || (c->nentry && c->qentry));
for(i=0; i<c->nentry; i++)
if(c->qentry[i]==nil)
return i;
extra = 16;
c->nentry += extra;
if(c->nentry > _threadhighnentry) _threadhighnentry = c->nentry;
c->qentry = realloc((void*)c->qentry, c->nentry*sizeof(c->qentry[0]));
if(c->qentry == nil)
sysfatal("realloc channel entries: %r");
_threadmemset(&c->qentry[i], 0, extra*sizeof(c->qentry[0]));
return i;
}
static void
enqueue(Alt *a, Thread *t)
{
int i;
_threaddebug(DBGCHAN, "Queueing alt %p on channel %p", a, a->c);
a->thread = t;
i = emptyentry(a->c);
a->c->qentry[i] = a;
}
static void
dequeue(Alt *a)
{
int i;
Channel *c;
c = a->c;
for(i=0; i<c->nentry; i++)
if(c->qentry[i]==a){
_threaddebug(DBGCHAN, "Dequeuing alt %p from channel %p", a, a->c);
c->qentry[i] = nil;
if(c->freed)
_chanfree(c);
return;
}
}
static void*
altexecbuffered(Alt *a, int willreplace)
{
uchar *v;
Channel *c;
c = a->c;
/* use buffered channel queue */
if(a->op==CHANRCV && c->n > 0){
_threaddebug(DBGCHAN, "buffer recv alt %p chan %p", a, c);
v = c->v + c->e*(c->f%c->s);
if(!willreplace)
c->n--;
c->f++;
return v;
}
if(a->op==CHANSND && c->n < c->s){
_threaddebug(DBGCHAN, "buffer send alt %p chan %p", a, c);
v = c->v + c->e*((c->f+c->n)%c->s);
if(!willreplace)
c->n++;
return v;
}
abort();
return nil;
}
static void
altcopy(void *dst, void *src, int sz)
{
if(dst){
if(src)
memmove(dst, src, sz);
else
_threadmemset(dst, 0, sz);
}
}
static int
altexec(Alt *a)
{
volatile Alt *b;
int i, n, otherop;
Channel *c;
void *me, *waiter, *buf;
c = a->c;
/* rendezvous with others */
otherop = (CHANSND+CHANRCV) - a->op;
n = 0;
b = nil;
me = a->v;
for(i=0; i<c->nentry; i++)
if(c->qentry[i] && c->qentry[i]->op==otherop && c->qentry[i]->thread->altc==nil)
if(nrand(++n) == 0)
b = c->qentry[i];
if(b != nil){
_threaddebug(DBGCHAN, "rendez %s alt %p chan %p alt %p", a->op==CHANRCV?"recv":"send", a, c, b);
waiter = b->v;
if(c->s && c->n){
/*
* if buffer is full and there are waiters
* and we're meeting a waiter,
* we must be receiving.
*
* we use the value in the channel buffer,
* copy the waiter's value into the channel buffer
* on behalf of the waiter, and then wake the waiter.
*/
if(a->op!=CHANRCV)
abort();
buf = altexecbuffered(a, 1);
altcopy(me, buf, c->e);
altcopy(buf, waiter, c->e);
}else{
if(a->op==CHANRCV)
altcopy(me, waiter, c->e);
else
altcopy(waiter, me, c->e);
}
b->thread->altc = c;
_threadwakeup(&b->thread->altrend);
_threaddebug(DBGCHAN, "chanlock is %lud", *(ulong*)(void*)&chanlock);
_threaddebug(DBGCHAN, "unlocking the chanlock");
unlock(&chanlock);
return 1;
}
buf = altexecbuffered(a, 0);
if(a->op==CHANRCV)
altcopy(me, buf, c->e);
else
altcopy(buf, me, c->e);
unlock(&chanlock);
return 1;
}

View file

@ -1,20 +0,0 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
int
chanprint(Channel *c, char *fmt, ...)
{
va_list arg;
char *p;
int n;
va_start(arg, fmt);
p = vsmprint(fmt, arg);
va_end(arg);
if(p == nil)
sysfatal("vsmprint failed: %r");
n = sendp(c, p);
yield(); /* let recipient handle message immediately */
return n;
}

View file

@ -1,211 +0,0 @@
#include "threadimpl.h"
Pqueue _threadpq; /* list of all procs */
int _threadnprocs; /* count of procs */
static int newthreadid(void);
static int newprocid(void);
/*
* Create and initialize a new Thread structure attached to a given proc.
*/
int
_newthread(Proc *p, void (*f)(void *arg), void *arg, uint stacksize,
char *name, int grp)
{
int id;
Thread *t;
t = _threadmalloc(sizeof(Thread), 1);
t->proc = p;
t->nextproc = p;
t->homeproc = p;
t->grp = grp;
t->id = id = newthreadid();
if(name)
t->name = strdup(name);
_threaddebug(DBGSCHED, "create thread %d.%d name %s", p->id, id, name);
/*
* Allocate and clear stack.
*/
if(stacksize < 1024)
sysfatal("bad stacksize %d", stacksize);
t->stk = _threadmalloc(stacksize, 0);
t->stksize = stacksize;
_threaddebugmemset(t->stk, 0xFE, stacksize);
/*
* Set up t->context to call f(arg).
*/
_threadinitstack(t, f, arg);
/*
* Add thread to proc.
*/
lock(&p->lock);
_procaddthread(p, t);
/*
* Mark thread as ready to run.
*/
t->state = Ready;
_threadready(t);
unlock(&p->lock);
return id;
}
/*
* Free a Thread structure.
*/
void
_threadfree(Thread *t)
{
free(t->stk);
free(t->name);
free(t);
}
/*
* Create and initialize a new Proc structure with a single Thread
* running inside it. Add the Proc to the global process list.
*/
Proc*
_newproc(void)
{
Proc *p;
/*
* Allocate.
*/
p = _threadmalloc(sizeof *p, 1);
p->id = newprocid();
/*
* Add to list. Record if we're now multiprocess.
*/
lock(&_threadpq.lock);
if(_threadpq.head == nil)
_threadpq.head = p;
else
*_threadpq.tail = p;
_threadpq.tail = &p->next;
if(_threadnprocs == 1)
_threadmultiproc();
_threadnprocs++;
unlock(&_threadpq.lock);
return p;
}
/*
* Allocate a new thread running f(arg) on a stack of size stacksize.
* Return the thread id. The thread group inherits from the current thread.
*/
int
threadcreate(void (*f)(void*), void *arg, uint stacksize)
{
return _newthread(_threadgetproc(), f, arg, stacksize, nil, threadgetgrp());
}
/*
* Allocate a new idle thread. Only allowed in a single-proc program.
*/
int
threadcreateidle(void (*f)(void *arg), void *arg, uint stacksize)
{
int id;
assert(_threadpq.head->next == nil); /* only 1 */
id = threadcreate(f, arg, stacksize);
_threaddebug(DBGSCHED, "idle is %d", id);
_threadsetidle(id);
return id;
}
/*
* Threadcreate, but do it inside a fresh proc.
*/
int
proccreate(void (*f)(void*), void *arg, uint stacksize)
{
int id;
Proc *p, *np;
p = _threadgetproc();
np = _newproc();
p->newproc = np;
p->schedfn = _kthreadstartproc;
id = _newthread(np, f, arg, stacksize, nil, p->thread->grp);
_sched(); /* call into scheduler to create proc XXX */
return id;
}
/*
* Allocate a new thread id.
*/
static int
newthreadid(void)
{
static Lock l;
static int id;
int i;
lock(&l);
i = ++id;
unlock(&l);
return i;
}
/*
* Allocate a new proc id.
*/
static int
newprocid(void)
{
static Lock l;
static int id;
int i;
lock(&l);
i = ++id;
unlock(&l);
return i;
}
/*
* Add thread to proc's list.
*/
void
_procaddthread(Proc *p, Thread *t)
{
p->nthreads++;
if(p->threads.head == nil)
p->threads.head = t;
else{
t->prevt = p->threads.tail;
t->prevt->nextt = t;
}
p->threads.tail = t;
t->next = (Thread*)~0;
}
/*
* Remove thread from proc's list.
*/
void
_procdelthread(Proc *p, Thread *t)
{
if(t->prevt)
t->prevt->nextt = t->nextt;
else
p->threads.head = t->nextt;
if(t->nextt)
t->nextt->prevt = t->prevt;
else
p->threads.tail = t->prevt;
p->nthreads--;
}

View file

@ -1,48 +0,0 @@
#include "threadimpl.h"
int _threaddebuglevel;
void
__threaddebug(ulong flag, char *fmt, ...)
{
char buf[128];
va_list arg;
Fmt f;
Proc *p;
if((_threaddebuglevel&flag) == 0)
return;
fmtfdinit(&f, 2, buf, sizeof buf);
p = _threadgetproc();
if(p==nil)
fmtprint(&f, "noproc ");
else if(p->thread)
fmtprint(&f, "%d.%d ", p->id, p->thread->id);
else
fmtprint(&f, "%d._ ", p->id);
va_start(arg, fmt);
fmtvprint(&f, fmt, arg);
va_end(arg);
fmtprint(&f, "\n");
fmtfdflush(&f);
}
void
_threadassert(char *s)
{
char buf[256];
int n;
Proc *p;
p = _threadgetproc();
if(p && p->thread)
n = sprint(buf, "%d.%d ", p->pid, p->thread->id);
else
n = 0;
snprint(buf+n, sizeof(buf)-n, "%s: assertion failed\n", s);
write(2, buf, strlen(buf));
abort();
}

View file

@ -1,147 +0,0 @@
#include <u.h>
#include <fcntl.h>
#include <unistd.h>
#include "threadimpl.h"
static void efork(int[3], int[2], char*, char**);
int
_threadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs)
{
int pfd[2];
int n, pid;
char exitstr[ERRMAX];
_threaddebug(DBGEXEC, "threadexec %s", prog);
/*
* We want threadexec to behave like exec; if exec succeeds,
* never return, and if it fails, return with errstr set.
* Unfortunately, the exec happens in another proc since
* we have to wait for the exec'ed process to finish.
* To provide the semantics, we open a pipe with the
* write end close-on-exec and hand it to the proc that
* is doing the exec. If the exec succeeds, the pipe will
* close so that our read below fails. If the exec fails,
* then the proc doing the exec sends the errstr down the
* pipe to us.
*/
if(pipe(pfd) < 0)
goto Bad;
if(fcntl(pfd[0], F_SETFD, 1) < 0)
goto Bad;
if(fcntl(pfd[1], F_SETFD, 1) < 0)
goto Bad;
switch(pid = fork()){
case -1:
close(pfd[0]);
close(pfd[1]);
goto Bad;
case 0:
efork(fd, pfd, prog, args);
_threaddebug(DBGSCHED, "exit after efork");
_exit(0);
default:
if(freeargs)
free(args);
break;
}
close(pfd[1]);
if((n = read(pfd[0], exitstr, ERRMAX-1)) > 0){ /* exec failed */
exitstr[n] = '\0';
errstr(exitstr, ERRMAX);
close(pfd[0]);
goto Bad;
}
close(pfd[0]);
close(fd[0]);
if(fd[1] != fd[0])
close(fd[1]);
if(fd[2] != fd[1] && fd[2] != fd[0])
close(fd[2]);
if(pidc)
sendul(pidc, pid);
_threaddebug(DBGEXEC, "threadexec schedexecwait");
return pid;
Bad:
_threaddebug(DBGEXEC, "threadexec bad %r");
if(pidc)
sendul(pidc, ~0);
return -1;
}
void
threadexec(Channel *pidc, int fd[3], char *prog, char *args[])
{
if(_kthreadexec(pidc, fd, prog, args, 0) >= 0)
threadexits(nil);
}
int
threadspawn(int fd[3], char *prog, char *args[])
{
return _kthreadexec(nil, fd, prog, args, 0);
}
/*
* The &f+1 trick doesn't work on SunOS, so we might
* as well bite the bullet and do this correctly.
*/
void
threadexecl(Channel *pidc, int fd[3], char *f, ...)
{
char **args, *s;
int n;
va_list arg;
va_start(arg, f);
for(n=0; va_arg(arg, char*) != 0; n++)
;
n++;
va_end(arg);
args = malloc(n*sizeof(args[0]));
if(args == nil){
if(pidc)
sendul(pidc, ~0);
return;
}
va_start(arg, f);
for(n=0; (s=va_arg(arg, char*)) != 0; n++)
args[n] = s;
args[n] = 0;
va_end(arg);
if(_kthreadexec(pidc, fd, f, args, 1) >= 0)
threadexits(nil);
}
static void
efork(int stdfd[3], int fd[2], char *prog, char **args)
{
char buf[ERRMAX];
int i;
_threaddebug(DBGEXEC, "_schedexec %s -- calling execv", prog);
dup(stdfd[0], 0);
dup(stdfd[1], 1);
dup(stdfd[2], 2);
for(i=3; i<40; i++)
if(i != fd[1])
close(i);
rfork(RFNOTEG);
execvp(prog, args);
_threaddebug(DBGEXEC, "_schedexec failed: %r");
rerrstr(buf, sizeof buf);
if(buf[0]=='\0')
strcpy(buf, "exec failed");
write(fd[1], buf, strlen(buf));
close(fd[1]);
_threaddebug(DBGSCHED, "_exits in exec-unix");
_exits(buf);
}

153
src/libthread/exec.c Normal file
View file

@ -0,0 +1,153 @@
#include "u.h"
#include <errno.h>
#include "libc.h"
#include "thread.h"
#include "threadimpl.h"
static Lock thewaitlock;
static Channel *thewaitchan;
static Channel *dowaitchan;
/* BUG - start waitproc on first exec, not when threadwaitchan is called */
static void
waitproc(void *v)
{
Channel *c;
Waitmsg *w;
_threadsetsysproc();
for(;;){
while((w = wait()) == nil){
if(errno == ECHILD)
recvul(dowaitchan);
}
if((c = thewaitchan) != nil)
sendp(c, w);
else
free(w);
}
}
Channel*
threadwaitchan(void)
{
if(thewaitchan)
return thewaitchan;
lock(&thewaitlock);
if(thewaitchan){
unlock(&thewaitlock);
return thewaitchan;
}
thewaitchan = chancreate(sizeof(Waitmsg*), 4);
chansetname(thewaitchan, "threadwaitchan");
dowaitchan = chancreate(sizeof(ulong), 1);
chansetname(dowaitchan, "dowaitchan");
proccreate(waitproc, nil, STACK);
unlock(&thewaitlock);
return thewaitchan;
}
int
threadspawn(int fd[3], char *cmd, char *argv[])
{
int i, n, p[2], pid;
char exitstr[100];
if(pipe(p) < 0)
return -1;
if(fcntl(p[0], F_SETFD, 1) < 0 || fcntl(p[1], F_SETFD, 1) < 0){
close(p[0]);
close(p[1]);
return -1;
}
switch(pid = fork()){
case -1:
close(p[0]);
close(p[1]);
return -1;
case 0:
dup2(fd[0], 0);
dup2(fd[1], 1);
dup2(fd[2], 2);
for(i=3; i<100; i++)
if(i != p[1])
close(i);
execvp(cmd, argv);
fprint(p[1], "%d", errno);
close(p[1]);
_exit(0);
}
close(p[1]);
n = read(p[0], exitstr, sizeof exitstr-1);
close(p[0]);
if(n > 0){ /* exec failed */
exitstr[n] = 0;
errno = atoi(exitstr);
return -1;
}
close(fd[0]);
if(fd[1] != fd[0])
close(fd[1]);
if(fd[2] != fd[1] && fd[2] != fd[0])
close(fd[2]);
channbsendul(dowaitchan, 1);
return pid;
}
int
_threadexec(Channel *cpid, int fd[3], char *cmd, char *argv[])
{
int pid;
pid = threadspawn(fd, cmd, argv);
if(cpid){
if(pid < 0)
chansendul(cpid, ~0);
else
chansendul(cpid, pid);
}
return pid;
}
void
threadexec(Channel *cpid, int fd[3], char *cmd, char *argv[])
{
if(_threadexec(cpid, fd, cmd, argv) >= 0)
threadexits("threadexec");
}
void
threadexecl(Channel *cpid, int fd[3], char *cmd, ...)
{
char **argv, *s;
int n, pid;
va_list arg;
va_start(arg, cmd);
for(n=0; va_arg(arg, char*) != nil; n++)
;
n++;
va_end(arg);
argv = malloc(n*sizeof(argv[0]));
if(argv == nil){
if(cpid)
chansendul(cpid, ~0);
return;
}
va_start(arg, cmd);
for(n=0; (s=va_arg(arg, char*)) != nil; n++)
argv[n] = s;
argv[n] = 0;
va_end(arg);
pid = _threadexec(cpid, fd, cmd, argv);
free(argv);
if(pid >= 0)
threadexits("threadexecl");
}

View file

@ -1,183 +0,0 @@
/*
* Set up a dedicated proc to handle calls to exec.
* The proc also waits for child messages.
* This way, each proc scheduler need not worry
* about calling wait in its main loop.
*
* To be included from other files (e.g., Linux-clone.c).
*/
typedef struct Xarg Xarg;
struct Xarg
{
Channel *pidc;
int fd[3];
char *prog;
char **args;
int freeargs;
Channel *ret;
int errn;
char errstr[ERRMAX];
};
static Proc *_threadexecproc;
static Channel *_threadexecchan;
static Lock threadexeclock;
/*
* Called to poll for any kids of this pthread.
* We have a separate proc responsible for exec,
* so this is a no-op.
*/
void
_threadwaitkids(Proc *p)
{
}
#define WAITSIG SIGCHLD
/*
* Separate process to wait for child messages.
* Also runs signal handlers and runs all execs.
*/
static void
nop(int sig)
{
USED(sig);
}
static void
_threadwaitproc(void *v)
{
Channel *c;
Waitmsg *w;
sigset_t mask;
int ret, nkids;
Xarg *xa;
nkids = 0;
sigemptyset(&mask);
siginterrupt(WAITSIG, 1);
signal(WAITSIG, nop);
sigaddset(&mask, WAITSIG);
sigprocmask(SIG_BLOCK, &mask, nil);
USED(v);
for(;;){
while((nkids > 0 ? nbrecv : recv)(_threadexecchan, &xa) == 1){
ret = _threadexec(xa->pidc, xa->fd, xa->prog, xa->args, xa->freeargs);
if(ret > 0)
nkids++;
else{
rerrstr(xa->errstr, sizeof xa->errstr);
xa->errn = errno;
}
sendul(xa->ret, ret);
}
if(nkids > 0){
sigprocmask(SIG_UNBLOCK, &mask, nil);
w = wait();
sigprocmask(SIG_BLOCK, &mask, nil);
if(w == nil && errno == ECHILD){
fprint(2, "wait returned ECHILD but nkids=%d; reset\n", nkids);
nkids = 0;
}
if(w){
nkids--;
if((c = _threadwaitchan) != nil)
sendp(c, w);
else
free(w);
}
}
}
}
static void _kickexecproc(void);
int
_callthreadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs)
{
int ret;
Xarg xa;
if(_threadexecchan == nil){
lock(&threadexeclock);
if(_threadexecchan == nil)
_threadfirstexec();
unlock(&threadexeclock);
}
xa.pidc = pidc;
xa.fd[0] = fd[0];
xa.fd[1] = fd[1];
xa.fd[2] = fd[2];
xa.prog = prog;
xa.args = args;
xa.freeargs = freeargs;
xa.ret = chancreate(sizeof(ulong), 1);
sendp(_threadexecchan, &xa);
_kickexecproc();
ret = recvul(xa.ret);
if(ret < 0){
werrstr("%s", xa.errstr);
errno = xa.errn;
}
chanfree(xa.ret);
return ret;
}
/*
* Called before the first exec.
*/
void
_threadfirstexec(void)
{
int id;
Proc *p;
_threadexecchan = chancreate(sizeof(Xarg*), 1);
id = proccreate(_threadwaitproc, nil, 32*1024);
/*
* Sleazy: decrement threadnprocs so that
* the existence of the _threadwaitproc proc
* doesn't keep us from exiting.
*/
lock(&_threadpq.lock);
--_threadnprocs;
for(p=_threadpq.head; p; p=p->next)
if(p->threads.head && p->threads.head->id == id)
break;
if(p == nil)
sysfatal("cannot find exec proc");
unlock(&_threadpq.lock);
_threadexecproc = p;
}
/*
* Called after the thread t has been rescheduled.
* Kick the exec proc in case it is in the middle of a wait.
*/
static void
_kickexecproc(void)
{
kill(_threadexecproc->pid, WAITSIG);
}
/*
* Called before exec.
*/
void
_threadbeforeexec(void)
{
}
/*
* Called after exec.
*/
void
_threadafterexec(void)
{
}

View file

@ -1,25 +0,0 @@
/*
* Implement threadexitsall by sending a signal to every proc.
*
* To be included from another C file (e.g., Linux-clone.c).
*/
void
_threadexitallproc(char *exitstr)
{
Proc *p;
int mypid;
mypid = getpid();
lock(&_threadpq.lock);
for(p=_threadpq.head; p; p=p->next)
if(p->pid > 1 && p->pid != mypid)
kill(p->pid, SIGUSR2);
exits(exitstr);
}
void
_threadexitproc(char *exitstr)
{
_exits(exitstr);
}

View file

@ -1,44 +0,0 @@
#include <u.h>
#include <signal.h>
#include "threadimpl.h"
char *_threadexitsallstatus;
Channel *_threadwaitchan;
void
threadexits(char *exitstr)
{
Proc *p;
Thread *t;
p = _threadgetproc();
t = p->thread;
if(t == p->idle)
p->idle = nil;
t->moribund = 1;
_threaddebug(DBGSCHED, "threadexits %s", exitstr);
if(exitstr==nil)
exitstr="";
utfecpy(p->exitstr, p->exitstr+ERRMAX, exitstr);
_sched();
}
void
threadexitsall(char *exitstr)
{
_threaddebug(DBGSCHED, "threadexitsall %s", exitstr);
if(exitstr == nil)
exitstr = "";
_threadexitsallstatus = exitstr;
_threaddebug(DBGSCHED, "_threadexitsallstatus set to %p", _threadexitsallstatus);
/* leave */
_kthreadexitallproc(exitstr);
}
Channel*
threadwaitchan(void)
{
if(_threadwaitchan==nil)
_threadwaitchan = chancreate(sizeof(Waitmsg*), 16);
return _threadwaitchan;
}

View file

@ -1,241 +0,0 @@
#define NOPLAN9DEFINES
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "threadimpl.h"
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
void
fdwait()
{
fd_set rfd, wfd, efd;
FD_ZERO(&rfd);
FD_ZERO(&wfd);
FD_ZERO(&efd);
if(mode=='w')
FD_SET(&wfd, fd);
else
FD_SET(&rfd, fd);
FD_SET(&efd, fd);
select(fd+1, &rfd, &wfd, &efd, nil);
}
void
threadfdwaitsetup(void)
{
}
void
_threadfdwait(int fd, int rw, ulong pc)
{
int i;
struct {
Channel c;
ulong x;
Alt *qentry[2];
} s;
threadfdwaitsetup();
chaninit(&s.c, sizeof(ulong), 1);
s.c.qentry = (volatile Alt**)s.qentry;
s.c.nentry = 2;
memset(s.qentry, 0, sizeof s.qentry);
for(i=0; i<npoll; i++)
if(pfd[i].fd == -1)
break;
if(i==npoll){
if(npoll >= nelem(polls)){
fprint(2, "Too many polled fds.\n");
abort();
}
npoll++;
}
pfd[i].fd = fd;
pfd[i].events = rw=='r' ? POLLIN : POLLOUT;
polls[i].c = &s.c;
if(0) fprint(2, "%s [%3d] fdwait %d %c list *0x%lux\n",
argv0, threadid(), fd, rw, pc);
if(pollpid)
postnote(PNPROC, pollpid, "interrupt");
recvul(&s.c);
}
void
threadfdwait(int fd, int rw)
{
_threadfdwait(fd, rw, getcallerpc(&fd));
}
void
threadsleep(int ms)
{
struct {
Channel c;
ulong x;
Alt *qentry[2];
} s;
threadfdwaitsetup();
chaninit(&s.c, sizeof(ulong), 1);
s.c.qentry = (volatile Alt**)s.qentry;
s.c.nentry = 2;
memset(s.qentry, 0, sizeof s.qentry);
sleepchan[nsleep] = &s.c;
sleeptime[nsleep++] = p9nsec()/1000000+ms;
recvul(&s.c);
}
void
threadfdnoblock(int fd)
{
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0)|O_NONBLOCK);
}
long
threadread(int fd, void *a, long n)
{
int nn;
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 || errno == EAGAIN || errno == EWOULDBLOCK)
goto again;
}
return nn;
}
int
threadrecvfd(int fd)
{
int nn;
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 || errno == EAGAIN || errno == EWOULDBLOCK)
goto again;
}
return nn;
}
int
threadsendfd(int fd, int sfd)
{
int nn;
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 || errno == EAGAIN || errno == EWOULDBLOCK)
goto again;
}
return nn;
}
long
threadreadn(int fd, void *a, long n)
{
int tot, nn;
for(tot = 0; tot<n; tot+=nn){
nn = threadread(fd, (char*)a+tot, n-tot);
if(nn <= 0){
if(tot == 0)
return nn;
return tot;
}
}
return tot;
}
long
_threadwrite(int fd, const void *a, long n)
{
int nn;
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 || errno == EAGAIN || errno == EWOULDBLOCK)
goto again;
}
return nn;
}
long
threadwrite(int fd, const void *a, long n)
{
int tot, nn;
for(tot = 0; tot<n; tot+=nn){
nn = _threadwrite(fd, (char*)a+tot, n-tot);
if(nn <= 0){
if(tot == 0)
return nn;
return tot;
}
}
return tot;
}
int
threadannounce(char *addr, char *dir)
{
return p9announce(addr, dir);
}
int
threadlisten(char *dir, char *newdir)
{
int fd, ret;
extern int _p9netfd(char*);
fd = _p9netfd(dir);
if(fd < 0){
werrstr("bad 'directory' in listen: %s", dir);
return -1;
}
threadfdnoblock(fd);
while((ret = p9listen(dir, newdir)) < 0 && errno==EAGAIN)
_threadfdwait(fd, 'r', getcallerpc(&dir));
return ret;
}
int
threadaccept(int cfd, char *dir)
{
return p9accept(cfd, dir);
}

View file

@ -1,7 +0,0 @@
#include "threadimpl.h"
extern int getfforkid(void);
int
_threadgetpid(void)
{
return getfforkid();
}

View file

@ -1,136 +0,0 @@
#include "threadimpl.h"
int
threadid(void)
{
return _threadgetproc()->thread->id;
}
int
threadpid(int id)
{
int pid;
Proc *p;
Thread *t;
if (id < 0)
return -1;
if (id == 0)
return _threadgetproc()->pid;
lock(&_threadpq.lock);
for (p = _threadpq.head; p->next; p = p->next){
lock(&p->lock);
for (t = p->threads.head; t; t = t->nextt)
if (t->id == id){
pid = p->pid;
unlock(&p->lock);
unlock(&_threadpq.lock);
return pid;
}
unlock(&p->lock);
}
unlock(&_threadpq.lock);
return -1;
}
int
threadsetgrp(int ng)
{
int og;
Thread *t;
t = _threadgetproc()->thread;
og = t->grp;
t->grp = ng;
return og;
}
int
threadgetgrp(void)
{
return _threadgetproc()->thread->grp;
}
void
threadsetname(char *fmt, ...)
{
Proc *p;
Thread *t;
va_list arg;
p = _threadgetproc();
t = p->thread;
if(t->name)
free(t->name);
va_start(arg, fmt);
t->name = vsmprint(fmt, arg);
va_end(arg);
_threaddebug(DBGSCHED, "set name %s", t->name);
/* Plan 9 only
if(p->nthreads == 1){
snprint(buf, sizeof buf, "#p/%d/args", getpid());
if((fd = open(buf, OWRITE)) >= 0){
snprint(buf, sizeof buf, "%s [%s]", argv0, name);
n = strlen(buf)+1;
s = strchr(buf, ' ');
if(s)
*s = '\0';
write(fd, buf, n);
close(fd);
}
}
*/
}
char*
threadgetname(void)
{
return _threadgetproc()->thread->name;
}
void**
threaddata(void)
{
return &_threadgetproc()->thread->udata[0];
}
void**
procdata(void)
{
return &_threadgetproc()->udata;
}
static Lock privlock;
static int privmask = 1;
int
tprivalloc(void)
{
int i;
lock(&privlock);
for(i=0; i<NPRIV; i++)
if(!(privmask&(1<<i))){
privmask |= 1<<i;
unlock(&privlock);
return i;
}
unlock(&privlock);
return -1;
}
void
tprivfree(int i)
{
if(i < 0 || i >= NPRIV)
abort();
lock(&privlock);
privmask &= ~(1<<i);
}
void**
tprivaddr(int i)
{
return &_threadgetproc()->thread->udata[i];
}

View file

@ -1,55 +0,0 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "ioproc.h"
long
iocall(Ioproc *io, long (*op)(va_list*), ...)
{
char e[ERRMAX];
int ret, inted;
Ioproc *msg;
if(send(io->c, &io) == -1){
werrstr("interrupted");
return -1;
}
assert(!io->inuse);
io->inuse = 1;
io->op = op;
va_start(io->arg, op);
msg = io;
inted = 0;
while(send(io->creply, &msg) == -1){
msg = nil;
inted = 1;
}
if(inted){
werrstr("interrupted");
return -1;
}
/*
* If we get interrupted, we have stick around so that
* the IO proc has someone to talk to. Send it an interrupt
* and try again.
*/
inted = 0;
while(recv(io->creply, nil) == -1){
inted = 1;
iointerrupt(io);
}
USED(inted);
va_end(io->arg);
ret = io->ret;
if(ret < 0)
strecpy(e, e+sizeof e, io->err);
io->inuse = 0;
/* release resources */
while(send(io->creply, &io) == -1)
;
if(ret < 0)
errstr(e, sizeof e);
return ret;
}

View file

@ -1,16 +0,0 @@
#include "threadimpl.h"
static long
_ioclose(va_list *arg)
{
int fd;
fd = va_arg(*arg, int);
return close(fd);
}
int
ioclose(Ioproc *io, int fd)
{
return iocall(io, _ioclose, fd);
}

View file

@ -1,24 +0,0 @@
#include "threadimpl.h"
static long
_iodial(va_list *arg)
{
char *addr, *local, *dir;
int *cdfp, fd;
addr = va_arg(*arg, char*);
local = va_arg(*arg, char*);
dir = va_arg(*arg, char*);
cdfp = va_arg(*arg, int*);
fprint(2, "before dial\n");
fd = dial(addr, local, dir, cdfp);
fprint(2, "after dial\n");
return fd;
}
int
iodial(Ioproc *io, char *addr, char *local, char *dir, int *cdfp)
{
return iocall(io, _iodial, addr, local, dir, cdfp);
}

View file

@ -1,21 +0,0 @@
#include <u.h>
#include <unistd.h>
#include <fcntl.h>
#include "threadimpl.h"
static long
_ioopen(va_list *arg)
{
char *path;
int mode;
path = va_arg(*arg, char*);
mode = va_arg(*arg, int);
return open(path, mode);
}
int
ioopen(Ioproc *io, char *path, int mode)
{
return iocall(io, _ioopen, path, mode);
}

View file

@ -1,77 +0,0 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "ioproc.h"
enum
{
STACK = 8192,
};
void
iointerrupt(Ioproc *io)
{
if(!io->inuse)
return;
threadint(io->tid);
}
static void
xioproc(void *a)
{
Ioproc *io, *x;
io = a;
/*
* first recvp acquires the ioproc.
* second tells us that the data is ready.
*/
for(;;){
while(recv(io->c, &x) == -1)
;
if(x == 0) /* our cue to leave */
break;
assert(x == io);
/* caller is now committed -- even if interrupted he'll return */
while(recv(io->creply, &x) == -1)
;
if(x == 0) /* caller backed out */
continue;
assert(x == io);
io->ret = io->op(&io->arg);
if(io->ret < 0)
rerrstr(io->err, sizeof io->err);
while(send(io->creply, &io) == -1)
;
while(recv(io->creply, &x) == -1)
;
}
}
Ioproc*
ioproc(void)
{
Ioproc *io;
io = mallocz(sizeof(*io), 1);
if(io == nil)
sysfatal("ioproc malloc: %r");
io->c = chancreate(sizeof(void*), 0);
io->creply = chancreate(sizeof(void*), 0);
io->tid = proccreate(xioproc, io, STACK);
return io;
}
void
closeioproc(Ioproc *io)
{
if(io == nil)
return;
iointerrupt(io);
while(send(io->c, 0) == -1)
;
chanfree(io->c);
chanfree(io->creply);
free(io);
}

View file

@ -1,14 +0,0 @@
#define ioproc_arg(io, type) (va_arg((io)->arg, type))
struct Ioproc
{
int tid;
Channel *c, *creply;
int inuse;
long (*op)(va_list*);
va_list arg;
long ret;
char err[ERRMAX];
Ioproc *next;
};

View file

@ -1,20 +0,0 @@
#include "threadimpl.h"
static long
_ioread(va_list *arg)
{
int fd;
void *a;
long n;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
return read(fd, a, n);
}
long
ioread(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _ioread, fd, a, n);
}

View file

@ -1,21 +0,0 @@
#include "threadimpl.h"
static long
_ioreadn(va_list *arg)
{
int fd;
void *a;
long n;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
n = readn(fd, a, n);
return n;
}
long
ioreadn(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _ioreadn, fd, a, n);
}

178
src/libthread/iorw.c Normal file
View file

@ -0,0 +1,178 @@
#include <u.h>
#include <unistd.h>
#include <fcntl.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include "ioproc.h"
static long
_ioclose(va_list *arg)
{
int fd;
fd = va_arg(*arg, int);
return close(fd);
}
int
ioclose(Ioproc *io, int fd)
{
return iocall(io, _ioclose, fd);
}
static long
_iodial(va_list *arg)
{
char *addr, *local, *dir;
int *cdfp, fd;
addr = va_arg(*arg, char*);
local = va_arg(*arg, char*);
dir = va_arg(*arg, char*);
cdfp = va_arg(*arg, int*);
fd = dial(addr, local, dir, cdfp);
return fd;
}
int
iodial(Ioproc *io, char *addr, char *local, char *dir, int *cdfp)
{
return iocall(io, _iodial, addr, local, dir, cdfp);
}
static long
_ioopen(va_list *arg)
{
char *path;
int mode;
path = va_arg(*arg, char*);
mode = va_arg(*arg, int);
return open(path, mode);
}
int
ioopen(Ioproc *io, char *path, int mode)
{
return iocall(io, _ioopen, path, mode);
}
static long
_ioread(va_list *arg)
{
int fd;
void *a;
long n;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
return read(fd, a, n);
}
long
ioread(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _ioread, fd, a, n);
}
static long
_ioreadn(va_list *arg)
{
int fd;
void *a;
long n;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
n = readn(fd, a, n);
return n;
}
long
ioreadn(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _ioreadn, fd, a, n);
}
static long
_iosleep(va_list *arg)
{
long n;
n = va_arg(*arg, long);
return sleep(n);
}
int
iosleep(Ioproc *io, long n)
{
return iocall(io, _iosleep, n);
}
static long
_iowrite(va_list *arg)
{
int fd;
void *a;
long n, nn;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
nn = write(fd, a, n);
return nn;
}
long
iowrite(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _iowrite, fd, a, n);
}
static long
_iosendfd(va_list *arg)
{
int n, fd, fd2;
fd = va_arg(*arg, int);
fd2 = va_arg(*arg, int);
n = sendfd(fd, fd2);
return n;
}
int
iosendfd(Ioproc *io, int fd, int fd2)
{
return iocall(io, _iosendfd, fd, fd2);
}
static long
_iorecvfd(va_list *arg)
{
int n, fd;
fd = va_arg(*arg, int);
n = recvfd(fd);
return n;
}
int
iorecvfd(Ioproc *io, int fd)
{
return iocall(io, _iorecvfd, fd);
}
static long
_ioread9pmsg(va_list *arg)
{
int fd;
void *a;
int n;
int r;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, int);
r = read9pmsg(fd, a, n);
return n;
}
int
ioread9pmsg(Ioproc *io, int fd, void *a, int n)
{
return iocall(io, _ioread9pmsg, fd, a, n);
}

View file

@ -1,16 +0,0 @@
#include "threadimpl.h"
static long
_iosleep(va_list *arg)
{
long n;
n = va_arg(*arg, long);
return sleep(n);
}
int
iosleep(Ioproc *io, long n)
{
return iocall(io, _iosleep, n);
}

View file

@ -1,22 +0,0 @@
#include "threadimpl.h"
static long
_iowrite(va_list *arg)
{
int fd;
void *a;
long n, nn;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
nn = write(fd, a, n);
fprint(2, "_iowrite %d %d %r\n", n, nn);
return nn;
}
long
iowrite(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _iowrite, fd, a, n);
}

View file

@ -1,90 +0,0 @@
#include <u.h>
#include <signal.h>
#include "threadimpl.h"
static void tinterrupt(Proc*, Thread*);
static void
threadxxxgrp(int grp, int dokill)
{
Proc *p;
Thread *t;
lock(&_threadpq.lock);
for(p=_threadpq.head; p; p=p->next){
lock(&p->lock);
for(t=p->threads.head; t; t=t->nextt)
if(t->grp == grp){
if(dokill)
t->moribund = 1;
tinterrupt(p, t);
}
unlock(&p->lock);
}
unlock(&_threadpq.lock);
_threadbreakrendez();
}
static void
threadxxx(int id, int dokill)
{
Proc *p;
Thread *t;
lock(&_threadpq.lock);
for(p=_threadpq.head; p; p=p->next){
lock(&p->lock);
for(t=p->threads.head; t; t=t->nextt)
if(t->id == id){
if(dokill)
t->moribund = 1;
tinterrupt(p, t);
unlock(&p->lock);
unlock(&_threadpq.lock);
_threadbreakrendez();
return;
}
unlock(&p->lock);
}
unlock(&_threadpq.lock);
_threaddebug(DBGNOTE, "Can't find thread to kill");
return;
}
void
threadkillgrp(int grp)
{
threadxxxgrp(grp, 1);
}
void
threadkill(int id)
{
threadxxx(id, 1);
}
void
threadintgrp(int grp)
{
threadxxxgrp(grp, 0);
}
void
threadint(int id)
{
threadxxx(id, 0);
}
static void
tinterrupt(Proc *p, Thread *t)
{
switch(t->state){
case Running:
kill(p->pid, SIGINT);
// postnote(PNPROC, p->pid, "threadint");
break;
case Rendezvous:
_threadflagrendez(t);
break;
}
}

View file

@ -1,68 +0,0 @@
/*
* setjmp and longjmp, but our own because some (stupid) c libraries
* assume longjmp is only used to move up the stack, and error out
* if you do otherwise.
*/
typedef struct Label Label;
#define LABELDPC 0
#if defined(__linux__)
#include <ucontext.h>
struct Label
{
ucontext_t uc;
};
#elif defined (__i386__) && (defined(__FreeBSD__) || defined(__linux__) || defined(__OpenBSD__))
struct Label
{
ulong pc;
ulong bx;
ulong sp;
ulong bp;
ulong si;
ulong di;
};
#elif defined(__APPLE__)
struct Label
{
ulong pc; /* lr */
ulong cr; /* mfcr */
ulong ctr; /* mfcr */
ulong xer; /* mfcr */
ulong sp; /* callee saved: r1 */
ulong toc; /* callee saved: r2 */
ulong gpr[19]; /* callee saved: r13-r31 */
// XXX: currently do not save vector registers or floating-point state
// ulong pad;
// uvlong fpr[18]; /* callee saved: f14-f31 */
// ulong vr[4*12]; /* callee saved: v20-v31, 256-bits each */
};
#elif defined(__sun__)
struct Label
{
ulong input[8]; /* %i registers */
ulong local[8]; /* %l registers */
ulong sp; /* %o6 */
ulong link; /* %o7 */
};
#elif defined(__powerpc__)
struct Label
{
ulong pc; /* lr */
ulong cr; /* mfcr */
ulong ctr; /* mfcr */
ulong xer; /* mfcr */
ulong sp; /* callee saved: r1 */
ulong toc; /* callee saved: r2 */
ulong gpr[19]; /* callee saved: r13-r31 */
// XXX: currently do not save vector registers or floating-point state
// ulong pad;
// uvlong fpr[18]; /* callee saved: f14-f31 */
// ulong vr[4*12]; /* callee saved: v20-v31, 256-bits each */
};
#else
#error "Unknown or unsupported architecture"
#endif

View file

@ -1,35 +0,0 @@
#include "threadimpl.h"
static long totalmalloc;
void*
_threadmalloc(long size, int z)
{
void *m;
m = malloc(size);
if (m == nil)
sysfatal("Malloc of size %ld failed: %r\n", size);
setmalloctag(m, getcallerpc(&size));
totalmalloc += size;
if (size > 1000000) {
fprint(2, "Malloc of size %ld, total %ld\n", size, totalmalloc);
abort();
}
if (z)
_threadmemset(m, 0, size);
return m;
}
void
_threadsysfatal(char *fmt, va_list arg)
{
char buf[1024]; /* size doesn't matter; we're about to exit */
vseprint(buf, buf+sizeof(buf), fmt, arg);
if(argv0)
fprint(2, "%s: %s\n", argv0, buf);
else
fprint(2, "%s\n", buf);
threadexitsall(buf);
}

View file

@ -1,98 +0,0 @@
/*
* Thread library.
*/
#include "threadimpl.h"
typedef struct Mainarg Mainarg;
struct Mainarg
{
int argc;
char **argv;
};
int mainstacksize;
extern void (*_sysfatal)(char*, va_list);
extern Jmp *(*_notejmpbuf)(void);
static void
mainlauncher(void *arg)
{
Mainarg *a;
a = arg;
_kmaininit();
threadmain(a->argc, a->argv);
threadexits("threadmain");
}
int
main(int argc, char **argv)
{
Mainarg a;
Proc *p;
/*
* In pthreads, threadmain is actually run in a subprocess,
* so that the main process can exit (if threaddaemonize is called).
* The main process relays notes to the subprocess.
* _Threadbackgroundsetup will return only in the subprocess.
*/
_threadbackgroundinit();
/*
* Instruct QLock et al. to use our scheduling functions
* so that they can operate at the thread level.
*/
_qlockinit(_threadsleep, _threadwakeup);
/*
* Install our own _threadsysfatal which takes down
* the whole confederation of procs.
*/
_sysfatal = _threadsysfatal;
/*
* Install our own jump handler.
*/
_notejmpbuf = _threadgetjmp;
/*
* Install our own signal handler.
*/
notify(_threadnote);
/*
* Construct the initial proc running mainlauncher(&a).
*/
if(mainstacksize == 0)
mainstacksize = 32*1024;
a.argc = argc;
a.argv = argv;
p = _newproc();
_newthread(p, mainlauncher, &a, mainstacksize, "threadmain", 0);
_threadscheduler(p);
abort(); /* not reached */
return 0;
}
/*
* No-op function here so that sched.o drags in main.o.
*/
void
_threadlinkmain(void)
{
}
Jmp*
_threadgetjmp(void)
{
static Jmp j;
Proc *p;
p = _threadgetproc();
if(p == nil)
return &j;
return &p->sigjmp;
}

View file

@ -1,8 +0,0 @@
#include "threadimpl.h"
#include <string.h>
void
_threadmemset(void *v, int c, int n)
{
memset(v, c, n);
}

View file

@ -1,8 +0,0 @@
#include "threadimpl.h"
#include <string.h>
void
_threaddebugmemset(void *v, int c, int n)
{
memset(v, c, n);
}

View file

@ -1,73 +0,0 @@
<$PLAN9/src/mkhdr
LIB=libthread.a
SYSOFILES=`sh ./sysofiles.sh`
OFILES=\
$SYSOFILES\
channel.$O\
chanprint.$O\
create.$O\
debug.$O\
exec-unix.$O\
exit.$O\
fdwait.$O\
getpid.$O\
id.$O\
iocall.$O\
ioclose.$O\
iodial.$O\
ioopen.$O\
ioproc.$O\
ioread.$O\
ioreadn.$O\
iosleep.$O\
iowrite.$O\
lib.$O\
main.$O\
memset.$O\
memsetd.$O\
read9pmsg.$O\
ref.$O\
sched.$O\
setproc.$O\
sleep.$O\
HFILES=\
$PLAN9/include/thread.h\
label.h\
threadimpl.h\
<$PLAN9/src/mksyslib
tfork: tfork.$O $PLAN9/lib/$LIB
$LD -o tfork tfork.$O $LDFLAGS -lthread -l9
tprimes: tprimes.$O $PLAN9/lib/$LIB
$LD -o tprimes tprimes.$O $LDFLAGS -lthread -l9
texec: texec.$O $PLAN9/lib/$LIB
$LD -o texec texec.$O $LDFLAGS -lthread -l9
tspawn: tspawn.$O $PLAN9/lib/$LIB
$LD -o tspawn tspawn.$O $LDFLAGS -lthread -l9
trend: trend.$O $PLAN9/lib/$LIB
$LD -o trend trend.$O $LDFLAGS -lthread -l9
tsignal: tsignal.$O $PLAN9/lib/$LIB
$LD -o tsignal tsignal.$O $LDFLAGS -lthread -l9
CLEANFILES=$CLEANFILES tprimes texec
asm-Linux-ppc.$O: asm-Linux-386.s
asm-Linux-386.$O: asm-FreeBSD-386.s
asm-NetBSD-386.$O: asm-FreeBSD-386.s
asm-OpenBSD-386.$O: asm-FreeBSD-386.s
# sorry
VG=`test -d /home/rsc/pub/valgrind-debian && echo -DUSEVALGRIND`
# VG=
CFLAGS=$CFLAGS $VG
Linux-clone.$O: execproc.ch exit-getpid.ch proctab.ch procstack.ch

View file

@ -1,145 +0,0 @@
#include "threadimpl.h"
int _threadnopasser;
#define NFN 33
#define ERRLEN 48
typedef struct Note Note;
struct Note
{
Lock inuse;
Proc *proc; /* recipient */
char s[ERRMAX]; /* arg2 */
};
static Note notes[128];
static Note *enotes = notes+nelem(notes);
static int (*onnote[NFN])(void*, char*);
static int onnotepid[NFN];
static Lock onnotelock;
int
threadnotify(int (*f)(void*, char*), int in)
{
int i, topid;
int (*from)(void*, char*), (*to)(void*, char*);
if(in){
from = 0;
to = f;
topid = _threadgetproc()->pid;
}else{
from = f;
to = 0;
topid = 0;
}
lock(&onnotelock);
for(i=0; i<NFN; i++)
if(onnote[i]==from){
onnote[i] = to;
onnotepid[i] = topid;
break;
}
unlock(&onnotelock);
return i<NFN;
}
static void
delayednotes(Proc *p, void *v)
{
int i;
Note *n;
int (*fn)(void*, char*);
if(!p->pending)
return;
p->pending = 0;
for(n=notes; n<enotes; n++){
if(n->proc == p){
for(i=0; i<NFN; i++){
if(onnotepid[i]!=p->pid || (fn = onnote[i])==0)
continue;
if((*fn)(v, n->s))
break;
}
if(i==NFN){
_threaddebug(DBGNOTE, "Unhandled note %s, proc %p\n", n->s, p);
if(strcmp(n->s, "sys: child") == 0)
noted(NCONT);
fprint(2, "unhandled note %s, pid %d\n", n->s, p->pid);
if(v != nil)
noted(NDFLT);
else if(strncmp(n->s, "sys:", 4)==0)
abort();
threadexitsall(n->s);
}
n->proc = nil;
unlock(&n->inuse);
}
}
}
void
_threadnote(void *v, char *s)
{
Proc *p;
Note *n;
_threaddebug(DBGNOTE, "Got note %s", s);
if(strncmp(s, "sys:", 4) == 0)
noted(NDFLT);
// if(_threadexitsallstatus){
// _threaddebug(DBGNOTE, "Threadexitsallstatus = '%s'\n", _threadexitsallstatus);
// _exits(_threadexitsallstatus);
// }
if(strcmp(s, "threadint")==0 || strcmp(s, "interrupt")==0)
noted(NCONT);
p = _threadgetproc();
if(p == nil)
noted(NDFLT);
for(n=notes; n<enotes; n++)
if(canlock(&n->inuse))
break;
if(n==enotes)
sysfatal("libthread: too many delayed notes");
utfecpy(n->s, n->s+ERRMAX, s);
n->proc = p;
p->pending = 1;
if(!p->splhi)
delayednotes(p, v);
noted(NCONT);
}
int
_procsplhi(void)
{
int s;
Proc *p;
p = _threadgetproc();
s = p->splhi;
p->splhi = 1;
return s;
}
void
_procsplx(int s)
{
Proc *p;
p = _threadgetproc();
p->splhi = s;
if(s)
return;
/*
if(p->pending)
delayednotes(p, nil);
*/
}

View file

@ -1,25 +0,0 @@
mypid = getpid();
/*
* signal others.
* copying all the pids first avoids other thread's
* teardown procedures getting in the way.
*/
lock(&_threadpq.lock);
npid = 0;
for(p=_threadpq.head; p; p=p->next)
npid++;
pid = _threadmalloc(npid*sizeof(pid[0]), 0);
npid = 0;
for(p = _threadpq.head; p; p=p->next)
pid[npid++] = p->pid;
unlock(&_threadpq.lock);
for(i=0; i<npid; i++){
_threaddebug(DBGSCHED, "threadexitsall kill %d", pid[i]);
if(pid[i]==0 || pid[i]==-1)
fprint(2, "bad pid in threadexitsall: %d\n", pid[i]);
else if(pid[i] != mypid){
kill(pid[i], SIGTERM);
}
}

View file

@ -1,40 +0,0 @@
#include "threadimpl.h"
static void
launcherpower(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7,
void (*f)(void *arg), void *arg)
{
(*f)(arg);
threadexits(nil);
}
void
_threadinitstack(Thread *t, void (*f)(void*), void *arg)
{
ulong *tos, *stk;
tos = (ulong*)&t->stk[t->stksize&~7];
stk = tos;
--stk;
--stk;
--stk;
--stk;
*--stk = (ulong)arg;
*--stk = (ulong)f;
t->sched.pc = (ulong)launcherpower+LABELDPC;
t->sched.sp = ((ulong)stk)-8;
*((ulong *)t->sched.sp) = 0;
}
void
_threadinswitch(int enter)
{
USED(enter);
}
void
_threadstacklimit(void *addr, void *addr2)
{
USED(addr);
}

View file

@ -1,75 +0,0 @@
static int fforkstacksize = 16384;
typedef struct Stack Stack;
struct Stack
{
Stack *next;
Stack *fnext;
int pid;
};
static Lock stacklock;
static Stack *freestacks;
static Stack *allstacks;
static int stackmallocs;
static void gc(void);
static void*
mallocstack(void)
{
Stack *p;
lock(&stacklock);
top:
p = freestacks;
if(p)
freestacks = p->fnext;
else{
if(stackmallocs++%1 == 0)
gc();
if(freestacks)
goto top;
p = malloc(fforkstacksize);
p->next = allstacks;
allstacks = p;
}
if(p)
p->pid = 1;
unlock(&stacklock);
return p;
}
static void
gc(void)
{
Stack *p;
for(p=allstacks; p; p=p->next){
if(p->pid > 1 && procexited(p->pid)){
if(0) fprint(2, "reclaim stack from %d\n", p->pid);
p->pid = 0;
}
if(p->pid == 0){
p->fnext = freestacks;
freestacks = p;
}
}
}
static void
freestack(void *v)
{
Stack *p;
p = v;
if(p == nil)
return;
lock(&stacklock);
p->fnext = freestacks;
p->pid = 0;
freestacks = p;
unlock(&stacklock);
return;
}

View file

@ -1,75 +0,0 @@
#include "threadimpl.h"
/* this will need work */
enum
{
PTABHASH = 257,
};
static int multi;
static Proc *theproc;
void
_threadmultiproc(void)
{
if(multi == 0){
multi = 1;
_threadsetproc(theproc);
}
}
static Lock ptablock;
Proc *ptab[PTABHASH];
void
_threadsetproc(Proc *p)
{
int h;
if(!multi){
theproc = p;
return;
}
lock(&ptablock);
h = ((unsigned)p->pid)%PTABHASH;
p->link = ptab[h];
unlock(&ptablock);
ptab[h] = p;
}
static Proc*
__threadgetproc(int rm)
{
Proc **l, *p;
int h, pid;
if(!multi)
return theproc;
pid = getpid();
lock(&ptablock);
h = ((unsigned)pid)%PTABHASH;
for(l=&ptab[h]; p=*l; l=&p->link){
if(p->pid == pid){
if(rm)
*l = p->link;
unlock(&ptablock);
return p;
}
}
unlock(&ptablock);
return nil;
}
Proc*
_threadgetproc(void)
{
return __threadgetproc(0);
}
Proc*
_threaddelproc(void)
{
return __threadgetproc(1);
}

View file

@ -1,97 +0,0 @@
/*
* Proc structure hash table indexed by proctabid() (usually getpid()).
* No lock is necessary for lookups (important when called from signal
* handlers).
*
* To be included from other files (e.g., Linux-clone.c).
*/
#define T ((void*)-1)
enum
{
PTABHASH = 1031,
};
static Lock ptablock;
static Proc *proctab[PTABHASH];
static Proc *theproc;
static int multi;
void
_threadmultiproc(void)
{
if(multi == 0){
multi = 1;
_threadsetproc(theproc);
}
}
void
_threadsetproc(Proc *p)
{
int i, h;
Proc **t;
if(!multi){
theproc = p;
return;
}
lock(&ptablock);
p->procid = procid();
h = p->procid%PTABHASH;
for(i=0; i<PTABHASH; i++){
t = &proctab[(h+i)%PTABHASH];
if(*t==nil || *t==T){
*t = p;
break;
}
}
unlock(&ptablock);
if(i == PTABHASH)
sysfatal("too many procs - proctab is full");
}
static Proc**
_threadfindproc(int id)
{
int i, h;
Proc **t;
if(!multi)
return &theproc;
h = id%PTABHASH;
for(i=0; i<PTABHASH; i++){
t = &proctab[(h+i)%PTABHASH];
if(*t != nil && *t != T && (*t)->procid == id){
unlock(&ptablock);
return t;
}
}
return nil;
}
Proc*
_threadgetproc(void)
{
Proc **t;
t = _threadfindproc(procid());
if(t == nil)
return nil;
return *t;
}
Proc*
_threaddelproc(void)
{
Proc **t, *p;
t = _threadfindproc(procid());
if(t == nil)
return nil;
p = *t;
*t = T;
return p;
}

View file

@ -1,271 +0,0 @@
#include <u.h>
#include <errno.h>
#include "threadimpl.h"
/*
* Basic kernel thread management.
*/
static pthread_key_t key;
void
_kthreadinit(void)
{
pthread_key_create(&key, 0);
}
void
_kthreadsetproc(Proc *p)
{
sigset_t all;
p->pthreadid = pthread_self();
sigfillset(&all);
pthread_sigmask(SIG_SETMASK, &all, nil);
pthread_setspecific(key, p);
}
Proc*
_kthreadgetproc(void)
{
return pthread_getspecific(key);
}
void
_kthreadstartproc(Proc *p)
{
Proc *np;
pthread_t tid;
sigset_t all;
np = p->newproc;
sigfillset(&all);
pthread_sigmask(SIG_SETMASK, &all, nil);
if(pthread_create(&tid, nil, (void*(*)(void*))_threadscheduler,
np) < 0)
sysfatal("pthread_create: %r");
np->pthreadid = tid;
}
void
_kthreadexitproc(char *exitstr)
{
_threaddebug(DBGSCHED, "_pthreadexit");
pthread_exit(nil);
}
void
_kthreadexitallproc(char *exitstr)
{
_threaddebug(DBGSCHED, "_threadexitallproc");
exits(exitstr);
}
/*
* Exec. Pthreads does the hard work of making it possible
* for any thread to do the waiting, so this is pretty easy.
* We create a separate proc whose job is to wait for children
* and deliver wait messages.
*/
static Channel *_threadexecwaitchan;
static void
_threadwaitproc(void *v)
{
Channel *c;
Waitmsg *w;
_threadinternalproc();
USED(v);
for(;;){
w = wait();
if(w == nil){
if(errno == ECHILD) /* wait for more */
recvul(_threadexecwaitchan);
continue;
}
if((c = _threadwaitchan) != nil)
sendp(c, w);
else
free(w);
}
fprint(2, "_threadwaitproc exits\n"); /* not reached */
}
/*
* Call _threadexec in the right conditions.
*/
int
_kthreadexec(Channel *c, int fd[3], char *prog, char *args[], int freeargs)
{
static Lock lk;
int rv;
if(!_threadexecwaitchan){
lock(&lk);
if(!_threadexecwaitchan){
_threadexecwaitchan = chancreate(sizeof(ulong), 1);
proccreate(_threadwaitproc, nil, 32*1024);
}
unlock(&lk);
}
rv = _threadexec(c, fd, prog, args, freeargs);
nbsendul(_threadexecwaitchan, 1);
return rv;
}
/*
* Some threaded applications want to run in the background.
* Calling fork() and exiting in the parent will result in a child
* with a single pthread (if we are using pthreads), and will screw
* up our internal process info if we are using clone/rfork.
* Instead, apps should call threadbackground(), which takes
* care of this.
*
* _threadbackgroundinit is called from main.
*/
static int mainpid, passerpid;
static void
passer(void *x, char *msg)
{
Waitmsg *w;
USED(x);
if(strcmp(msg, "sys: usr2") == 0)
_exit(0); /* daemonize */
else if(strcmp(msg, "sys: child") == 0){
/* child exited => so should we */
w = wait();
if(w == nil)
_exit(1);
_exit(atoi(w->msg));
}else
postnote(PNGROUP, mainpid, msg);
}
void
_threadbackgroundinit(void)
{
int pid;
sigset_t mask;
sigfillset(&mask);
pthread_sigmask(SIG_BLOCK, &mask, 0);
return;
passerpid = getpid();
switch(pid = fork()){
case -1:
sysfatal("fork: %r");
case 0:
rfork(RFNOTEG);
return;
default:
break;
}
mainpid = pid;
notify(passer);
notifyon("sys: child");
notifyon("sys: usr2"); /* should already be on */
for(;;)
pause();
_exit(0);
}
void
threadbackground(void)
{
if(passerpid <= 1)
return;
postnote(PNPROC, passerpid, "sys: usr2");
}
/*
* Notes.
*/
Channel *_threadnotechan;
static ulong sigs;
static Lock _threadnotelk;
static void _threadnoteproc(void*);
extern int _p9strsig(char*);
extern char *_p9sigstr(int);
Channel*
threadnotechan(void)
{
if(_threadnotechan == nil){
lock(&_threadnotelk);
if(_threadnotechan == nil){
_threadnotechan = chancreate(sizeof(char*), 1);
proccreate(_threadnoteproc, nil, 32*1024);
}
unlock(&_threadnotelk);
}
return _threadnotechan;
}
void
_threadnote(void *x, char *msg)
{
USED(x);
if(_threadexitsallstatus)
_kthreadexitproc(_threadexitsallstatus);
if(strcmp(msg, "sys: usr2") == 0)
noted(NCONT);
if(_threadnotechan == nil)
noted(NDFLT);
sigs |= 1<<_p9strsig(msg);
noted(NCONT);
}
void
_threadnoteproc(void *x)
{
int i;
sigset_t none;
Channel *c;
_threadinternalproc();
sigemptyset(&none);
pthread_sigmask(SIG_SETMASK, &none, 0);
c = _threadnotechan;
for(;;){
if(sigs == 0)
pause();
for(i=0; i<32; i++){
if((sigs&(1<<i)) == 0)
continue;
sigs &= ~(1<<i);
if(i == 0)
continue;
sendp(c, _p9sigstr(i));
}
}
}
void
_threadschednote(void)
{
}
void
_kmaininit(void)
{
sigset_t all;
sigfillset(&all);
pthread_sigmask(SIG_SETMASK, &all, 0);
}

3
src/libthread/qlock.c Normal file
View file

@ -0,0 +1,3 @@
#include "u.h"
#include "libc.h"

View file

@ -1,32 +0,0 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
int
threadread9pmsg(int fd, void *abuf, uint n)
{
int m, len;
uchar *buf;
buf = abuf;
/* read count */
m = threadreadn(fd, buf, BIT32SZ);
if(m != BIT32SZ){
if(m < 0)
return -1;
return 0;
}
len = GBIT32(buf);
if(len <= BIT32SZ || len > n){
werrstr("bad length in 9P2000 message header");
return -1;
}
len -= BIT32SZ;
m = threadreadn(fd, buf+BIT32SZ, len);
if(m < len)
return 0;
return BIT32SZ+m;
}

View file

@ -1,26 +0,0 @@
/*
* Atomic reference counts - used by applications.
*
* We use locks to avoid the assembly of the Plan 9 versions.
*/
#include "threadimpl.h"
void
incref(Ref *r)
{
lock(&r->lk);
r->ref++;
unlock(&r->lk);
}
long
decref(Ref *r)
{
long n;
lock(&r->lk);
n = --r->ref;
unlock(&r->lk);
return n;
}

View file

@ -1,343 +0,0 @@
/*
* Thread scheduler.
*/
#include "threadimpl.h"
static Thread *runthread(Proc*);
static void schedexit(Proc*);
/*
* Main scheduling loop.
*/
void
_threadscheduler(void *arg)
{
Proc *p;
Thread *t;
p = arg;
_threadlinkmain();
_threadsetproc(p);
for(;;){
/*
* Clean up zombie children.
*/
/*
* Find next thread to run.
*/
_threaddebug(DBGSCHED, "runthread");
t = runthread(p);
if(t == nil)
schedexit(p);
/*
* If it's ready, run it (might instead be marked to die).
*/
lock(&p->lock);
if(t->state == Ready){
_threaddebug(DBGSCHED, "running %d.%d", p->id, t->id);
t->state = Running;
t->nextstate = Ready;
p->thread = t;
unlock(&p->lock);
_swaplabel(&p->context, &t->context);
lock(&p->lock);
p->thread = nil;
}
/*
* If thread needs to die, kill it.
* t->proc == p may not be true if we're
* trying to jump into the exec proc (see exec-unix.c).
*/
if(t->moribund){
_threaddebug(DBGSCHED, "moribund %d.%d", p->id, t->id);
if(t->moribund != 1)
print("moribund broke %p %d\n", &t->moribund, t->moribund);
assert(t->moribund == 1);
t->state = Dead;
_procdelthread(p, t);
unlock(&p->lock);
_threadfree(t);
t = nil;
continue;
}
/*
* If the thread has asked to move to another proc,
* let it go (only to be used in *very* special situations).
if(t->nextproc != p)
_procdelthread(p, t);
*/
unlock(&p->lock);
/*
* If the thread has asked to move to another proc,
* add it to the new proc.
*/
if(t->nextproc != p){
// lock(&t->nextproc->lock);
// _procaddthread(t->nextproc, t);
// unlock(&t->nextproc->lock);
t->proc = t->nextproc;
}
/*
* If there is a request to run a function on the
* scheduling stack, do so.
*/
if(p->schedfn){
_threaddebug(DBGSCHED, "schedfn");
p->schedfn(p);
p->schedfn = nil;
_threaddebug(DBGSCHED, "schedfn ended");
}
/*
* Move the thread along.
*/
t->state = t->nextstate;
_threaddebug(DBGSCHED, "moveon %d.%d", t->proc->id, t->id);
if(t->state == Ready)
_threadready(t);
}
}
/*
* Called by thread to give up control of processor to scheduler.
*/
int
_sched(void)
{
Proc *p;
Thread *t;
p = _threadgetproc();
t = p->thread;
assert(t != nil);
_swaplabel(&t->context, &p->context);
return p->nsched++;
}
/*
* Called by thread to yield the processor to other threads.
* Returns number of other threads run between call and return.
*/
int
yield(void)
{
Proc *p;
int nsched;
p = _threadgetproc();
nsched = p->nsched;
return _sched() - nsched;
}
/*
* Choose the next thread to run.
*/
static Thread*
runthread(Proc *p)
{
Thread *t;
Tqueue *q;
/*
* No threads left?
*/
if(p->nthreads==0 || (p->nthreads==1 && p->idle))
return nil;
_threadschednote();
lock(&p->readylock);
q = &p->ready;
if(q->head == nil){
/*
* Is this a single-process program with an idle thread?
*/
if(p->idle){
/*
* The idle thread had better be ready!
*/
if(p->idle->state != Ready)
sysfatal("all threads are asleep");
/*
* Run the idle thread.
*/
unlock(&p->readylock);
_threaddebug(DBGSCHED, "running idle thread", p->nthreads);
return p->idle;
}
/*
* Wait until one of our threads is readied (by another proc!).
*/
q->asleep = 1;
p->rend.l = &p->readylock;
while(q->asleep){
_procsleep(&p->rend);
_threadschednote();
}
/*
* Maybe we were awakened to exit?
*/
if(_threadexitsallstatus){
_threaddebug(DBGSCHED, "time to exit");
_exits(_threadexitsallstatus);
}
assert(q->head != nil);
}
t = q->head;
q->head = t->next;
unlock(&p->readylock);
return t;
}
/*
* Add a newly-ready thread to its proc's run queue.
*/
void
_threadready(Thread *t)
{
Tqueue *q;
/*
* The idle thread does not go on the run queue.
*/
if(t == t->proc->idle){
_threaddebug(DBGSCHED, "idle thread is ready");
return;
}
assert(t->state == Ready);
_threaddebug(DBGSCHED, "readying %d.%d", t->proc->id, t->id);
/*
* Add thread to run queue.
*/
q = &t->proc->ready;
lock(&t->proc->readylock);
t->next = nil;
if(q->head == nil)
q->head = t;
else
q->tail->next = t;
q->tail = t;
/*
* Wake proc scheduler if it is sleeping.
*/
if(q->asleep){
assert(q->asleep == 1);
q->asleep = 0;
_procwakeup(&t->proc->rend);
}
unlock(&t->proc->readylock);
}
/*
* Mark the given thread as the idle thread.
* Since the idle thread was just created, it is sitting
* somewhere on the ready queue.
*/
void
_threadsetidle(int id)
{
Tqueue *q;
Thread *t, **l, *last;
Proc *p;
p = _threadgetproc();
lock(&p->readylock);
/*
* Find thread on ready queue.
*/
q = &p->ready;
for(l=&q->head, last=nil; (t=*l) != nil; l=&t->next, last=t)
if(t->id == id)
break;
assert(t != nil);
/*
* Remove it from ready queue.
*/
*l = t->next;
if(t == q->head)
q->head = t->next;
if(t->next == nil)
q->tail = last;
/*
* Set as idle thread.
*/
p->idle = t;
_threaddebug(DBGSCHED, "p->idle is %d\n", t->id);
unlock(&p->readylock);
}
/*
* Mark proc as internal so that if all but internal procs exit, we exit.
*/
void
_threadinternalproc(void)
{
Proc *p;
p = _threadgetproc();
if(p->internal)
return;
lock(&_threadpq.lock);
if(p->internal == 0){
p->internal = 1;
--_threadnprocs;
}
unlock(&_threadpq.lock);
}
static void
schedexit(Proc *p)
{
char ex[ERRMAX];
int n;
Proc **l;
_threaddebug(DBGSCHED, "exiting proc %d", p->id);
lock(&_threadpq.lock);
for(l=&_threadpq.head; *l; l=&(*l)->next){
if(*l == p){
*l = p->next;
if(*l == nil)
_threadpq.tail = l;
break;
}
}
if(p->internal)
n = _threadnprocs;
else
n = --_threadnprocs;
unlock(&_threadpq.lock);
strncpy(ex, p->exitstr, sizeof ex);
ex[sizeof ex-1] = '\0';
free(p);
if(n == 0){
_threaddebug(DBGSCHED, "procexit; no more procs");
_kthreadexitallproc(ex);
}else{
_threaddebug(DBGSCHED, "procexit");
_kthreadexitproc(ex);
}
}

View file

@ -1,36 +0,0 @@
/*
* Avoid using threading calls for single-proc programs.
*/
#include "threadimpl.h"
static int multi;
static Proc *theproc;
void
_threadsetproc(Proc *p)
{
if(!multi)
theproc = p;
else
_kthreadsetproc(p);
}
Proc*
_threadgetproc(void)
{
if(!multi)
return theproc;
return _kthreadgetproc();
}
void
_threadmultiproc(void)
{
if(multi)
return;
multi = 1;
_kthreadinit();
_threadsetproc(theproc);
}

View file

@ -1,39 +0,0 @@
#include "threadimpl.h"
int _threadhighnrendez;
int _threadnrendez;
void
_threadsleep(_Procrend *r)
{
Thread *t;
t = _threadgetproc()->thread;
r->arg = t;
t->nextstate = Rendezvous;
t->asleep = 1;
unlock(r->l);
_sched();
t->asleep = 0;
lock(r->l);
}
void
_threadwakeup(_Procrend *r)
{
Thread *t;
t = r->arg;
while(t->state == Running)
sleep(0);
lock(&t->proc->lock);
if(t->state == Dead){
unlock(&t->proc->lock);
return;
}
assert(t->state == Rendezvous && t->asleep);
t->state = Ready;
_threadready(t);
unlock(&t->proc->lock);
}

View file

@ -1,53 +0,0 @@
#include "threadimpl.h"
static void
launchersparc(uint o0, uint o1, uint o2, uint o3,
uint o4, uint o5, uint o6, uint o7,
void (*f)(void *arg), void *arg)
{
if(0) print("ls %x %x %x %x %x %x %x %x %x %x at %x\n",
o0, o1, o2, o3, o4, o5, o6, o7, f, arg, &o0);
(*f)(arg);
threadexits(nil);
}
void
_threadinitstack(Thread *t, void (*f)(void*), void *arg)
{
ulong *tos, *stk;
/*
* This is a bit more complicated than it should be,
* because we need to set things up so that gotolabel
* (which executes a return) gets us into launchersparc.
* So all the registers are going to be renamed before
* we get there. The input registers here become the
* output registers there, which is useless.
* The input registers there are inaccessible, so we
* have to give launchersparc enough arguments that
* everything ends up in the stack.
*/
tos = (ulong*)&t->stk[t->stksize&~7];
stk = tos;
--stk;
*--stk = (ulong)arg;
*--stk = (ulong)f;
stk -= 25; /* would love to understand this */
t->sched.link = (ulong)launchersparc - 8;
t->sched.input[6] = 0;
t->sched.sp = (ulong)stk;
if(0) print("tis %x %x at %x\n", f, arg, t->sched.sp);
}
void
_threadinswitch(int enter)
{
USED(enter);
}
void
_threadstacklimit(void *addr, void *addr2)
{
USED(addr);
}

View file

@ -1,11 +0,0 @@
#!/bin/sh
case "`uname`-`uname -r`" in
Linux-2.[01234]*)
echo Linux-clone.o ucontext.o
exit 0
;;
esac
echo pthread.o ucontext.o
exit 0

View file

@ -0,0 +1,38 @@
#include <pthread.h>
#include <utf.h>
#include <fmt.h>
pthread_key_t key;
void
pexit(void *v)
{
int s;
pthread_setspecific(key, (void*)1);
switch(fork()){
case -1:
fprint(2, "fork: %r\n");
case 0:
_exit(0);
default:
wait(&s);
}
pthread_exit(0);
}
int
main(int argc, char *argv[])
{
int i;
pthread_t pid;
pthread_key_create(&key, 0);
for(i=0;; i++){
print("%d\n", i);
if(pthread_create(&pid, 0, pexit, 0) < 0){
fprint(2, "pthread_create: %r\n");
abort();
}
}
}

View file

@ -0,0 +1,80 @@
#include "u.h"
#include "libc.h"
#include "thread.h"
enum
{
STACK = 8192
};
int max = 10000;
int (*mk)(void (*fn)(void*), void *arg, uint stack);
void
countthread(void *v)
{
uint i;
Channel *c;
c = v;
for(i=2;; i++){
sendul(c, i);
}
}
void
filterthread(void *v)
{
uint i, p;
Channel *c, *nextc;
c = v;
p = recvul(c);
print("%d\n", p);
if(p > max)
threadexitsall(0);
nextc = chancreate(sizeof(ulong), 0);
mk(filterthread, nextc, STACK);
for(;;){
i = recvul(c);
if(i%p)
sendul(nextc, i);
}
}
void
usage(void)
{
fprint(2, "usage: tprimes [-p] [max]\n");
threadexitsall("usage");
}
void
threadmain(int argc, char **argv)
{
Channel *c;
int nbuf;
nbuf = 0;
mk = threadcreate;
ARGBEGIN{
default:
usage();
case 'b':
nbuf = atoi(EARGF(usage()));
break;
case 'p':
mk = proccreate;
max = 1000;
break;
}ARGEND
if(argc == 1)
max = atoi(argv[0]);
else if(argc)
usage();
c = chancreate(sizeof(ulong), nbuf);
mk(countthread, c, STACK);
mk(filterthread, c, STACK);
}

View file

@ -1,17 +1,6 @@
#include <lib9.h>
#include <thread.h>
extern int _threaddebuglevel;
void
doexec(void *v)
{
int fd[3];
char **argv = v;
print("exec failed: %r\n");
sendp(threadwaitchan(), nil);
threadexits(nil);
}
#include "u.h"
#include "libc.h"
#include "thread.h"
void
threadmain(int argc, char **argv)
@ -22,7 +11,6 @@ threadmain(int argc, char **argv)
ARGBEGIN{
case 'D':
_threaddebuglevel = ~0;
break;
}ARGEND
@ -30,9 +18,19 @@ threadmain(int argc, char **argv)
fd[0] = dup(0, -1);
fd[1] = dup(1, -1);
fd[2] = dup(2, -1);
if(threadspawn(fd, argv[0], argv) < 0)
sysfatal("threadspawn: %r");
fd[0] = dup(0, -1);
fd[1] = dup(1, -1);
fd[2] = dup(2, -1);
if(threadspawn(fd, argv[0], argv) < 0)
sysfatal("threadspawn: %r");
w = recvp(c);
if(w == nil)
print("exec/recvp failed: %r\n");
else
print("%d %lud %lud %lud %s\n", w->pid, w->time[0], w->time[1], w->time[2], w->msg);
w = recvp(c);
if(w == nil)
print("exec/recvp failed: %r\n");
else

View file

@ -0,0 +1,41 @@
#include "u.h"
#include "libc.h"
#include "thread.h"
void
execproc(void *v)
{
int i, fd[3];
char buf[100], *args[3];
i = (int)v;
sprint(buf, "%d", i);
fd[0] = dup(0, -1);
fd[1] = dup(1, -1);
fd[2] = dup(2, -1);
args[0] = "echo";
args[1] = buf;
args[2] = nil;
threadexec(nil, fd, args[0], args);
}
void
threadmain(int argc, char **argv)
{
int i;
Channel *c;
Waitmsg *w;
ARGBEGIN{
case 'D':
break;
}ARGEND
c = threadwaitchan();
for(i=0;; i++){
proccreate(execproc, (void*)i, 16384);
w = recvp(c);
if(w == nil)
sysfatal("exec/recvp failed: %r");
}
}

View file

@ -1,45 +0,0 @@
#include <lib9.h>
#include <thread.h>
extern int _threaddebuglevel;
void
doexec(void *v)
{
int fd[3];
char **argv = v;
fd[0] = dup(0, -1);
fd[1] = dup(1, -1);
fd[2] = dup(2, -1);
threadexec(nil, fd, argv[0], argv);
print("exec failed: %r\n");
sendp(threadwaitchan(), nil);
threadexits(nil);
}
void
threadmain(int argc, char **argv)
{
Channel *c;
Waitmsg *w;
int (*mk)(void(*)(void*), void*, uint);
mk = threadcreate;
ARGBEGIN{
case 'D':
_threaddebuglevel = ~0;
break;
case 'p':
mk = proccreate;
break;
}ARGEND
c = threadwaitchan();
mk(doexec, argv, 8192);
w = recvp(c);
if(w == nil)
print("exec/recvp failed: %r\n");
else
print("%d %lud %lud %lud %s\n", w->pid, w->time[0], w->time[1], w->time[2], w->msg);
threadexits(nil);
}

View file

@ -1,23 +0,0 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
Channel *c;
void
f(void *v)
{
recvp(c);
}
void
threadmain(int argc, char **argv)
{
int i;
c = chancreate(sizeof(ulong), 0);
for(i=0;; i++){
print("%d\n", i);
proccreate(f, nil, 8192);
}
}

535
src/libthread/thread.c Normal file
View file

@ -0,0 +1,535 @@
#include "u.h"
#include <linux/unistd.h>
#include "libc.h"
#include "thread.h"
#include "threadimpl.h"
_syscall0(pid_t,gettid)
int _threaddebuglevel;
static uint threadnproc;
static uint threadnsysproc;
static Lock threadnproclock;
static Ref threadidref;
static void addthread(_Threadlist*, _Thread*);
static void delthread(_Threadlist*, _Thread*);
static void addthreadinproc(Proc*, _Thread*);
static void delthreadinproc(Proc*, _Thread*);
static void contextswitch(Context *from, Context *to);
static void scheduler(void*);
static _Thread*
getthreadnow(void)
{
return proc()->thread;
}
_Thread *(*threadnow)(void) = getthreadnow;
static Proc*
procalloc(void)
{
Proc *p;
p = malloc(sizeof *p);
memset(p, 0, sizeof *p);
lock(&threadnproclock);
threadnproc++;
unlock(&threadnproclock);
return p;
}
static void
threadstart(void *v)
{
_Thread *t;
t = v;
t->startfn(t->startarg);
_threadexit();
}
static _Thread*
threadalloc(void (*fn)(void*), void *arg, uint stack)
{
_Thread *t;
sigset_t zero;
/* allocate the task and stack together */
t = malloc(sizeof *t+stack);
memset(t, 0, sizeof *t);
t->stk = (uchar*)(t+1);
t->stksize = stack;
t->id = incref(&threadidref);
t->startfn = fn;
t->startarg = arg;
/* do a reasonable initialization */
memset(&t->context.uc, 0, sizeof t->context.uc);
sigemptyset(&zero);
sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask);
/* on Linux makecontext neglects floating point */
getcontext(&t->context.uc);
/* call makecontext to do the real work. */
/* leave a few words open on both ends */
t->context.uc.uc_stack.ss_sp = t->stk+8;
t->context.uc.uc_stack.ss_size = t->stksize-16;
makecontext(&t->context.uc, (void(*)())threadstart, 1, t);
return t;
}
_Thread*
_threadcreate(Proc *p, void (*fn)(void*), void *arg, uint stack)
{
_Thread *t;
t = threadalloc(fn, arg, stack);
t->proc = p;
addthreadinproc(p, t);
p->nthread++;
_threadready(t);
return t;
}
int
threadcreate(void (*fn)(void*), void *arg, uint stack)
{
_Thread *t;
t = _threadcreate(proc(), fn, arg, stack);
return t->id;
}
int
proccreate(void (*fn)(void*), void *arg, uint stack)
{
_Thread *t;
Proc *p;
p = procalloc();
//print("pa %p\n", p);
t = _threadcreate(p, fn, arg, stack);
//print("ps %p\n", p);
_procstart(p, scheduler);
return t->id;
}
void
_threadswitch(void)
{
Proc *p;
p = proc();
contextswitch(&p->thread->context, &p->schedcontext);
}
void
_threadready(_Thread *t)
{
Proc *p;
p = t->proc;
lock(&p->lock);
addthread(&p->runqueue, t);
_procwakeup(&p->runrend);
unlock(&p->lock);
}
void
threadyield(void)
{
_threadready(proc()->thread);
_threadswitch();
}
void
_threadexit(void)
{
proc()->thread->exiting = 1;
_threadswitch();
}
void
threadexits(char *msg)
{
/*
Proc *p;
p = proc();
utfecpy(p->msg, p->msg+sizeof p->msg, msg);
*/
_threadexit();
}
void
threadexitsall(char *msg)
{
if(msg && msg[0])
exit(1);
exit(0);
}
static void
contextswitch(Context *from, Context *to)
{
if(swapcontext(&from->uc, &to->uc) < 0){
fprint(2, "swapcontext failed: %r\n");
assert(0);
}
}
static void
scheduler(void *v)
{
_Thread *t;
Proc *p;
p = v;
setproc(p);
print("s %p %d\n", p, gettid());
p->tid = pthread_self();
pthread_detach(p->tid);
lock(&p->lock);
for(;;){
while((t = p->runqueue.head) == nil){
if(p->nthread == 0)
goto Out;
p->runrend.l = &p->lock;
_procsleep(&p->runrend);
}
delthread(&p->runqueue, t);
unlock(&p->lock);
p->thread = t;
// print("run %s %d\n", t->name, t->id);
contextswitch(&p->schedcontext, &t->context);
p->thread = nil;
lock(&p->lock);
if(t->exiting){
delthreadinproc(p, t);
p->nthread--;
free(t);
}
}
Out:
lock(&threadnproclock);
if(p->sysproc)
--threadnsysproc;
if(--threadnproc == threadnsysproc)
exit(0);
unlock(&threadnproclock);
unlock(&p->lock);
free(p);
setproc(0);
print("e %p (tid %d)\n", p, gettid());
pthread_exit(nil);
}
void
_threadsetsysproc(void)
{
lock(&threadnproclock);
if(++threadnsysproc == threadnproc)
exit(0);
unlock(&threadnproclock);
proc()->sysproc = 1;
}
/*
* debugging
*/
void
threadsetname(char *fmt, ...)
{
va_list arg;
_Thread *t;
t = proc()->thread;
va_start(arg, fmt);
vsnprint(t->name, sizeof t->name, fmt, arg);
va_end(arg);
}
void
threadsetstate(char *fmt, ...)
{
va_list arg;
_Thread *t;
t = proc()->thread;
va_start(arg, fmt);
vsnprint(t->state, sizeof t->name, fmt, arg);
va_end(arg);
}
/*
* locking
*/
static int
threadqlock(QLock *l, int block, ulong pc)
{
lock(&l->l);
if(l->owner == nil){
l->owner = (*threadnow)();
//print("qlock %p @%#x by %p\n", l, pc, l->owner);
unlock(&l->l);
return 1;
}
if(!block){
unlock(&l->l);
return 0;
}
//print("qsleep %p @%#x by %p\n", l, pc, (*threadnow)());
addthread(&l->waiting, (*threadnow)());
unlock(&l->l);
_threadswitch();
if(l->owner != (*threadnow)()){
fprint(2, "qlock pc=0x%lux owner=%p self=%p oops\n", pc, l->owner, (*threadnow)());
abort();
}
//print("qlock wakeup %p @%#x by %p\n", l, pc, (*threadnow)());
return 1;
}
static void
threadqunlock(QLock *l, ulong pc)
{
lock(&l->l);
//print("qlock unlock %p @%#x by %p (owner %p)\n", l, pc, (*threadnow)(), l->owner);
if(l->owner == nil){
fprint(2, "qunlock pc=0x%lux owner=%p self=%p oops\n",
pc, l->owner, (*threadnow)());
abort();
}
if((l->owner = l->waiting.head) != nil){
delthread(&l->waiting, l->owner);
_threadready(l->owner);
}
unlock(&l->l);
}
static int
threadrlock(RWLock *l, int block, ulong pc)
{
USED(pc);
lock(&l->l);
if(l->writer == nil && l->wwaiting.head == nil){
l->readers++;
unlock(&l->l);
return 1;
}
if(!block){
unlock(&l->l);
return 0;
}
addthread(&l->rwaiting, (*threadnow)());
unlock(&l->l);
_threadswitch();
return 1;
}
static int
threadwlock(RWLock *l, int block, ulong pc)
{
USED(pc);
lock(&l->l);
if(l->writer == nil && l->readers == 0){
l->writer = (*threadnow)();
unlock(&l->l);
return 1;
}
if(!block){
unlock(&l->l);
return 0;
}
addthread(&l->wwaiting, (*threadnow)());
unlock(&l->l);
_threadswitch();
return 1;
}
static void
threadrunlock(RWLock *l, ulong pc)
{
_Thread *t;
USED(pc);
lock(&l->l);
--l->readers;
if(l->readers == 0 && (t = l->wwaiting.head) != nil){
delthread(&l->wwaiting, t);
l->writer = t;
_threadready(t);
}
unlock(&l->l);
}
static void
threadwunlock(RWLock *l, ulong pc)
{
_Thread *t;
USED(pc);
lock(&l->l);
l->writer = nil;
assert(l->readers == 0);
while((t = l->rwaiting.head) != nil){
delthread(&l->rwaiting, t);
l->readers++;
_threadready(t);
}
if(l->readers == 0 && (t = l->wwaiting.head) != nil){
delthread(&l->wwaiting, t);
l->writer = t;
_threadready(t);
}
unlock(&l->l);
}
/*
* sleep and wakeup
*/
static void
threadrsleep(Rendez *r, ulong pc)
{
addthread(&r->waiting, proc()->thread);
qunlock(r->l);
_threadswitch();
qlock(r->l);
}
static int
threadrwakeup(Rendez *r, int all, ulong pc)
{
int i;
_Thread *t;
for(i=0;; i++){
if(i==1 && !all)
break;
if((t = r->waiting.head) == nil)
break;
delthread(&r->waiting, t);
_threadready(t);
}
return i;
}
/*
* hooray for linked lists
*/
static void
addthread(_Threadlist *l, _Thread *t)
{
if(l->tail){
l->tail->next = t;
t->prev = l->tail;
}else{
l->head = t;
t->prev = nil;
}
l->tail = t;
t->next = nil;
}
static void
delthread(_Threadlist *l, _Thread *t)
{
if(t->prev)
t->prev->next = t->next;
else
l->head = t->next;
if(t->next)
t->next->prev = t->prev;
else
l->tail = t->prev;
}
static void
addthreadinproc(Proc *p, _Thread *t)
{
_Threadlist *l;
l = &p->allthreads;
if(l->tail){
l->tail->allnext = t;
t->allprev = l->tail;
}else{
l->head = t;
t->allprev = nil;
}
l->tail = t;
t->allnext = nil;
}
static void
delthreadinproc(Proc *p, _Thread *t)
{
_Threadlist *l;
l = &p->allthreads;
if(t->allprev)
t->allprev->allnext = t->allnext;
else
l->head = t->allnext;
if(t->allnext)
t->allnext->allprev = t->allprev;
else
l->tail = t->allprev;
}
void**
procdata(void)
{
return &proc()->udata;
}
static int threadargc;
static char **threadargv;
int mainstacksize;
static void
threadmainstart(void *v)
{
USED(v);
threadmain(threadargc, threadargv);
}
int
main(int argc, char **argv)
{
Proc *p;
threadargc = argc;
threadargv = argv;
/*
* Install locking routines into C library.
*/
_lock = _threadlock;
_unlock = _threadunlock;
_qlock = threadqlock;
_qunlock = threadqunlock;
_rlock = threadrlock;
_runlock = threadrunlock;
_wlock = threadwlock;
_wunlock = threadwunlock;
_rsleep = threadrsleep;
_rwakeup = threadrwakeup;
pthreadinit();
p = procalloc();
if(mainstacksize == 0)
mainstacksize = 65536;
_threadcreate(p, threadmainstart, nil, mainstacksize);
scheduler(p);
return 0; /* not reached */
}

View file

@ -1,15 +0,0 @@
#!/bin/sh
if [ `uname` = Linux ]
then
case `uname -r` in
2.[6789]*)
echo pthread
;;
*)
echo Linux-clone
;;
esac
else
echo pthread
fi

View file

@ -1,241 +0,0 @@
/*
* Some notes on locking:
*
* All the locking woes come from implementing
* threadinterrupt (and threadkill).
*
* _threadgetproc()->thread is always a live pointer.
* p->threads, p->ready, and _threadrgrp also contain
* live thread pointers. These may only be consulted
* while holding p->lock; in procs other than p, the
* pointers are only guaranteed to be live while the lock
* is still being held.
*
* Thread structures can only be freed by the proc
* they belong to. Threads marked with t->inrendez
* need to be extracted from the _threadrgrp before
* being freed.
*/
#include <u.h>
#include <assert.h>
#include <libc.h>
#include <thread.h>
#include "label.h"
typedef struct Thread Thread;
typedef struct Proc Proc;
typedef struct Tqueue Tqueue;
typedef struct Pqueue Pqueue;
typedef struct Execargs Execargs;
typedef struct Jmp Jmp;
/* sync with ../lib9/notify.c */
struct Jmp
{
p9jmp_buf b;
};
typedef enum
{
Dead,
Running,
Ready,
Rendezvous,
} State;
typedef enum
{
Channone,
Chanalt,
Chansend,
Chanrecv,
} Chanstate;
enum
{
NPRIV = 8,
};
struct Tqueue /* Thread queue */
{
int asleep;
Thread *head;
Thread *tail;
};
struct Pqueue { /* Proc queue */
Lock lock;
Proc *head;
Proc **tail;
};
struct Thread
{
Lock lock; /* protects thread data structure */
int asleep; /* thread is in _threadsleep */
Label context; /* for context switches */
int grp; /* thread group */
Proc *homeproc; /* ``home'' proc */
int id; /* thread id */
int moribund; /* thread needs to die */
char *name; /* name of thread */
Thread *next; /* next on ready queue */
Thread *nextt; /* next on list of threads in this proc */
Proc *nextproc; /* next proc in which to run (rarely changes) */
State nextstate; /* next run state */
Proc *proc; /* proc of this thread */
Thread *prevt; /* prev on list of threads in this proc */
int ret; /* return value for Exec, Fork */
State state; /* run state */
uchar *stk; /* top of stack (lowest address of stack) */
uint stksize; /* stack size */
void* udata[NPRIV]; /* User per-thread data pointer */
/*
* for debugging only
* (could go away without impacting correct behavior):
*/
Channel *altc;
_Procrend altrend;
Chanstate chan; /* which channel operation is current */
Alt *alt; /* pointer to current alt structure (debugging) */
ulong userpc;
Channel *c;
};
struct Execargs
{
char *prog;
char **args;
int fd[2];
int *stdfd;
};
struct Proc
{
Lock lock;
Label context; /* for context switches */
Proc *link; /* in ptab */
int splhi; /* delay notes */
Thread *thread; /* running thread */
Thread *idle; /* idle thread */
int id;
int procid;
int needexec;
Execargs exec; /* exec argument */
Proc *newproc; /* fork argument */
char exitstr[ERRMAX]; /* exit status */
int internal;
int rforkflag;
int nthreads;
Tqueue threads; /* All threads of this proc */
Tqueue ready; /* Runnable threads */
Lock readylock;
int blocked; /* In a rendezvous */
int pending; /* delayed note pending */
int nonotes; /* delay notes */
uint nextID; /* ID of most recently created thread */
Proc *next; /* linked list of Procs */
Jmp sigjmp; /* for notify implementation */
void (*schedfn)(Proc*); /* function to call in scheduler */
_Procrend rend; /* sleep here for more ready threads */
void *arg; /* passed between shared and unshared stk */
char str[ERRMAX]; /* used by threadexits to avoid malloc */
char errbuf[ERRMAX]; /* errstr */
Waitmsg *waitmsg;
void* udata; /* User per-proc data pointer */
int nsched;
/*
* for debugging only
*/
int pid; /* process id */
int pthreadid; /* pthread id */
};
void _swaplabel(Label*, Label*);
Proc* _newproc(void);
int _newthread(Proc*, void(*)(void*), void*, uint, char*, int);
int _sched(void);
int _schedexec(Execargs*);
void _schedexecwait(void);
void _schedexit(Proc*);
int _schedfork(Proc*);
void _threadfree(Thread*);
void _threadscheduler(void*);
void _systhreadinit(void);
void _threadassert(char*);
void __threaddebug(ulong, char*, ...);
#define _threaddebug if(!_threaddebuglevel){}else __threaddebug
void _threadexitsall(char*);
Proc* _threadgetproc(void);
extern void _threadmultiproc(void);
Proc* _threaddelproc(void);
void _kthreadinitproc(Proc*);
void _threadsetproc(Proc*);
void _threadinitstack(Thread*, void(*)(void*), void*);
void _threadlinkmain(void);
void* _threadmalloc(long, int);
void _threadnote(void*, char*);
void _threadready(Thread*);
void _threadschednote(void);
void _threadsetidle(int);
void _threadsleep(_Procrend*);
void _threadwakeup(_Procrend*);
void _threadsignal(void);
void _threadsysfatal(char*, va_list);
long _xdec(long*);
void _xinc(long*);
void _threadremove(Proc*, Thread*);
void threadstatus(void);
void _threadefork(int[3], int[2], char*, char**);
Jmp* _threadgetjmp(void);
void _kthreadinit(void);
void _kthreadsetproc(Proc*);
Proc* _kthreadgetproc(void);
void _kthreadstartproc(Proc*);
void _kthreadexitproc(char*);
void _kthreadexitallproc(char*);
void _threadinternalproc(void);
void _threadbackgroundinit(void);
void _kmaininit(void);
extern int _threadnprocs;
extern int _threaddebuglevel;
extern char* _threadexitsallstatus;
extern Pqueue _threadpq;
extern Channel* _threadwaitchan;
#define DBGAPPL (1 << 0)
#define DBGSCHED (1 << 16)
#define DBGCHAN (1 << 17)
#define DBGREND (1 << 18)
/* #define DBGKILL (1 << 19) */
#define DBGNOTE (1 << 20)
#define DBGEXEC (1 << 21)
extern void _threadmemset(void*, int, int);
extern void _threaddebugmemset(void*, int, int);
extern int _threadprocs;
extern void _threadstacklimit(void*, void*);
extern void _procdelthread(Proc*, Thread*);
extern void _procaddthread(Proc*, Thread*);
extern void _threadmaininit(void);
extern int _threadexec(Channel*, int[3], char*, char*[], int);
extern int _kthreadexec(Channel*, int[3], char*, char*[], int);

View file

@ -1,68 +0,0 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
enum
{
STACK = 8192
};
int quiet;
int goal;
int buffer;
int (*fn)(void(*)(void*), void*, uint) = threadcreate;
void
primethread(void *arg)
{
Channel *c, *nc;
int p, i;
c = arg;
p = recvul(c);
if(p > goal)
threadexitsall(nil);
if(!quiet)
print("%d\n", p);
nc = chancreate(sizeof(ulong), buffer);
(*fn)(primethread, nc, STACK);
for(;;){
i = recvul(c);
if(i%p)
sendul(nc, i);
}
}
extern int _threaddebuglevel;
void
threadmain(int argc, char **argv)
{
int i;
Channel *c;
ARGBEGIN{
case 'D':
_threaddebuglevel = atoi(ARGF());
break;
case 'q':
quiet = 1;
break;
case 'b':
buffer = atoi(ARGF());
break;
case 'p':
fn = proccreate;
break;
}ARGEND
if(argc>0)
goal = atoi(argv[0]);
else
goal = 100;
c = chancreate(sizeof(ulong), buffer);
(*fn)(primethread, c, STACK);
for(i=2;; i++)
sendul(c, i);
}

View file

@ -1,31 +0,0 @@
#include <lib9.h>
#include <thread.h>
Channel *c[3];
void
pingpong(void *v)
{
int n;
Channel **c;
c = v;
do{
n = recvul(c[0]);
sendul(c[1], n-1);
}while(n > 0);
exit(0);
}
void
threadmain(int argc, char **argv)
{
c[0] = chancreate(sizeof(ulong), 1);
c[1] = chancreate(sizeof(ulong), 1);
c[2] = c[0];
proccreate(pingpong, c, 16384);
threadcreate(pingpong, c+1, 16384);
sendul(c[0], atoi(argv[1]));
}

View file

@ -1,43 +0,0 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
extern int _threaddebuglevel;
void
usage(void)
{
fprint(2, "usage: tsignal [-[ednf] note]*\n");
threadexitsall("usage");
}
void
threadmain(int argc, char **argv)
{
Channel *c;
char *msg;
ARGBEGIN{
case 'D':
_threaddebuglevel = ~0;
break;
default:
usage();
case 'e':
notifyenable(EARGF(usage()));
break;
case 'd':
notifydisable(EARGF(usage()));
break;
case 'n':
notifyon(EARGF(usage()));
break;
case 'f':
notifyoff(EARGF(usage()));
break;
}ARGEND
c = threadnotechan();
while((msg = recvp(c)) != nil)
print("note: %s\n", msg);
}

View file

@ -1,49 +0,0 @@
#include "threadimpl.h"
static void
launcher(void (*f)(void*), void *arg)
{
f(arg);
threadexits(nil);
}
void
_threadinitstack(Thread *t, void (*f)(void*), void *arg)
{
sigset_t zero;
/* do a reasonable initialization */
memset(&t->context.uc, 0, sizeof t->context.uc);
sigemptyset(&zero);
sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask);
/* call getcontext, because on Linux makecontext neglects floating point */
getcontext(&t->context.uc);
/* call makecontext to do the real work. */
/* leave a few words open on both ends */
t->context.uc.uc_stack.ss_sp = t->stk+8;
t->context.uc.uc_stack.ss_size = t->stksize-16;
makecontext(&t->context.uc, (void(*)())launcher, 2, f, 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");
}