void _sched(void) { Proc *p; Thread *t; Resched: p = _threadgetproc(); if((t = p->thread) != nil){ needstack(128); _threaddebug(DBGSCHED, "pausing, state=%s", psstate(t->state)); if(setjmp(t->sched)==0) longjmp(p->sched, 1); return; }else{ t = runthread(p); if(t == nil){ _threaddebug(DBGSCHED, "all threads gone; exiting"); _schedexit(p); } _threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id); p->thread = t; if(t->moribund){ _threaddebug(DBGSCHED, "%d.%d marked to die"); goto Resched; } t->state = Running; t->nextstate = Ready; longjmp(t->sched, 1); } }
void _threadready(Thread *t) { Tqueue *q; assert(t->state == Ready); _threaddebug(DBGSCHED, "readying %d.%d", t->proc->pid, t->id); q = &t->proc->ready; lock(&t->proc->readylock); t->next = nil; if(q->head==nil) q->head = t; else *q->tail = t; q->tail = &t->next; if(q->asleep){ q->asleep = 0; /* lock passes to runthread */ _threaddebug(DBGSCHED, "waking process %d", t->proc->pid); while(rendezvous(q, 0) == (void*)~0){ if(_threadexitsallstatus) exits(_threadexitsallstatus); } }else unlock(&t->proc->readylock); }
static void* altexecbuffered(Alt *a, int willreplace) { uchar *v; Channel *c; c = a->c; /* use buffered channel queue */ if(a->op==CHANRCV && c->n > 0){ _threaddebug(DBGCHAN, "buffer recv alt %p chan %p", a, c); v = c->v + c->e*(c->f%c->s); if(!willreplace) c->n--; c->f++; return v; } if(a->op==CHANSND && c->n < c->s){ _threaddebug(DBGCHAN, "buffer send alt %p chan %p", a, c); v = c->v + c->e*((c->f+c->n)%c->s); if(!willreplace) c->n++; return v; } abort(); return nil; }
void _threadnote(void *v, char *s) { Proc *p; Note *n; _threaddebug(DBGNOTE, "Got note %s", s); if(strncmp(s, "sys:", 4) == 0) noted(NDFLT); if(_threadexitsallstatus){ _threaddebug(DBGNOTE, "Threadexitsallstatus = '%s'\n", _threadexitsallstatus); _exits(_threadexitsallstatus); } if(strcmp(s, "threadint")==0) noted(NCONT); p = _threadgetproc(); if(p == nil) noted(NDFLT); for(n=notes; n<enotes; n++) if(canlock(&n->inuse)) break; if(n==enotes) sysfatal("libthread: too many delayed notes"); utfecpy(n->s, n->s+ERRMAX, s); n->proc = p; p->pending = 1; if(!p->splhi) delayednotes(p, v); noted(NCONT); }
static void delayednotes(Proc *p, void *v) { int i; Note *n; int (*fn)(void*, char*); if(!p->pending) return; p->pending = 0; for(n=notes; n<enotes; n++){ if(n->proc == p){ for(i=0; i<NFN; i++){ if(onnotepid[i]!=p->pid || (fn = onnote[i])==nil) continue; if((*fn)(v, n->s)) break; } if(i==NFN){ _threaddebug(DBGNOTE, "Unhandled note %s, proc %p\n", n->s, p); if(v != nil) noted(NDFLT); else if(strncmp(n->s, "sys:", 4)==0) abort(); threadexitsall(n->s); } n->proc = nil; unlock(&n->inuse); } } }
void _schedexecwait(void) { int pid; Channel *c; Proc *p; Thread *t; Waitmsg *w; p = _threadgetproc(); t = p->thread; pid = t->ret; _threaddebug(DBGEXEC, "_schedexecwait %d", t->ret); rfork(RFCFDG); for(;;){ w = wait(); if(w == nil) break; if(w->pid == pid) break; free(w); } if(w != nil){ if((c = _threadwaitchan) != nil) sendp(c, w); else free(w); } threadexits("procexec"); }
/* * Create and initialize a new Thread structure attached to a given proc. */ static int newthread(Proc *p, void (*f)(void *arg), void *arg, uint stacksize, char *name, int grp) { int id; Thread *t; if(stacksize < 32) sysfatal("bad stacksize %d", stacksize); t = _threadmalloc(sizeof(Thread), 1); t->stksize = stacksize; t->stk = _threadmalloc(stacksize, 0); memset(t->stk, 0xFE, stacksize); _threadinitstack(t, f, arg); t->grp = grp; if(name) t->cmdname = strdup(name); t->id = nextID(); id = t->id; t->next = (Thread*)~0; t->proc = p; _threaddebug(DBGSCHED, "create thread %d.%d name %s", p->pid, t->id, name); lock(&p->lock); p->nthreads++; if(p->threads.head == nil) p->threads.head = t; else *p->threads.tail = t; p->threads.tail = &t->nextt; t->nextt = nil; t->state = Ready; _threadready(t); unlock(&p->lock); return id; }
static void efork(Execargs *e) { char buf[ERRMAX]; _threaddebug(DBGEXEC, "_schedexec %s", e->prog); close(e->fd[0]); exec(e->prog, e->args); _threaddebug(DBGEXEC, "_schedexec failed: %r"); rerrstr(buf, sizeof buf); if(buf[0]=='\0') strcpy(buf, "exec failed"); write(e->fd[1], buf, strlen(buf)); close(e->fd[1]); _exits(buf); }
static Thread* runthread(Proc *p) { Thread *t; Tqueue *q; if(p->nthreads==0) return nil; q = &p->ready; lock(&p->readylock); if(q->head == nil){ q->asleep = 1; _threaddebug(DBGSCHED, "sleeping for more work"); unlock(&p->readylock); while(rendezvous(q, 0) == (void*)~0){ if(_threadexitsallstatus) exits(_threadexitsallstatus); } /* lock picked up from _threadready */ } t = q->head; q->head = t->next; unlock(&p->readylock); return t; }
static void enqueue(Alt *a, Channel **c) { int i; _threaddebug(DBGCHAN, "Queuing alt %p on channel %p", a, a->c); a->tag = c; i = emptyentry(a->c); a->c->qentry[i] = a; }
void _schedinit(void *arg) { Proc *p; Thread *t, **l; p = arg; _threadsetproc(p); p->pid = _tos->pid; //getpid(); while(setjmp(p->sched)) ; _threaddebug(DBGSCHED, "top of schedinit, _threadexitsallstatus=%p", _threadexitsallstatus); if(_threadexitsallstatus) exits(_threadexitsallstatus); lock(&p->lock); if((t=p->thread) != nil){ p->thread = nil; if(t->moribund){ t->state = Dead; for(l=&p->threads.head; *l; l=&(*l)->nextt) if(*l == t){ *l = t->nextt; if(*l==nil) p->threads.tail = l; p->nthreads--; break; } unlock(&p->lock); if(t->inrendez){ _threadflagrendez(t); _threadbreakrendez(); } free(t->stk); free(t->cmdname); free(t); /* XXX how do we know there are no references? */ t = nil; _sched(); } if(p->needexec){ t->ret = _schedexec(&p->exec); p->needexec = 0; } if(p->newproc){ t->ret = _schedfork(p->newproc); p->newproc = nil; } t->state = t->nextstate; if(t->state == Ready) _threadready(t); } unlock(&p->lock); _sched(); }
Channel* chancreate(int elemsize, int elemcnt) { Channel *c; if(elemcnt < 0 || elemsize <= 0) return nil; c = _threadmalloc(sizeof(Channel)+elemsize*elemcnt, 1); c->e = elemsize; c->s = elemcnt; _threaddebug(DBGCHAN, "chancreate %p", c); return c; }
int chaninit(Channel *c, int elemsize, int elemcnt) { if(elemcnt < 0 || elemsize <= 0 || c == nil) return -1; c->f = 0; c->n = 0; c->closed = 0; c->freed = 0; c->e = elemsize; c->s = elemcnt; _threaddebug(DBGCHAN, "chaninit %p", c); return 1; }
static int canexec(Alt *a) { int i, otherop; Channel *c; c = a->c; /* are there senders or receivers blocked? */ otherop = (CHANSND+CHANRCV) - a->op; for(i=0; i<c->nentry; i++) if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil){ _threaddebug(DBGCHAN, "can rendez alt %p chan %p", a, c); return 1; } /* is there room in the channel? */ if((a->op==CHANSND && c->n < c->s) || (a->op==CHANRCV && c->n > 0)){ _threaddebug(DBGCHAN, "can buffer alt %p chan %p", a, c); return 1; } return 0; }
static void dequeue(Alt *a) { int i; Channel *c; c = a->c; for(i=0; i<c->nentry; i++) if(c->qentry[i]==a){ _threaddebug(DBGCHAN, "Dequeuing alt %p from channel %p", a, a->c); c->qentry[i] = nil; /* release if freed and not closing */ if(c->freed && c->closed != 1) _chanfree(c); return; } }
void threadexitsall(char *exitstr) { Proc *p; int pid[64]; int i, npid, mypid; if(exitstr == nil) exitstr = ""; _threadexitsallstatus = exitstr; _threaddebug(DBGSCHED, "_threadexitsallstatus set to %p", _threadexitsallstatus); mypid = _tos->pid; //getpid(); /* * signal others. * copying all the pids first avoids other threads * teardown procedures getting in the way. * * avoid mallocs since malloc can post a note which can * call threadexitsall... */ for(;;){ lock(&_threadpq.lock); npid = 0; for(p = _threadpq.head; p && npid < nelem(pid); p=p->next){ if(p->threadint == 0 && p->pid != mypid){ pid[npid++] = p->pid; p->threadint = 1; } } unlock(&_threadpq.lock); if(npid == 0) break; for(i=0; i<npid; i++) postnote(PNPROC, pid[i], "threadint"); } /* leave */ exits(exitstr); }
void procexec(Channel *pidc, char *prog, char *args[]) { int n; Proc *p; Thread *t; _threaddebug(DBGEXEC, "procexec %s", prog); /* must be only thread in proc */ p = _threadgetproc(); t = p->thread; if(p->threads.head != t || p->threads.head->nextt != nil){ werrstr("not only thread in proc"); Bad: if(pidc) sendul(pidc, ~0); return; } /* * We want procexec to behave like exec; if exec succeeds, * never return, and if it fails, return with errstr set. * Unfortunately, the exec happens in another proc since * we have to wait for the exec'ed process to finish. * To provide the semantics, we open a pipe with the * write end close-on-exec and hand it to the proc that * is doing the exec. If the exec succeeds, the pipe will * close so that our read below fails. If the exec fails, * then the proc doing the exec sends the errstr down the * pipe to us. */ if(bind("#|", PIPEMNT, MREPL) < 0) goto Bad; if((p->exec.fd[0] = open(PIPEMNT "/data", OREAD)) < 0){ unmount(nil, PIPEMNT); goto Bad; } if((p->exec.fd[1] = open(PIPEMNT "/data1", OWRITE|OCEXEC)) < 0){ close(p->exec.fd[0]); unmount(nil, PIPEMNT); goto Bad; } unmount(nil, PIPEMNT); /* exec in parallel via the scheduler */ assert(p->needexec==0); p->exec.prog = prog; p->exec.args = args; p->needexec = 1; _sched(); close(p->exec.fd[1]); if((n = read(p->exec.fd[0], p->exitstr, ERRMAX-1)) > 0){ /* exec failed */ p->exitstr[n] = '\0'; errstr(p->exitstr, ERRMAX); close(p->exec.fd[0]); goto Bad; } close(p->exec.fd[0]); if(pidc) sendul(pidc, t->ret); /* wait for exec'ed program, then exit */ _schedexecwait(); }
static int altexec(Alt *a, int spl) { volatile Alt *b; int i, n, otherop; Channel *c; void *me, *waiter, *buf; c = a->c; /* rendezvous with others */ otherop = (CHANSND+CHANRCV) - a->op; n = 0; b = nil; me = a->v; for(i=0; i<c->nentry; i++) if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil) if(nrand(++n) == 0) b = c->qentry[i]; if(b != nil){ _threaddebug(DBGCHAN, "rendez %s alt %p chan %p alt %p", a->op==CHANRCV?"recv":"send", a, c, b); waiter = b->v; if(c->s && c->n){ /* * if buffer is full and there are waiters * and we're meeting a waiter, * we must be receiving. * * we use the value in the channel buffer, * copy the waiter's value into the channel buffer * on behalf of the waiter, and then wake the waiter. */ if(a->op!=CHANRCV) abort(); buf = altexecbuffered(a, 1); altcopy(me, buf, c->e); altcopy(buf, waiter, c->e); }else{ if(a->op==CHANRCV) altcopy(me, waiter, c->e); else altcopy(waiter, me, c->e); } *b->tag = c; /* commits us to rendezvous */ _threaddebug(DBGCHAN, "unlocking the chanlock"); unlock(&chanlock); _procsplx(spl); _threaddebug(DBGCHAN, "chanlock is %lud", *(ulong*)&chanlock); while(_threadrendezvous(b->tag, 0) == Intred) ; return 1; } buf = altexecbuffered(a, 0); if(a->op==CHANRCV) altcopy(me, buf, c->e); else altcopy(buf, me, c->e); unlock(&chanlock); _procsplx(spl); return 1; }