more thread work

This commit is contained in:
rsc 2004-11-08 16:03:20 +00:00
parent 77dcf88474
commit 1956455367
12 changed files with 294 additions and 340 deletions

View file

@ -2,53 +2,36 @@
#include <errno.h>
#include "threadimpl.h"
static int multi;
static Proc *theproc;
/*
* Basic kernel thread management.
*/
static pthread_key_t key;
/*
* Called before we go multiprocess.
*/
void
_threadmultiproc(void)
_kthreadinit(void)
{
if(multi == 0){
multi = 1;
pthread_key_create(&key, 0);
_threadsetproc(theproc);
}
pthread_key_create(&key, 0);
}
/*
* Set the proc for the current pthread.
*/
void
_threadsetproc(Proc *p)
_kthreadsetproc(Proc *p)
{
if(!multi){
theproc = p;
return;
}
sigset_t all;
p->pthreadid = pthread_self();
sigfillset(&all);
pthread_sigmask(SIG_SETMASK, &all, nil);
pthread_setspecific(key, p);
}
/*
* Get the proc for the current pthread.
*/
Proc*
_threadgetproc(void)
_kthreadgetproc(void)
{
if(!multi)
return theproc;
return pthread_getspecific(key);
}
/*
* Called to start a new proc.
*/
void
_threadstartproc(Proc *p)
_kthreadstartproc(Proc *p)
{
Proc *np;
pthread_t tid;
@ -63,69 +46,43 @@ _threadstartproc(Proc *p)
np->pthreadid = tid;
}
/*
* Called to associate p with the current pthread.
*/
void
_threadinitproc(Proc *p)
{
p->pthreadid = pthread_self();
_threadsetproc(p);
}
/*
* Called to exit the current pthread.
*/
void
_threadexitproc(char *exitstr)
_kthreadexitproc(char *exitstr)
{
_threaddebug(DBGSCHED, "_pthreadexit");
pthread_exit(nil);
}
/*
* Called to exit all pthreads.
*/
void
_threadexitallproc(char *exitstr)
_kthreadexitallproc(char *exitstr)
{
_threaddebug(DBGSCHED, "_threadexitallproc");
exits(exitstr);
}
/*
* Called to poll for any kids of this pthread.
* Wait messages aren't restricted to a particular
* pthread, so we have a separate proc responsible
* for them. So this is a no-op.
* 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.
*/
void
_threadwaitkids(Proc *p)
{
}
static Channel *_threadexecwaitchan;
/*
* Separate process to wait for child messages.
* Also runs signal handlers.
*/
static Channel *_threadexecchan;
static void
_threadwaitproc(void *v)
{
Channel *c;
Waitmsg *w;
sigset_t none;
sigemptyset(&none);
pthread_sigmask(SIG_SETMASK, &none, 0);
_threadinternalproc();
USED(v);
for(;;){
w = wait();
if(w == nil){
if(errno == ECHILD)
recvul(_threadexecchan);
if(errno == ECHILD) /* wait for more */
recvul(_threadexecwaitchan);
continue;
}
if((c = _threadwaitchan) != nil)
@ -133,43 +90,182 @@ _threadwaitproc(void *v)
else
free(w);
}
fprint(2, "_threadwaitproc exits\n");
fprint(2, "_threadwaitproc exits\n"); /* not reached */
}
/*
* Called before the first exec.
* Call _threadexec in the right conditions.
*/
void
_threadfirstexec(void)
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;
}
/*
* Called from mainlauncher before threadmain.
* 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.
*/
void
_threadmaininit(void)
{
_threadexecchan = chancreate(sizeof(ulong), 1);
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;
/* print("change %d -> %d\n", _threadnprocs+1, _threadnprocs); */
unlock(&_threadpq.lock);
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");
}
/*
* Called after forking the exec child.
* Notes.
*/
void
_threadafterexec(void)
Channel *_threadnotechan;
static ulong sigs;
static Lock _threadnotelk;
static void _threadnoteproc(void*);
extern int _p9strsig(char*);
extern char *_p9sigstr(int);
Channel*
threadnotechan(void)
{
nbsendul(_threadexecchan, 1);
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);
}