blob: 83d8b023576d6e0f3d0b0f4de42a7c8d05a59c31 [file] [log] [blame]
#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);
}