mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-12 11:10:07 +00:00
libthread: remove Linux 2.4 code
Linux.c was for Linux 2.4 and is no longer used directly, only indirectly because NetBSD.c was a 1-line file #including Linux.c. So mv Linux.c NetBSD.c. Also rm Linux-*-asm.s which was for Linux 2.4 as well.
This commit is contained in:
parent
c181e39eea
commit
8d82ccefd2
7 changed files with 462 additions and 513 deletions
|
@ -1,7 +0,0 @@
|
|||
.globl _tas
|
||||
_tas:
|
||||
movl $0xCAFEBABE, %eax
|
||||
movl 4(%esp), %ecx
|
||||
xchgl %eax, 0(%ecx)
|
||||
ret
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
.globl _tas
|
||||
_tas:
|
||||
mov r3, #0xCA000000
|
||||
add r3, r3, #0xFE0000
|
||||
add r3, r3, #0xBA00
|
||||
add r3, r3, #0xBE
|
||||
swp r3, r3, [r0]
|
||||
mov r0, r3
|
||||
mov pc, lr
|
|
@ -1,16 +0,0 @@
|
|||
.globl _tas
|
||||
_tas:
|
||||
li %r0, 0
|
||||
mr %r4, %r3
|
||||
lis %r5, 0xcafe
|
||||
ori %r5, %r5, 0xbabe
|
||||
1:
|
||||
lwarx %r3, %r0, %r4
|
||||
cmpwi %r3, 0
|
||||
bne 2f
|
||||
stwcx. %r5, %r0, %r4
|
||||
bne- 1b
|
||||
2:
|
||||
sync
|
||||
blr
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
! Actually sparc32 assembly.
|
||||
! Debian's sparc64 port is a 32-bit user space.
|
||||
|
||||
.section ".text", #alloc, #execinstr
|
||||
.align 8
|
||||
.skip 16
|
||||
.global _tas
|
||||
! .type _tas,2
|
||||
_tas:
|
||||
or %g0,1,%o1
|
||||
swap [%o0],%o1 ! o0 points to lock; key is first word
|
||||
retl
|
||||
mov %o1, %o0
|
||||
|
||||
.size _tas,(.-_tas)
|
||||
|
|
@ -1,462 +0,0 @@
|
|||
#include "threadimpl.h"
|
||||
|
||||
#undef exits
|
||||
#undef _exits
|
||||
|
||||
static int
|
||||
timefmt(Fmt *fmt)
|
||||
{
|
||||
static char *mon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
vlong ns;
|
||||
Tm tm;
|
||||
ns = nsec();
|
||||
tm = *localtime(time(0));
|
||||
return fmtprint(fmt, "%s %2d %02d:%02d:%02d.%03d",
|
||||
mon[tm.mon], tm.mday, tm.hour, tm.min, tm.sec,
|
||||
(int)(ns%1000000000)/1000000);
|
||||
}
|
||||
|
||||
/*
|
||||
* spin locks
|
||||
*/
|
||||
extern int _tas(int*);
|
||||
|
||||
void
|
||||
_threadunlock(Lock *l, ulong pc)
|
||||
{
|
||||
USED(pc);
|
||||
|
||||
l->held = 0;
|
||||
}
|
||||
|
||||
int
|
||||
_threadlock(Lock *l, int block, ulong pc)
|
||||
{
|
||||
int i;
|
||||
static int first=1;
|
||||
if(first) {first=0; fmtinstall('\001', timefmt);}
|
||||
|
||||
USED(pc);
|
||||
|
||||
/* once fast */
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
if(!block)
|
||||
return 0;
|
||||
|
||||
/* a thousand times pretty fast */
|
||||
for(i=0; i<1000; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
sched_yield();
|
||||
}
|
||||
/* now increasingly slow */
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(1);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop1 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(10);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop2 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(100);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop3 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(1000);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop4 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(10*1000);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop5 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<1000; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(100*1000);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop6 %p from %lux\n", argv0, l, pc);
|
||||
/* take your time */
|
||||
while(_tas(&l->held))
|
||||
usleep(1000*1000);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* sleep and wakeup
|
||||
*/
|
||||
static void
|
||||
ign(int x)
|
||||
{
|
||||
USED(x);
|
||||
}
|
||||
|
||||
static void /*__attribute__((constructor))*/
|
||||
ignusr1(int restart)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof sa);
|
||||
sa.sa_handler = ign;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaddset(&sa.sa_mask, SIGUSR1);
|
||||
if(restart)
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigaction(SIGUSR1, &sa, nil);
|
||||
}
|
||||
|
||||
void
|
||||
_procsleep(_Procrendez *r)
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
/*
|
||||
* Go to sleep.
|
||||
*
|
||||
* Block USR1, set the handler to interrupt system calls,
|
||||
* unlock the vouslock so our waker can wake us,
|
||||
* and then suspend.
|
||||
*/
|
||||
again:
|
||||
r->asleep = 1;
|
||||
r->pid = getpid();
|
||||
|
||||
sigprocmask(SIG_SETMASK, nil, &mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
sigprocmask(SIG_SETMASK, &mask, nil);
|
||||
ignusr1(0);
|
||||
unlock(r->l);
|
||||
sigdelset(&mask, SIGUSR1);
|
||||
sigsuspend(&mask);
|
||||
|
||||
/*
|
||||
* We're awake. Make USR1 not interrupt system calls.
|
||||
*/
|
||||
lock(r->l);
|
||||
ignusr1(1);
|
||||
if(r->asleep && r->pid == getpid()){
|
||||
/* Didn't really wake up - signal from something else */
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_procwakeupandunlock(_Procrendez *r)
|
||||
{
|
||||
int pid;
|
||||
|
||||
pid = 0;
|
||||
if(r->asleep){
|
||||
r->asleep = 0;
|
||||
assert(r->pid >= 1);
|
||||
pid = r->pid;
|
||||
}
|
||||
assert(r->l);
|
||||
unlock(r->l);
|
||||
if(pid)
|
||||
kill(pid, SIGUSR1);
|
||||
}
|
||||
|
||||
/*
|
||||
* process creation and exit
|
||||
*/
|
||||
typedef struct Stackfree Stackfree;
|
||||
struct Stackfree
|
||||
{
|
||||
Stackfree *next;
|
||||
int pid;
|
||||
int pid1;
|
||||
};
|
||||
static Lock stacklock;
|
||||
static Stackfree *stackfree;
|
||||
|
||||
static void
|
||||
delayfreestack(uchar *stk, int pid, int pid1)
|
||||
{
|
||||
Stackfree *sf;
|
||||
|
||||
sf = (Stackfree*)stk;
|
||||
sf->pid = pid;
|
||||
sf->pid1 = pid1;
|
||||
lock(&stacklock);
|
||||
sf->next = stackfree;
|
||||
stackfree = sf;
|
||||
unlock(&stacklock);
|
||||
}
|
||||
|
||||
static void
|
||||
dofreestacks(void)
|
||||
{
|
||||
Stackfree *sf, *last, *next;
|
||||
|
||||
if(stackfree==nil || !canlock(&stacklock))
|
||||
return;
|
||||
|
||||
for(last=nil,sf=stackfree; sf; last=sf,sf=next){
|
||||
next = sf->next;
|
||||
if(sf->pid >= 1 && kill(sf->pid, 0) < 0 && errno == ESRCH)
|
||||
if(sf->pid1 >= 1 && kill(sf->pid1, 0) < 0 && errno == ESRCH){
|
||||
free(sf);
|
||||
if(last)
|
||||
last->next = next;
|
||||
else
|
||||
stackfree = next;
|
||||
sf = last;
|
||||
}
|
||||
}
|
||||
unlock(&stacklock);
|
||||
}
|
||||
|
||||
static int
|
||||
startprocfn(void *v)
|
||||
{
|
||||
void **a;
|
||||
uchar *stk;
|
||||
void (*fn)(void*);
|
||||
Proc *p;
|
||||
int pid0, pid1;
|
||||
|
||||
a = (void**)v;
|
||||
fn = a[0];
|
||||
p = a[1];
|
||||
stk = a[2];
|
||||
pid0 = (int)a[4];
|
||||
pid1 = getpid();
|
||||
free(a);
|
||||
p->osprocid = pid1;
|
||||
|
||||
(*fn)(p);
|
||||
|
||||
delayfreestack(stk, pid0, pid1);
|
||||
_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* indirect through here so that parent need not wait for child zombie
|
||||
*
|
||||
* slight race - if child exits and then another process starts before we
|
||||
* manage to exit, we'll be running on a freed stack.
|
||||
*/
|
||||
static int
|
||||
trampnowait(void *v)
|
||||
{
|
||||
void **a;
|
||||
int *kidpid;
|
||||
|
||||
a = (void*)v;
|
||||
kidpid = a[3];
|
||||
a[4] = (void*)getpid();
|
||||
*kidpid = clone(startprocfn, a[2]+65536-512, CLONE_VM|CLONE_FILES, a);
|
||||
_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_procstart(Proc *p, void (*fn)(Proc*))
|
||||
{
|
||||
void **a;
|
||||
uchar *stk;
|
||||
int pid, kidpid, status;
|
||||
|
||||
dofreestacks();
|
||||
a = malloc(5*sizeof a[0]);
|
||||
if(a == nil)
|
||||
sysfatal("_procstart malloc: %r");
|
||||
stk = malloc(65536);
|
||||
if(stk == nil)
|
||||
sysfatal("_procstart malloc stack: %r");
|
||||
|
||||
a[0] = fn;
|
||||
a[1] = p;
|
||||
a[2] = stk;
|
||||
a[3] = &kidpid;
|
||||
kidpid = -1;
|
||||
|
||||
pid = clone(trampnowait, stk+65536-16, CLONE_VM|CLONE_FILES, a);
|
||||
if(pid > 0)
|
||||
if(wait4(pid, &status, __WALL, 0) < 0)
|
||||
fprint(2, "ffork wait4: %r\n");
|
||||
if(pid < 0 || kidpid < 0){
|
||||
fprint(2, "_procstart clone: %r\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static char *threadexitsmsg;
|
||||
void
|
||||
sigusr2handler(int s)
|
||||
{
|
||||
/* fprint(2, "%d usr2 %d\n", time(0), getpid()); */
|
||||
if(threadexitsmsg)
|
||||
_exits(threadexitsmsg);
|
||||
}
|
||||
|
||||
void
|
||||
threadexitsall(char *msg)
|
||||
{
|
||||
static int pid[1024];
|
||||
int i, npid, mypid;
|
||||
Proc *p;
|
||||
|
||||
if(msg == nil)
|
||||
msg = "";
|
||||
|
||||
/*
|
||||
* Only one guy, ever, gets to run this.
|
||||
* If two guys do it, inevitably they end up
|
||||
* tripping over each other in the underlying
|
||||
* C library exit() implementation, which is
|
||||
* trying to run the atexit handlers and apparently
|
||||
* not thread safe. This has been observed on
|
||||
* both Linux and OpenBSD. Sigh.
|
||||
*/
|
||||
{
|
||||
static Lock onelock;
|
||||
if(!canlock(&onelock))
|
||||
_exits(threadexitsmsg);
|
||||
threadexitsmsg = msg;
|
||||
}
|
||||
|
||||
mypid = getpid();
|
||||
lock(&_threadprocslock);
|
||||
npid = 0;
|
||||
for(p=_threadprocs; p; p=p->next)
|
||||
if(p->osprocid != mypid && p->osprocid >= 1)
|
||||
pid[npid++] = p->osprocid;
|
||||
for(i=0; i<npid; i++)
|
||||
kill(pid[i], SIGUSR2);
|
||||
unlock(&_threadprocslock);
|
||||
exits(msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* per-process data, indexed by pid
|
||||
*
|
||||
* could use modify_ldt and a segment register
|
||||
* to avoid the many calls to getpid(), but i don't
|
||||
* care -- this is compatibility code. linux 2.6 with
|
||||
* nptl is a good enough pthreads to avoid this whole file.
|
||||
*/
|
||||
typedef struct Perproc Perproc;
|
||||
struct Perproc
|
||||
{
|
||||
int pid;
|
||||
Proc *proc;
|
||||
};
|
||||
|
||||
static Lock perlock;
|
||||
static Perproc perproc[1024];
|
||||
#define P ((Proc*)-1)
|
||||
|
||||
static Perproc*
|
||||
myperproc(void)
|
||||
{
|
||||
int i, pid, h;
|
||||
Perproc *p;
|
||||
|
||||
pid = getpid();
|
||||
h = pid%nelem(perproc);
|
||||
for(i=0; i<nelem(perproc); i++){
|
||||
p = &perproc[(i+h)%nelem(perproc)];
|
||||
if(p->pid == pid)
|
||||
return p;
|
||||
if(p->pid == 0){
|
||||
print("found 0 at %d (h=%d)\n", (i+h)%nelem(perproc), h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprint(2, "myperproc %d (%s): cannot find self\n", pid, argv0);
|
||||
abort();
|
||||
return nil;
|
||||
}
|
||||
|
||||
static Perproc*
|
||||
newperproc(void)
|
||||
{
|
||||
int i, pid, h;
|
||||
Perproc *p;
|
||||
|
||||
lock(&perlock);
|
||||
pid = getpid();
|
||||
h = pid%nelem(perproc);
|
||||
for(i=0; i<nelem(perproc); i++){
|
||||
p = &perproc[(i+h)%nelem(perproc)];
|
||||
if(p->pid == pid || p->pid == -1 || p->pid == 0){
|
||||
p->pid = pid;
|
||||
unlock(&perlock);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
fprint(2, "newperproc %d: out of procs\n", pid);
|
||||
abort();
|
||||
return nil;
|
||||
}
|
||||
|
||||
Proc*
|
||||
_threadproc(void)
|
||||
{
|
||||
return myperproc()->proc;
|
||||
}
|
||||
|
||||
void
|
||||
_threadsetproc(Proc *p)
|
||||
{
|
||||
Perproc *pp;
|
||||
|
||||
if(p)
|
||||
p->osprocid = getpid();
|
||||
pp = newperproc();
|
||||
pp->proc = p;
|
||||
if(p == nil)
|
||||
pp->pid = -1;
|
||||
}
|
||||
|
||||
void
|
||||
_pthreadinit(void)
|
||||
{
|
||||
signal(SIGUSR2, sigusr2handler);
|
||||
}
|
||||
|
||||
void
|
||||
_threadpexit(void)
|
||||
{
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
#ifdef __arm__
|
||||
void
|
||||
makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...)
|
||||
{
|
||||
int i, *sp;
|
||||
va_list arg;
|
||||
|
||||
sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4;
|
||||
va_start(arg, argc);
|
||||
for(i=0; i<4 && i<argc; i++)
|
||||
uc->uc_mcontext.gregs[i] = va_arg(arg, uint);
|
||||
va_end(arg);
|
||||
uc->uc_mcontext.gregs[13] = (uint)sp;
|
||||
uc->uc_mcontext.gregs[14] = (uint)fn;
|
||||
}
|
||||
|
||||
int
|
||||
swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
|
||||
{
|
||||
if(getcontext(oucp) == 0)
|
||||
setcontext(ucp);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -1 +1,462 @@
|
|||
#include "Linux.c"
|
||||
#include "threadimpl.h"
|
||||
|
||||
#undef exits
|
||||
#undef _exits
|
||||
|
||||
static int
|
||||
timefmt(Fmt *fmt)
|
||||
{
|
||||
static char *mon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
vlong ns;
|
||||
Tm tm;
|
||||
ns = nsec();
|
||||
tm = *localtime(time(0));
|
||||
return fmtprint(fmt, "%s %2d %02d:%02d:%02d.%03d",
|
||||
mon[tm.mon], tm.mday, tm.hour, tm.min, tm.sec,
|
||||
(int)(ns%1000000000)/1000000);
|
||||
}
|
||||
|
||||
/*
|
||||
* spin locks
|
||||
*/
|
||||
extern int _tas(int*);
|
||||
|
||||
void
|
||||
_threadunlock(Lock *l, ulong pc)
|
||||
{
|
||||
USED(pc);
|
||||
|
||||
l->held = 0;
|
||||
}
|
||||
|
||||
int
|
||||
_threadlock(Lock *l, int block, ulong pc)
|
||||
{
|
||||
int i;
|
||||
static int first=1;
|
||||
if(first) {first=0; fmtinstall('\001', timefmt);}
|
||||
|
||||
USED(pc);
|
||||
|
||||
/* once fast */
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
if(!block)
|
||||
return 0;
|
||||
|
||||
/* a thousand times pretty fast */
|
||||
for(i=0; i<1000; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
sched_yield();
|
||||
}
|
||||
/* now increasingly slow */
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(1);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop1 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(10);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop2 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(100);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop3 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(1000);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop4 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<10; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(10*1000);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop5 %p from %lux\n", argv0, l, pc);
|
||||
for(i=0; i<1000; i++){
|
||||
if(!_tas(&l->held))
|
||||
return 1;
|
||||
usleep(100*1000);
|
||||
}
|
||||
fprint(2, "%\001 %s: lock loop6 %p from %lux\n", argv0, l, pc);
|
||||
/* take your time */
|
||||
while(_tas(&l->held))
|
||||
usleep(1000*1000);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* sleep and wakeup
|
||||
*/
|
||||
static void
|
||||
ign(int x)
|
||||
{
|
||||
USED(x);
|
||||
}
|
||||
|
||||
static void /*__attribute__((constructor))*/
|
||||
ignusr1(int restart)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof sa);
|
||||
sa.sa_handler = ign;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaddset(&sa.sa_mask, SIGUSR1);
|
||||
if(restart)
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigaction(SIGUSR1, &sa, nil);
|
||||
}
|
||||
|
||||
void
|
||||
_procsleep(_Procrendez *r)
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
/*
|
||||
* Go to sleep.
|
||||
*
|
||||
* Block USR1, set the handler to interrupt system calls,
|
||||
* unlock the vouslock so our waker can wake us,
|
||||
* and then suspend.
|
||||
*/
|
||||
again:
|
||||
r->asleep = 1;
|
||||
r->pid = getpid();
|
||||
|
||||
sigprocmask(SIG_SETMASK, nil, &mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
sigprocmask(SIG_SETMASK, &mask, nil);
|
||||
ignusr1(0);
|
||||
unlock(r->l);
|
||||
sigdelset(&mask, SIGUSR1);
|
||||
sigsuspend(&mask);
|
||||
|
||||
/*
|
||||
* We're awake. Make USR1 not interrupt system calls.
|
||||
*/
|
||||
lock(r->l);
|
||||
ignusr1(1);
|
||||
if(r->asleep && r->pid == getpid()){
|
||||
/* Didn't really wake up - signal from something else */
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_procwakeupandunlock(_Procrendez *r)
|
||||
{
|
||||
int pid;
|
||||
|
||||
pid = 0;
|
||||
if(r->asleep){
|
||||
r->asleep = 0;
|
||||
assert(r->pid >= 1);
|
||||
pid = r->pid;
|
||||
}
|
||||
assert(r->l);
|
||||
unlock(r->l);
|
||||
if(pid)
|
||||
kill(pid, SIGUSR1);
|
||||
}
|
||||
|
||||
/*
|
||||
* process creation and exit
|
||||
*/
|
||||
typedef struct Stackfree Stackfree;
|
||||
struct Stackfree
|
||||
{
|
||||
Stackfree *next;
|
||||
int pid;
|
||||
int pid1;
|
||||
};
|
||||
static Lock stacklock;
|
||||
static Stackfree *stackfree;
|
||||
|
||||
static void
|
||||
delayfreestack(uchar *stk, int pid, int pid1)
|
||||
{
|
||||
Stackfree *sf;
|
||||
|
||||
sf = (Stackfree*)stk;
|
||||
sf->pid = pid;
|
||||
sf->pid1 = pid1;
|
||||
lock(&stacklock);
|
||||
sf->next = stackfree;
|
||||
stackfree = sf;
|
||||
unlock(&stacklock);
|
||||
}
|
||||
|
||||
static void
|
||||
dofreestacks(void)
|
||||
{
|
||||
Stackfree *sf, *last, *next;
|
||||
|
||||
if(stackfree==nil || !canlock(&stacklock))
|
||||
return;
|
||||
|
||||
for(last=nil,sf=stackfree; sf; last=sf,sf=next){
|
||||
next = sf->next;
|
||||
if(sf->pid >= 1 && kill(sf->pid, 0) < 0 && errno == ESRCH)
|
||||
if(sf->pid1 >= 1 && kill(sf->pid1, 0) < 0 && errno == ESRCH){
|
||||
free(sf);
|
||||
if(last)
|
||||
last->next = next;
|
||||
else
|
||||
stackfree = next;
|
||||
sf = last;
|
||||
}
|
||||
}
|
||||
unlock(&stacklock);
|
||||
}
|
||||
|
||||
static int
|
||||
startprocfn(void *v)
|
||||
{
|
||||
void **a;
|
||||
uchar *stk;
|
||||
void (*fn)(void*);
|
||||
Proc *p;
|
||||
int pid0, pid1;
|
||||
|
||||
a = (void**)v;
|
||||
fn = a[0];
|
||||
p = a[1];
|
||||
stk = a[2];
|
||||
pid0 = (int)a[4];
|
||||
pid1 = getpid();
|
||||
free(a);
|
||||
p->osprocid = pid1;
|
||||
|
||||
(*fn)(p);
|
||||
|
||||
delayfreestack(stk, pid0, pid1);
|
||||
_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* indirect through here so that parent need not wait for child zombie
|
||||
*
|
||||
* slight race - if child exits and then another process starts before we
|
||||
* manage to exit, we'll be running on a freed stack.
|
||||
*/
|
||||
static int
|
||||
trampnowait(void *v)
|
||||
{
|
||||
void **a;
|
||||
int *kidpid;
|
||||
|
||||
a = (void*)v;
|
||||
kidpid = a[3];
|
||||
a[4] = (void*)getpid();
|
||||
*kidpid = clone(startprocfn, a[2]+65536-512, CLONE_VM|CLONE_FILES, a);
|
||||
_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_procstart(Proc *p, void (*fn)(Proc*))
|
||||
{
|
||||
void **a;
|
||||
uchar *stk;
|
||||
int pid, kidpid, status;
|
||||
|
||||
dofreestacks();
|
||||
a = malloc(5*sizeof a[0]);
|
||||
if(a == nil)
|
||||
sysfatal("_procstart malloc: %r");
|
||||
stk = malloc(65536);
|
||||
if(stk == nil)
|
||||
sysfatal("_procstart malloc stack: %r");
|
||||
|
||||
a[0] = fn;
|
||||
a[1] = p;
|
||||
a[2] = stk;
|
||||
a[3] = &kidpid;
|
||||
kidpid = -1;
|
||||
|
||||
pid = clone(trampnowait, stk+65536-16, CLONE_VM|CLONE_FILES, a);
|
||||
if(pid > 0)
|
||||
if(wait4(pid, &status, __WALL, 0) < 0)
|
||||
fprint(2, "ffork wait4: %r\n");
|
||||
if(pid < 0 || kidpid < 0){
|
||||
fprint(2, "_procstart clone: %r\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static char *threadexitsmsg;
|
||||
void
|
||||
sigusr2handler(int s)
|
||||
{
|
||||
/* fprint(2, "%d usr2 %d\n", time(0), getpid()); */
|
||||
if(threadexitsmsg)
|
||||
_exits(threadexitsmsg);
|
||||
}
|
||||
|
||||
void
|
||||
threadexitsall(char *msg)
|
||||
{
|
||||
static int pid[1024];
|
||||
int i, npid, mypid;
|
||||
Proc *p;
|
||||
|
||||
if(msg == nil)
|
||||
msg = "";
|
||||
|
||||
/*
|
||||
* Only one guy, ever, gets to run this.
|
||||
* If two guys do it, inevitably they end up
|
||||
* tripping over each other in the underlying
|
||||
* C library exit() implementation, which is
|
||||
* trying to run the atexit handlers and apparently
|
||||
* not thread safe. This has been observed on
|
||||
* both Linux and OpenBSD. Sigh.
|
||||
*/
|
||||
{
|
||||
static Lock onelock;
|
||||
if(!canlock(&onelock))
|
||||
_exits(threadexitsmsg);
|
||||
threadexitsmsg = msg;
|
||||
}
|
||||
|
||||
mypid = getpid();
|
||||
lock(&_threadprocslock);
|
||||
npid = 0;
|
||||
for(p=_threadprocs; p; p=p->next)
|
||||
if(p->osprocid != mypid && p->osprocid >= 1)
|
||||
pid[npid++] = p->osprocid;
|
||||
for(i=0; i<npid; i++)
|
||||
kill(pid[i], SIGUSR2);
|
||||
unlock(&_threadprocslock);
|
||||
exits(msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* per-process data, indexed by pid
|
||||
*
|
||||
* could use modify_ldt and a segment register
|
||||
* to avoid the many calls to getpid(), but i don't
|
||||
* care -- this is compatibility code. linux 2.6 with
|
||||
* nptl is a good enough pthreads to avoid this whole file.
|
||||
*/
|
||||
typedef struct Perproc Perproc;
|
||||
struct Perproc
|
||||
{
|
||||
int pid;
|
||||
Proc *proc;
|
||||
};
|
||||
|
||||
static Lock perlock;
|
||||
static Perproc perproc[1024];
|
||||
#define P ((Proc*)-1)
|
||||
|
||||
static Perproc*
|
||||
myperproc(void)
|
||||
{
|
||||
int i, pid, h;
|
||||
Perproc *p;
|
||||
|
||||
pid = getpid();
|
||||
h = pid%nelem(perproc);
|
||||
for(i=0; i<nelem(perproc); i++){
|
||||
p = &perproc[(i+h)%nelem(perproc)];
|
||||
if(p->pid == pid)
|
||||
return p;
|
||||
if(p->pid == 0){
|
||||
print("found 0 at %d (h=%d)\n", (i+h)%nelem(perproc), h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprint(2, "myperproc %d (%s): cannot find self\n", pid, argv0);
|
||||
abort();
|
||||
return nil;
|
||||
}
|
||||
|
||||
static Perproc*
|
||||
newperproc(void)
|
||||
{
|
||||
int i, pid, h;
|
||||
Perproc *p;
|
||||
|
||||
lock(&perlock);
|
||||
pid = getpid();
|
||||
h = pid%nelem(perproc);
|
||||
for(i=0; i<nelem(perproc); i++){
|
||||
p = &perproc[(i+h)%nelem(perproc)];
|
||||
if(p->pid == pid || p->pid == -1 || p->pid == 0){
|
||||
p->pid = pid;
|
||||
unlock(&perlock);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
fprint(2, "newperproc %d: out of procs\n", pid);
|
||||
abort();
|
||||
return nil;
|
||||
}
|
||||
|
||||
Proc*
|
||||
_threadproc(void)
|
||||
{
|
||||
return myperproc()->proc;
|
||||
}
|
||||
|
||||
void
|
||||
_threadsetproc(Proc *p)
|
||||
{
|
||||
Perproc *pp;
|
||||
|
||||
if(p)
|
||||
p->osprocid = getpid();
|
||||
pp = newperproc();
|
||||
pp->proc = p;
|
||||
if(p == nil)
|
||||
pp->pid = -1;
|
||||
}
|
||||
|
||||
void
|
||||
_pthreadinit(void)
|
||||
{
|
||||
signal(SIGUSR2, sigusr2handler);
|
||||
}
|
||||
|
||||
void
|
||||
_threadpexit(void)
|
||||
{
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
#ifdef __arm__
|
||||
void
|
||||
makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...)
|
||||
{
|
||||
int i, *sp;
|
||||
va_list arg;
|
||||
|
||||
sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4;
|
||||
va_start(arg, argc);
|
||||
for(i=0; i<4 && i<argc; i++)
|
||||
uc->uc_mcontext.gregs[i] = va_arg(arg, uint);
|
||||
va_end(arg);
|
||||
uc->uc_mcontext.gregs[13] = (uint)sp;
|
||||
uc->uc_mcontext.gregs[14] = (uint)fn;
|
||||
}
|
||||
|
||||
int
|
||||
swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
|
||||
{
|
||||
if(getcontext(oucp) == 0)
|
||||
setcontext(ucp);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -16,7 +16,6 @@ OFILES=\
|
|||
<$PLAN9/src/mksyslib
|
||||
|
||||
HFILES=thread.h threadimpl.h
|
||||
NetBSD.$O: Linux.c
|
||||
|
||||
tprimes: test/tprimes.$O
|
||||
9l -o $target test/$target.$O
|
||||
|
|
Loading…
Reference in a new issue