| #include <signal.h> |
| #include <errno.h> |
| #include "threadimpl.h" |
| |
| //static Thread *runthread(Proc*); |
| |
| static char *_psstate[] = { |
| "Dead", |
| "Running", |
| "Ready", |
| "Rendezvous", |
| }; |
| |
| static char* |
| psstate(int s) |
| { |
| if(s < 0 || s >= nelem(_psstate)) |
| return "unknown"; |
| return _psstate[s]; |
| } |
| |
| void |
| _schedinit(void *arg) |
| { |
| Proc *p; |
| Thread *t; |
| extern void ignusr1(void), _threaddie(int); |
| ignusr1(); |
| signal(SIGTERM, _threaddie); |
| |
| p = arg; |
| lock(&p->lock); |
| p->pid = _threadgetpid(); |
| _threadsetproc(p); |
| unlock(&p->lock); |
| while(_setlabel(&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){ |
| if(t->moribund != 1) |
| fprint(2, "moribund %d\n", t->moribund); |
| assert(t->moribund == 1); |
| t->state = Dead; |
| if(t->prevt) |
| t->prevt->nextt = t->nextt; |
| else |
| p->threads.head = t->nextt; |
| if(t->nextt) |
| t->nextt->prevt = t->prevt; |
| else |
| p->threads.tail = t->prevt; |
| unlock(&p->lock); |
| if(t->inrendez){ |
| _threadflagrendez(t); |
| _threadbreakrendez(); |
| } |
| _stackfree(t->stk); |
| free(t->cmdname); |
| free(t); /* XXX how do we know there are no references? */ |
| p->nthreads--; |
| t = nil; |
| _sched(); |
| } |
| /* |
| if(p->needexec){ |
| t->ret = _schedexec(&p->exec); |
| p->needexec = 0; |
| } |
| */ |
| if(p->newproc){ |
| t->ret = _schedfork(p->newproc); |
| if(t->ret < 0){ |
| //fprint(2, "_schedfork: %r\n"); |
| abort(); |
| } |
| p->newproc = nil; |
| } |
| t->state = t->nextstate; |
| if(t->state == Ready) |
| _threadready(t); |
| } |
| unlock(&p->lock); |
| _sched(); |
| } |
| |
| static Thread* |
| runthread(Proc *p) |
| { |
| Channel *c; |
| Thread *t; |
| Tqueue *q; |
| Waitmsg *w; |
| int e, sent; |
| |
| if(p->nthreads==0 || (p->nthreads==1 && p->idle)) |
| return nil; |
| q = &p->ready; |
| relock: |
| lock(&p->readylock); |
| if(q->head == nil){ |
| e = errno; |
| if((c = _threadwaitchan) != nil){ |
| if(c->n <= c->s){ |
| sent = 0; |
| for(;;){ |
| if((w = p->waitmsg) != nil) |
| p->waitmsg = nil; |
| else |
| w = waitnohang(); |
| if(w == nil) |
| break; |
| if(sent == 0){ |
| unlock(&p->readylock); |
| sent = 1; |
| } |
| if(nbsendp(c, w) != 1) |
| break; |
| } |
| p->waitmsg = w; |
| if(sent) |
| goto relock; |
| } |
| }else{ |
| while((w = waitnohang()) != nil) |
| free(w); |
| } |
| errno = e; |
| if(p->idle){ |
| if(p->idle->state != Ready){ |
| fprint(2, "everyone is asleep\n"); |
| exits("everyone is asleep"); |
| } |
| unlock(&p->readylock); |
| _threaddebug(DBGSCHED, "running idle thread", p->nthreads); |
| return p->idle; |
| } |
| |
| _threaddebug(DBGSCHED, "sleeping for more work (%d threads)", p->nthreads); |
| q->asleep = 1; |
| unlock(&p->readylock); |
| while(rendezvous((ulong)q, 0) == ~0){ |
| if(_threadexitsallstatus) |
| exits(_threadexitsallstatus); |
| } |
| /* lock picked up from _threadready */ |
| } |
| t = q->head; |
| q->head = t->next; |
| unlock(&p->readylock); |
| return t; |
| } |
| |
| void |
| _sched(void) |
| { |
| Proc *p; |
| Thread *t; |
| |
| Resched: |
| p = _threadgetproc(); |
| //fprint(2, "p %p\n", p); |
| if((t = p->thread) != nil){ |
| if((ulong)&p < (ulong)t->stk){ /* stack overflow */ |
| fprint(2, "stack overflow %lux %lux\n", (ulong)&p, (ulong)t->stk); |
| abort(); |
| } |
| // _threaddebug(DBGSCHED, "pausing, state=%s set %p goto %p", |
| // psstate(t->state), &t->sched, &p->sched); |
| if(_setlabel(&t->sched)==0) |
| _gotolabel(&p->sched); |
| return; |
| }else{ |
| t = runthread(p); |
| if(t == nil){ |
| _threaddebug(DBGSCHED, "all threads gone; exiting"); |
| _threaddelproc(); |
| _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; |
| _gotolabel(&t->sched); |
| } |
| } |
| |
| long |
| threadstack(void) |
| { |
| Proc *p; |
| Thread *t; |
| |
| p = _threadgetproc(); |
| t = p->thread; |
| return (ulong)&p - (ulong)t->stk; |
| } |
| |
| void |
| _threadready(Thread *t) |
| { |
| Tqueue *q; |
| |
| if(t == t->proc->idle){ |
| _threaddebug(DBGSCHED, "idle thread is ready"); |
| return; |
| } |
| |
| 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->next = t; |
| q->tail = t; |
| if(q->asleep){ |
| assert(q->asleep == 1); |
| q->asleep = 0; |
| /* lock passes to runthread */ |
| _threaddebug(DBGSCHED, "waking process %d", t->proc->pid); |
| while(rendezvous((ulong)q, 0) == ~0){ |
| if(_threadexitsallstatus) |
| exits(_threadexitsallstatus); |
| } |
| }else |
| unlock(&t->proc->readylock); |
| } |
| |
| void |
| _threadidle(void) |
| { |
| Tqueue *q; |
| Thread *t, *idle; |
| Proc *p; |
| |
| p = _threadgetproc(); |
| q = &p->ready; |
| lock(&p->readylock); |
| assert(q->tail); |
| idle = q->tail; |
| if(q->head == idle){ |
| q->head = nil; |
| q->tail = nil; |
| }else{ |
| for(t=q->head; t->next!=q->tail; t=t->next) |
| ; |
| t->next = nil; |
| q->tail = t; |
| } |
| p->idle = idle; |
| _threaddebug(DBGSCHED, "p->idle is %d\n", idle->id); |
| unlock(&p->readylock); |
| } |
| |
| void |
| yield(void) |
| { |
| _sched(); |
| } |
| |
| void |
| threadstatus(void) |
| { |
| Proc *p; |
| Thread *t; |
| |
| p = _threadgetproc(); |
| for(t=p->threads.head; t; t=t->nextt) |
| fprint(2, "[%3d] %s userpc=%lux\n", |
| t->id, psstate(t->state), t->userpc); |
| } |
| |