| /* |
| * Set up a dedicated proc to handle calls to exec. |
| * The proc also waits for child messages. |
| * This way, each proc scheduler need not worry |
| * about calling wait in its main loop. |
| * |
| * To be included from other files (e.g., Linux-clone.c). |
| */ |
| |
| typedef struct Xarg Xarg; |
| struct Xarg |
| { |
| Channel *pidc; |
| int fd[3]; |
| char *prog; |
| char **args; |
| int freeargs; |
| Channel *ret; |
| int errn; |
| char errstr[ERRMAX]; |
| }; |
| |
| static Proc *_threadexecproc; |
| static Channel *_threadexecchan; |
| static Lock threadexeclock; |
| |
| /* |
| * Called to poll for any kids of this pthread. |
| * We have a separate proc responsible for exec, |
| * so this is a no-op. |
| */ |
| void |
| _threadwaitkids(Proc *p) |
| { |
| } |
| |
| #define WAITSIG SIGCHLD |
| |
| /* |
| * Separate process to wait for child messages. |
| * Also runs signal handlers and runs all execs. |
| */ |
| static void |
| nop(int sig) |
| { |
| USED(sig); |
| } |
| |
| static void |
| _threadwaitproc(void *v) |
| { |
| Channel *c; |
| Waitmsg *w; |
| sigset_t mask; |
| int ret, nkids; |
| Xarg *xa; |
| |
| nkids = 0; |
| |
| sigemptyset(&mask); |
| siginterrupt(WAITSIG, 1); |
| signal(WAITSIG, nop); |
| sigaddset(&mask, WAITSIG); |
| sigprocmask(SIG_BLOCK, &mask, nil); |
| USED(v); |
| for(;;){ |
| while((nkids > 0 ? nbrecv : recv)(_threadexecchan, &xa) == 1){ |
| ret = _threadexec(xa->pidc, xa->fd, xa->prog, xa->args, xa->freeargs); |
| if(ret > 0) |
| nkids++; |
| else{ |
| rerrstr(xa->errstr, sizeof xa->errstr); |
| xa->errn = errno; |
| } |
| sendul(xa->ret, ret); |
| } |
| if(nkids > 0){ |
| sigprocmask(SIG_UNBLOCK, &mask, nil); |
| w = wait(); |
| sigprocmask(SIG_BLOCK, &mask, nil); |
| if(w == nil && errno == ECHILD){ |
| fprint(2, "wait returned ECHILD but nkids=%d; reset\n", nkids); |
| nkids = 0; |
| } |
| if(w){ |
| nkids--; |
| if((c = _threadwaitchan) != nil) |
| sendp(c, w); |
| else |
| free(w); |
| } |
| } |
| } |
| } |
| |
| static void _kickexecproc(void); |
| |
| int |
| _callthreadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs) |
| { |
| int ret; |
| Xarg xa; |
| |
| if(_threadexecchan == nil){ |
| lock(&threadexeclock); |
| if(_threadexecchan == nil) |
| _threadfirstexec(); |
| unlock(&threadexeclock); |
| } |
| |
| xa.pidc = pidc; |
| xa.fd[0] = fd[0]; |
| xa.fd[1] = fd[1]; |
| xa.fd[2] = fd[2]; |
| xa.prog = prog; |
| xa.args = args; |
| xa.freeargs = freeargs; |
| xa.ret = chancreate(sizeof(ulong), 1); |
| sendp(_threadexecchan, &xa); |
| _kickexecproc(); |
| ret = recvul(xa.ret); |
| if(ret < 0){ |
| werrstr("%s", xa.errstr); |
| errno = xa.errn; |
| } |
| chanfree(xa.ret); |
| return ret; |
| } |
| |
| /* |
| * Called before the first exec. |
| */ |
| void |
| _threadfirstexec(void) |
| { |
| int id; |
| Proc *p; |
| |
| _threadexecchan = chancreate(sizeof(Xarg*), 1); |
| id = 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; |
| for(p=_threadpq.head; p; p=p->next) |
| if(p->threads.head && p->threads.head->id == id) |
| break; |
| if(p == nil) |
| sysfatal("cannot find exec proc"); |
| unlock(&_threadpq.lock); |
| _threadexecproc = p; |
| } |
| |
| /* |
| * Called after the thread t has been rescheduled. |
| * Kick the exec proc in case it is in the middle of a wait. |
| */ |
| static void |
| _kickexecproc(void) |
| { |
| kill(_threadexecproc->pid, WAITSIG); |
| } |
| |
| /* |
| * Called before exec. |
| */ |
| void |
| _threadbeforeexec(void) |
| { |
| } |
| |
| /* |
| * Called after exec. |
| */ |
| void |
| _threadafterexec(void) |
| { |
| } |
| |