diff options
Diffstat (limited to 'src/libthread/sched.c')
-rw-r--r-- | src/libthread/sched.c | 355 |
1 files changed, 182 insertions, 173 deletions
diff --git a/src/libthread/sched.c b/src/libthread/sched.c index 7f7b6daa..3fb2ff20 100644 --- a/src/libthread/sched.c +++ b/src/libthread/sched.c @@ -1,76 +1,59 @@ -#include <u.h> -#include <signal.h> -#include <errno.h> +/* + * Thread scheduler. + */ #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 -needstack(int howmuch) -{ - Proc *p; - Thread *t; - - p = _threadgetproc(); - if(p == nil || (t=p->thread) == nil) - return; - if((ulong)&howmuch < (ulong)t->stk+howmuch){ /* stack overflow waiting to happen */ - fprint(2, "stack overflow: stack at 0x%lux, limit at 0x%lux, need 0x%lux\n", (ulong)&p, (ulong)t->stk, howmuch); - abort(); - } -} +static Thread *runthread(Proc*); +static void schedexit(Proc*); +/* + * Main scheduling loop. + */ void -_scheduler(void *arg) +_threadscheduler(void *arg) { Proc *p; Thread *t; p = arg; - lock(&p->lock); - p->pid = _threadgetpid(); - _threadsetproc(p); + + _threadlinkmain(); + _threadinitproc(p); for(;;){ + /* + * Clean up zombie children. + */ + _threadwaitkids(p); + + /* + * Find next thread to run. + */ + _threaddebug(DBGSCHED, "runthread"); 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 Moribund; + if(t == nil) + schedexit(p); + + /* + * If it's ready, run it (might instead be marked to die). + */ + lock(&p->lock); + if(t->state == Ready){ + _threaddebug(DBGSCHED, "running %d.%d", p->id, t->id); + t->state = Running; + t->nextstate = Ready; + p->thread = t; + unlock(&p->lock); + _swaplabel(&p->context, &t->context); + lock(&p->lock); + p->thread = nil; } - t->state = Running; - t->nextstate = Ready; - unlock(&p->lock); - _swaplabel(&p->sched, &t->sched); - - lock(&p->lock); - p->thread = nil; + /* + * If thread needs to die, kill it. + */ if(t->moribund){ - Moribund: - if(t->moribund != 1) - fprint(2, "moribund %d\n", t->moribund); + _threaddebug(DBGSCHED, "moribund %d.%d", p->id, t->id); assert(t->moribund == 1); t->state = Dead; if(t->prevt) @@ -82,40 +65,37 @@ _scheduler(void *arg) else p->threads.tail = t->prevt; unlock(&p->lock); - if(t->inrendez){ - abort(); - // _threadflagrendez(t); - // _threadbreakrendez(); - } - _stackfree(t->stk); - free(t->cmdname); - free(t); /* XXX how do we know there are no references? */ + _threadfree(t); p->nthreads--; t = nil; - lock(&p->lock); continue; } -/* - 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; + unlock(&p->lock); + + /* + * If there is a request to run a function on the + * scheduling stack, do so. + */ + if(p->schedfn){ + _threaddebug(DBGSCHED, "schedfn"); + p->schedfn(p); + p->schedfn = nil; + _threaddebug(DBGSCHED, "schedfn ended"); } + + /* + * Move the thread along. + */ t->state = t->nextstate; + _threaddebug(DBGSCHED, "moveon %d.%d", p->id, t->id); if(t->state == Ready) _threadready(t); - unlock(&p->lock); } } +/* + * Called by thread to give up control of processor to scheduler. + */ int _sched(void) { @@ -125,166 +105,195 @@ _sched(void) p = _threadgetproc(); t = p->thread; assert(t != nil); - _swaplabel(&t->sched, &p->sched); + _swaplabel(&t->context, &p->context); return p->nsched++; } +/* + * Called by thread to yield the processor to other threads. + * Returns number of other threads run between call and return. + */ +int +yield(void) +{ + Proc *p; + int nsched; + + p = _threadgetproc(); + nsched = p->nsched; + return _sched() - nsched; +} + +/* + * Choose the next thread to run. + */ static Thread* runthread(Proc *p) { - Channel *c; Thread *t; Tqueue *q; - Waitmsg *w; - int e, sent; + /* + * No threads left? + */ if(p->nthreads==0 || (p->nthreads==1 && p->idle)) return nil; - q = &p->ready; -relock: + lock(&p->readylock); - if(p->nsched%128 == 0){ - /* clean up children */ - 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; - } + q = &p->ready; if(q->head == nil){ + /* + * Is this a single-process program with an idle thread? + */ if(p->idle){ - if(p->idle->state != Ready){ - fprint(2, "everyone is asleep\n"); - exits("everyone is asleep"); - } + /* + * The idle thread had better be ready! + */ + if(p->idle->state != Ready) + sysfatal("all threads are asleep"); + + /* + * Run the idle thread. + */ unlock(&p->readylock); _threaddebug(DBGSCHED, "running idle thread", p->nthreads); return p->idle; } - _threaddebug(DBGSCHED, "sleeping for more work (%d threads)", p->nthreads); + /* + * Wait until one of our threads is readied (by another proc!). + */ q->asleep = 1; p->rend.l = &p->readylock; _procsleep(&p->rend); + + /* + * Maybe we were awakened to exit? + */ if(_threadexitsallstatus) _exits(_threadexitsallstatus); + + assert(q->head != nil); } + t = q->head; q->head = t->next; unlock(&p->readylock); - return t; -} -long -threadstack(void) -{ - Proc *p; - Thread *t; - - p = _threadgetproc(); - t = p->thread; - return (ulong)&p - (ulong)t->stk; + return t; } +/* + * Add a newly-ready thread to its proc's run queue. + */ void _threadready(Thread *t) { Tqueue *q; + /* + * The idle thread does not go on the run queue. + */ 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); + _threaddebug(DBGSCHED, "readying %d.%d", t->proc->id, t->id); + + /* + * Add thread to run queue. + */ q = &t->proc->ready; lock(&t->proc->readylock); + t->next = nil; - if(q->head==nil) + if(q->head == nil) q->head = t; else q->tail->next = t; q->tail = t; + + /* + * Wake proc scheduler if it is sleeping. + */ if(q->asleep){ assert(q->asleep == 1); q->asleep = 0; - /* lock passes to runthread */ _procwakeup(&t->proc->rend); } unlock(&t->proc->readylock); - if(_threadexitsallstatus) - _exits(_threadexitsallstatus); } +/* + * Mark the given thread as the idle thread. + * Since the idle thread was just created, it is sitting + * somewhere on the ready queue. + */ void -_threadidle(void) +_threadsetidle(int id) { Tqueue *q; - Thread *t, *idle; + Thread *t, **l, *last; 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); -} -int -yield(void) -{ - Proc *p; - int nsched; + /* + * Find thread on ready queue. + */ + q = &p->ready; + for(l=&q->head, last=nil; (t=*l) != nil; l=&t->next, last=t) + if(t->id == id) + break; + assert(t != nil); - p = _threadgetproc(); - nsched = p->nsched; - return _sched() - nsched; + /* + * Remove it from ready queue. + */ + *l = t->next; + if(t == q->head) + q->head = t->next; + if(t->next == nil) + q->tail = last; + + /* + * Set as idle thread. + */ + p->idle = t; + _threaddebug(DBGSCHED, "p->idle is %d\n", t->id); + unlock(&p->readylock); } -void -threadstatus(void) +static void +schedexit(Proc *p) { - Proc *p; - Thread *t; + char ex[ERRMAX]; + int n; + Proc **l; - 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); + _threaddebug(DBGSCHED, "exiting proc %d", p->id); + lock(&_threadpq.lock); + for(l=&_threadpq.head; *l; l=&(*l)->next){ + if(*l == p){ + *l = p->next; + if(*l == nil) + _threadpq.tail = l; + break; + } + } + n = --_threadnprocs; + unlock(&_threadpq.lock); + + strncpy(ex, p->exitstr, sizeof ex); + ex[sizeof ex-1] = '\0'; + free(p); + if(n == 0) + _threadexitallproc(ex); + else + _threadexitproc(ex); } - + |