#include "rc.h" #include // ----------------------------------------------------------------------- // globals struct WaitMsg { int pid; int type; ulong time[3]; struct{ int code; char *str; }status; char *str; }; // ----------------------------------------------------------------------- // internal static int await(int pid4, int opt, struct WaitMsg *msg) { int pid, status, core; struct rusage ru; ulong u, s; /* event loop */ for(;;){ if((pid = wait4(pid4, &status, opt, &ru)) <= 0){ if(errno == ECHILD){ msg->pid = -1; return 1; } msg->pid = 0; perror("failed wait4"); return 0; } u = ru.ru_utime.tv_sec*1000+((ru.ru_utime.tv_usec+500)/1000); s = ru.ru_stime.tv_sec*1000+((ru.ru_stime.tv_usec+500)/1000); if(WIFEXITED(status)){ msg->pid = pid; msg->time[0] = u; msg->time[1] = s; msg->time[2] = u+s; msg->type = Pdone; msg->status.code = WEXITSTATUS(status); msg->status.str = nil; return 1; } if(WIFSIGNALED(status)){ msg->pid = pid; msg->time[0] = u; msg->time[1] = s; msg->time[2] = u+s; msg->type = Psig; msg->status.code = WTERMSIG(status); msg->status.str = nil; return 1; } if(WIFSTOPPED(status)){ msg->pid = pid; msg->time[0] = u; msg->time[1] = s; msg->time[2] = u+s; msg->status.code = WSTOPSIG(status); msg->status.str = nil; msg->type = Pstop; return 1; } } } static int shouldwait(Thread *job) { int i; for(i=0; iwait.len; i++){ if(job->wait.on[i].status == Prun) return 1; } return 0; } static inline void notify(Thread *job, struct WaitMsg msg) { int i; for(i=0; i < job->wait.len; i++){ if(job->wait.on[i].pid == msg.pid){ job->status = msg.status.code; switch(msg.type){ case Pstop: /* NOTE: this needs to have an interactive check */ print(shell.err, "%d: suspended\n", msg.pid); job->wait.status = Pstop; job->wait.on[i].status = Pstop; break; case Psig: /* NOTE: this needs to have an interactive check */ print(shell.err, "%d: terminated by signal %d\n", msg.pid, msg.status); /* fallthrough */ case Pdone: job->wait.on[i].status = Pdone; delwait(job, msg.pid); if(!job->wait.len) job->wait.status = Pdone; break; default: /* NOTE: this needs to have an interactive check */ fatal("%d: unrecognized message type %d\n", msg.pid, msg.type); } break; } } } // ----------------------------------------------------------------------- // exported void clearwait(Thread *job) { job->wait.len = 0; } int havewait(Thread *job, int pid) { int i; for(i=0; iwait.len; i++) if(job->wait.on[i].pid == pid) return 1; return 0; } void addwait(Thread *job, int pid) { if(job->wait.len == job->wait.cap){ job->wait.cap = job->wait.cap + 2; job->wait.on = erealloc(job->wait.on, job->wait.cap*sizeof(*job->wait.on)); } job->wait.on[job->wait.len++] = (struct WaitItem){.pid=pid, .status=Prun}; } void delwait(Thread *job, int pid) { int r, w; for(r=w=0; r < job->wait.len; r++){ if(job->wait.on[r].pid != pid) job->wait.on[w++].pid = job->wait.on[r].pid; } job->wait.len = w; } int waitall(Thread *job) { int i; Thread *t; struct WaitMsg msg; while(shouldwait(job) && await(-job->pgid, WUNTRACED, &msg)){ switch(msg.pid){ case 0: // error perror("wait job"); return 0; case -1: // no children: assume they have exited job->wait.status = Pdone; clearwait(job); return 1; default: ; } notify(job, msg); } return 1; } int waitfor(Thread *job, int pid) { int i; Thread *t; struct WaitMsg msg; while(shouldwait(job) && await(-job->pgid, WUNTRACED, &msg)){ switch(msg.pid){ case 0: // error perror("wait for"); return 0; case -1: // no children: assume they have exited job->wait.status = Pdone; clearwait(job); return 1; default: ; } notify(job, msg); /* allow for an early exit */ if(msg.pid == pid) return 1; } return 1; } void killzombies(void) { Thread *job; int index, status, pid; while((pid=waitpid(-1, &status, WNOHANG))>0){ print(shell.err, "found zombie pid %d\n", pid); flush(shell.err); job = getjob(pid, &index); if(!job) perror("invalid pid"); if(WIFEXITED(status)) job->wait.status = Pdone; if(WIFSTOPPED(status)) job->wait.status = Pstop; if(WIFCONTINUED(status)) job->wait.status = Pagain; if(job->wait.status == Pdone){ report(job,index); deljob(job); } } }