| #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); |
| } |