| #include "mk.h" |
| |
| typedef struct Event |
| { |
| int pid; |
| Job *job; |
| } Event; |
| static Event *events; |
| static int nevents, nrunning, nproclimit; |
| |
| typedef struct Process |
| { |
| int pid; |
| int status; |
| struct Process *b, *f; |
| } Process; |
| static Process *phead, *pfree; |
| static void sched(void); |
| static void pnew(int, int), pdelete(Process *); |
| |
| int pidslot(int); |
| |
| void |
| run(Job *j) |
| { |
| Job *jj; |
| |
| if(jobs){ |
| for(jj = jobs; jj->next; jj = jj->next) |
| ; |
| jj->next = j; |
| } else |
| jobs = j; |
| j->next = 0; |
| /* this code also in waitup after parse redirect */ |
| if(nrunning < nproclimit) |
| sched(); |
| } |
| |
| static void |
| sched(void) |
| { |
| char *flags; |
| Job *j; |
| Bufblock *buf; |
| int slot; |
| Node *n; |
| Envy *e; |
| |
| if(jobs == 0){ |
| usage(); |
| return; |
| } |
| j = jobs; |
| jobs = j->next; |
| if(DEBUG(D_EXEC)) |
| fprint(1, "firing up job for target %s\n", wtos(j->t, ' ')); |
| slot = nextslot(); |
| events[slot].job = j; |
| buf = newbuf(); |
| e = buildenv(j, slot); |
| shprint(j->r->recipe, e, buf, j->r->shellt); |
| if(!tflag && (nflag || !(j->r->attr&QUIET))) |
| Bwrite(&bout, buf->start, (long)strlen(buf->start)); |
| freebuf(buf); |
| if(nflag||tflag){ |
| for(n = j->n; n; n = n->next){ |
| if(tflag){ |
| if(!(n->flags&VIRTUAL)) |
| touch(n->name); |
| else if(explain) |
| Bprint(&bout, "no touch of virtual '%s'\n", n->name); |
| } |
| n->time = time((long *)0); |
| MADESET(n, MADE); |
| } |
| } else { |
| if(DEBUG(D_EXEC)) |
| fprint(1, "recipe='%s'", j->r->recipe);/**/ |
| Bflush(&bout); |
| if(j->r->attr&NOMINUSE) |
| flags = 0; |
| else |
| flags = "-e"; |
| events[slot].pid = execsh(flags, j->r->recipe, 0, e, j->r->shellt, j->r->shellcmd); |
| usage(); |
| nrunning++; |
| if(DEBUG(D_EXEC)) |
| fprint(1, "pid for target %s = %d\n", wtos(j->t, ' '), events[slot].pid); |
| } |
| } |
| |
| int |
| waitup(int echildok, int *retstatus) |
| { |
| Envy *e; |
| int pid; |
| int slot; |
| Symtab *s; |
| Word *w; |
| Job *j; |
| char buf[ERRMAX]; |
| Bufblock *bp; |
| int uarg = 0; |
| int done; |
| Node *n; |
| Process *p; |
| extern int runerrs; |
| |
| /* first check against the proces slist */ |
| if(retstatus) |
| for(p = phead; p; p = p->f) |
| if(p->pid == *retstatus){ |
| *retstatus = p->status; |
| pdelete(p); |
| return(-1); |
| } |
| again: /* rogue processes */ |
| pid = waitfor(buf); |
| if(pid == -1){ |
| if(echildok > 0) |
| return(1); |
| else { |
| fprint(2, "mk: (waitup %d): %r\n", echildok); |
| Exit(); |
| } |
| } |
| if(DEBUG(D_EXEC)) |
| fprint(1, "waitup got pid=%d, status='%s'\n", pid, buf); |
| if(retstatus && pid == *retstatus){ |
| *retstatus = buf[0]? 1:0; |
| return(-1); |
| } |
| slot = pidslot(pid); |
| if(slot < 0){ |
| if(DEBUG(D_EXEC)) |
| fprint(2, "mk: wait returned unexpected process %d\n", pid); |
| pnew(pid, buf[0]? 1:0); |
| goto again; |
| } |
| j = events[slot].job; |
| usage(); |
| nrunning--; |
| events[slot].pid = -1; |
| if(buf[0]){ |
| e = buildenv(j, slot); |
| bp = newbuf(); |
| shprint(j->r->recipe, e, bp, j->r->shellt); |
| front(bp->start); |
| fprint(2, "mk: %s: exit status=%s", bp->start, buf); |
| freebuf(bp); |
| for(n = j->n, done = 0; n; n = n->next) |
| if(n->flags&DELETE){ |
| if(done++ == 0) |
| fprint(2, ", deleting"); |
| fprint(2, " '%s'", n->name); |
| delete(n->name); |
| } |
| fprint(2, "\n"); |
| if(kflag){ |
| runerrs++; |
| uarg = 1; |
| } else { |
| jobs = 0; |
| Exit(); |
| } |
| } |
| for(w = j->t; w; w = w->next){ |
| if((s = symlook(w->s, S_NODE, 0)) == 0) |
| continue; /* not interested in this node */ |
| update(uarg, s->u.ptr); |
| } |
| if(nrunning < nproclimit) |
| sched(); |
| return(0); |
| } |
| |
| void |
| nproc(void) |
| { |
| Symtab *sym; |
| Word *w; |
| |
| if(sym = symlook("NPROC", S_VAR, 0)) { |
| w = sym->u.ptr; |
| if (w && w->s && w->s[0]) |
| nproclimit = atoi(w->s); |
| } |
| if(nproclimit < 1) |
| nproclimit = 1; |
| if(DEBUG(D_EXEC)) |
| fprint(1, "nprocs = %d\n", nproclimit); |
| if(nproclimit > nevents){ |
| if(nevents) |
| events = (Event *)Realloc((char *)events, nproclimit*sizeof(Event)); |
| else |
| events = (Event *)Malloc(nproclimit*sizeof(Event)); |
| while(nevents < nproclimit) |
| events[nevents++].pid = 0; |
| } |
| } |
| |
| int |
| nextslot(void) |
| { |
| int i; |
| |
| for(i = 0; i < nproclimit; i++) |
| if(events[i].pid <= 0) return i; |
| assert("out of slots!!", 0); |
| return 0; /* cyntax */ |
| } |
| |
| int |
| pidslot(int pid) |
| { |
| int i; |
| |
| for(i = 0; i < nevents; i++) |
| if(events[i].pid == pid) return(i); |
| if(DEBUG(D_EXEC)) |
| fprint(2, "mk: wait returned unexpected process %d\n", pid); |
| return(-1); |
| } |
| |
| |
| static void |
| pnew(int pid, int status) |
| { |
| Process *p; |
| |
| if(pfree){ |
| p = pfree; |
| pfree = p->f; |
| } else |
| p = (Process *)Malloc(sizeof(Process)); |
| p->pid = pid; |
| p->status = status; |
| p->f = phead; |
| phead = p; |
| if(p->f) |
| p->f->b = p; |
| p->b = 0; |
| } |
| |
| static void |
| pdelete(Process *p) |
| { |
| if(p->f) |
| p->f->b = p->b; |
| if(p->b) |
| p->b->f = p->f; |
| else |
| phead = p->f; |
| p->f = pfree; |
| pfree = p; |
| } |
| |
| void |
| killchildren(char *msg) |
| { |
| Process *p; |
| |
| kflag = 1; /* to make sure waitup doesn't exit */ |
| jobs = 0; /* make sure no more get scheduled */ |
| for(p = phead; p; p = p->f) |
| expunge(p->pid, msg); |
| while(waitup(1, (int *)0) == 0) |
| ; |
| Bprint(&bout, "mk: %s\n", msg); |
| Exit(); |
| } |
| |
| static long tslot[1000]; |
| static long tick; |
| |
| void |
| usage(void) |
| { |
| long t; |
| |
| time(&t); |
| if(tick) |
| tslot[nrunning] += (t-tick); |
| tick = t; |
| } |
| |
| void |
| prusage(void) |
| { |
| int i; |
| |
| usage(); |
| for(i = 0; i <= nevents; i++) |
| fprint(1, "%d: %ld\n", i, tslot[i]); |
| } |