| #include <u.h> |
| #include <errno.h> |
| #include "threadimpl.h" |
| |
| static int multi; |
| static Proc *theproc; |
| static pthread_key_t key; |
| |
| /* |
| * Called before we go multiprocess. |
| */ |
| void |
| _threadmultiproc(void) |
| { |
| if(multi == 0){ |
| multi = 1; |
| pthread_key_create(&key, 0); |
| _threadsetproc(theproc); |
| } |
| } |
| |
| /* |
| * Set the proc for the current pthread. |
| */ |
| void |
| _threadsetproc(Proc *p) |
| { |
| if(!multi){ |
| theproc = p; |
| return; |
| } |
| pthread_setspecific(key, p); |
| } |
| |
| /* |
| * Get the proc for the current pthread. |
| */ |
| Proc* |
| _threadgetproc(void) |
| { |
| if(!multi) |
| return theproc; |
| |
| return pthread_getspecific(key); |
| } |
| |
| /* |
| * 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) |
| { |
| _threaddebug(DBGSCHED, "_pthreadexit"); |
| pthread_exit(nil); |
| } |
| |
| /* |
| * Called to exit all pthreads. |
| */ |
| void |
| _threadexitallproc(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. |
| */ |
| void |
| _threadwaitkids(Proc *p) |
| { |
| } |
| |
| /* |
| * Separate process to wait for child messages. |
| */ |
| Channel *_threadexecchan; |
| void |
| _threadwaitproc(void *v) |
| { |
| Channel *c; |
| Waitmsg *w; |
| sigset_t none; |
| |
| sigemptyset(&none); |
| pthread_sigmask(SIG_SETMASK, &none, 0); |
| |
| USED(v); |
| |
| for(;;){ |
| w = wait(); |
| if(w == nil){ |
| if(errno == ECHILD) |
| recvul(_threadexecchan); |
| continue; |
| } |
| if((c = _threadwaitchan) != nil) |
| sendp(c, w); |
| else |
| free(w); |
| } |
| fprint(2, "_threadwaitproc exits\n"); |
| } |
| |
| /* |
| * Called before the first exec. |
| */ |
| void |
| _threadfirstexec(void) |
| { |
| } |
| |
| 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); |
| } |
| |
| void |
| _threadafterexec(void) |
| { |
| nbsendul(_threadexecchan, 1); |
| } |
| |
| /* |
| * Called to start a new proc. |
| */ |
| void |
| _threadstartproc(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; |
| } |