New thread library
This commit is contained in:
parent
7788fd5409
commit
1544f90960
70 changed files with 1041 additions and 4684 deletions
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -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>.
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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>.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
#
|
||||
|
|
@ -1 +0,0 @@
|
|||
.include "asm-FreeBSD-386.s"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
#
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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--;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
153
src/libthread/exec.c
Normal 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");
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#include "threadimpl.h"
|
||||
extern int getfforkid(void);
|
||||
int
|
||||
_threadgetpid(void)
|
||||
{
|
||||
return getfforkid();
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
178
src/libthread/iorw.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#include "threadimpl.h"
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
_threadmemset(void *v, int c, int n)
|
||||
{
|
||||
memset(v, c, n);
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#include "threadimpl.h"
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
_threaddebugmemset(void *v, int c, int n)
|
||||
{
|
||||
memset(v, c, n);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
3
src/libthread/qlock.c
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#include "u.h"
|
||||
#include "libc.h"
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
38
src/libthread/test/pthreadloop.c
Normal file
38
src/libthread/test/pthreadloop.c
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/libthread/test/tprimes.c
Normal file
80
src/libthread/test/tprimes.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
41
src/libthread/test/tspawnloop.c
Normal file
41
src/libthread/test/tspawnloop.c
Normal 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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
535
src/libthread/thread.c
Normal 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 */
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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]));
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue