2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* Thread scheduler.
|
|
|
|
|
*/
|
2003-11-23 18:18:00 +00:00
|
|
|
#include "threadimpl.h"
|
2003-09-30 17:47:42 +00:00
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
static Thread *runthread(Proc*);
|
|
|
|
|
static void schedexit(Proc*);
|
2004-09-21 01:11:28 +00:00
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* Main scheduling loop.
|
|
|
|
|
*/
|
2004-09-21 01:11:28 +00:00
|
|
|
void
|
2004-09-23 03:01:36 +00:00
|
|
|
_threadscheduler(void *arg)
|
2004-09-21 01:11:28 +00:00
|
|
|
{
|
|
|
|
|
Proc *p;
|
|
|
|
|
Thread *t;
|
|
|
|
|
|
2003-09-30 17:47:42 +00:00
|
|
|
p = arg;
|
2004-09-23 03:01:36 +00:00
|
|
|
|
|
|
|
|
_threadlinkmain();
|
2004-11-08 16:03:20 +00:00
|
|
|
_threadsetproc(p);
|
2004-09-21 01:11:28 +00:00
|
|
|
|
|
|
|
|
for(;;){
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* Clean up zombie children.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find next thread to run.
|
|
|
|
|
*/
|
|
|
|
|
_threaddebug(DBGSCHED, "runthread");
|
2004-09-21 01:11:28 +00:00
|
|
|
t = runthread(p);
|
2004-09-23 03:01:36 +00:00
|
|
|
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;
|
2004-09-21 01:11:28 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* If thread needs to die, kill it.
|
2004-10-22 18:45:08 +00:00
|
|
|
* t->proc == p may not be true if we're
|
|
|
|
|
* trying to jump into the exec proc (see exec-unix.c).
|
2004-09-23 03:01:36 +00:00
|
|
|
*/
|
2003-09-30 17:47:42 +00:00
|
|
|
if(t->moribund){
|
2004-09-23 03:01:36 +00:00
|
|
|
_threaddebug(DBGSCHED, "moribund %d.%d", p->id, t->id);
|
2004-10-22 18:45:08 +00:00
|
|
|
if(t->moribund != 1)
|
|
|
|
|
print("moribund broke %p %d\n", &t->moribund, t->moribund);
|
2003-09-30 17:47:42 +00:00
|
|
|
assert(t->moribund == 1);
|
|
|
|
|
t->state = Dead;
|
2004-10-22 18:45:08 +00:00
|
|
|
_procdelthread(p, t);
|
2003-09-30 17:47:42 +00:00
|
|
|
unlock(&p->lock);
|
2004-09-23 03:01:36 +00:00
|
|
|
_threadfree(t);
|
2003-09-30 17:47:42 +00:00
|
|
|
t = nil;
|
2004-09-21 01:11:28 +00:00
|
|
|
continue;
|
2003-09-30 17:47:42 +00:00
|
|
|
}
|
2004-10-22 18:45:08 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the thread has asked to move to another proc,
|
|
|
|
|
* let it go (only to be used in *very* special situations).
|
|
|
|
|
if(t->nextproc != p)
|
|
|
|
|
_procdelthread(p, t);
|
|
|
|
|
*/
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
unlock(&p->lock);
|
|
|
|
|
|
2004-10-22 18:45:08 +00:00
|
|
|
/*
|
|
|
|
|
* If the thread has asked to move to another proc,
|
|
|
|
|
* add it to the new proc.
|
|
|
|
|
*/
|
|
|
|
|
if(t->nextproc != p){
|
|
|
|
|
// lock(&t->nextproc->lock);
|
|
|
|
|
// _procaddthread(t->nextproc, t);
|
|
|
|
|
// unlock(&t->nextproc->lock);
|
|
|
|
|
t->proc = t->nextproc;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* 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");
|
2003-09-30 17:47:42 +00:00
|
|
|
}
|
2004-09-23 03:01:36 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Move the thread along.
|
|
|
|
|
*/
|
2003-09-30 17:47:42 +00:00
|
|
|
t->state = t->nextstate;
|
2004-10-22 18:45:08 +00:00
|
|
|
_threaddebug(DBGSCHED, "moveon %d.%d", t->proc->id, t->id);
|
2003-09-30 17:47:42 +00:00
|
|
|
if(t->state == Ready)
|
|
|
|
|
_threadready(t);
|
|
|
|
|
}
|
2004-09-21 01:11:28 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* Called by thread to give up control of processor to scheduler.
|
|
|
|
|
*/
|
2004-09-21 01:11:28 +00:00
|
|
|
int
|
|
|
|
|
_sched(void)
|
|
|
|
|
{
|
|
|
|
|
Proc *p;
|
|
|
|
|
Thread *t;
|
|
|
|
|
|
|
|
|
|
p = _threadgetproc();
|
|
|
|
|
t = p->thread;
|
|
|
|
|
assert(t != nil);
|
2004-09-23 03:01:36 +00:00
|
|
|
_swaplabel(&t->context, &p->context);
|
2004-09-21 01:11:28 +00:00
|
|
|
return p->nsched++;
|
2003-09-30 17:47:42 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2003-11-23 18:38:17 +00:00
|
|
|
static Thread*
|
2003-09-30 17:47:42 +00:00
|
|
|
runthread(Proc *p)
|
|
|
|
|
{
|
|
|
|
|
Thread *t;
|
|
|
|
|
Tqueue *q;
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* No threads left?
|
|
|
|
|
*/
|
2003-12-06 18:05:27 +00:00
|
|
|
if(p->nthreads==0 || (p->nthreads==1 && p->idle))
|
2003-09-30 17:47:42 +00:00
|
|
|
return nil;
|
2004-09-23 03:01:36 +00:00
|
|
|
|
2004-11-08 16:03:20 +00:00
|
|
|
_threadschednote();
|
2003-09-30 17:47:42 +00:00
|
|
|
lock(&p->readylock);
|
2004-09-23 03:01:36 +00:00
|
|
|
q = &p->ready;
|
2004-06-09 14:23:34 +00:00
|
|
|
if(q->head == nil){
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* Is this a single-process program with an idle thread?
|
|
|
|
|
*/
|
2003-12-06 18:05:27 +00:00
|
|
|
if(p->idle){
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* The idle thread had better be ready!
|
|
|
|
|
*/
|
|
|
|
|
if(p->idle->state != Ready)
|
|
|
|
|
sysfatal("all threads are asleep");
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Run the idle thread.
|
|
|
|
|
*/
|
2003-12-06 18:05:27 +00:00
|
|
|
unlock(&p->readylock);
|
2003-12-11 17:48:38 +00:00
|
|
|
_threaddebug(DBGSCHED, "running idle thread", p->nthreads);
|
2003-12-06 18:05:27 +00:00
|
|
|
return p->idle;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* Wait until one of our threads is readied (by another proc!).
|
|
|
|
|
*/
|
2003-12-11 17:48:38 +00:00
|
|
|
q->asleep = 1;
|
2004-09-17 03:34:32 +00:00
|
|
|
p->rend.l = &p->readylock;
|
2004-11-08 16:03:20 +00:00
|
|
|
while(q->asleep){
|
|
|
|
|
_procsleep(&p->rend);
|
|
|
|
|
_threadschednote();
|
|
|
|
|
}
|
2004-09-23 03:01:36 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Maybe we were awakened to exit?
|
|
|
|
|
*/
|
2004-10-22 17:15:30 +00:00
|
|
|
if(_threadexitsallstatus){
|
|
|
|
|
_threaddebug(DBGSCHED, "time to exit");
|
2004-09-17 03:34:32 +00:00
|
|
|
_exits(_threadexitsallstatus);
|
2004-10-22 17:15:30 +00:00
|
|
|
}
|
2004-09-23 03:01:36 +00:00
|
|
|
assert(q->head != nil);
|
2003-09-30 17:47:42 +00:00
|
|
|
}
|
2004-09-23 03:01:36 +00:00
|
|
|
|
2003-09-30 17:47:42 +00:00
|
|
|
t = q->head;
|
|
|
|
|
q->head = t->next;
|
|
|
|
|
unlock(&p->readylock);
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
return t;
|
2003-09-30 17:47:42 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* Add a newly-ready thread to its proc's run queue.
|
|
|
|
|
*/
|
2003-09-30 17:47:42 +00:00
|
|
|
void
|
|
|
|
|
_threadready(Thread *t)
|
|
|
|
|
{
|
|
|
|
|
Tqueue *q;
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* The idle thread does not go on the run queue.
|
|
|
|
|
*/
|
2003-12-11 17:48:38 +00:00
|
|
|
if(t == t->proc->idle){
|
|
|
|
|
_threaddebug(DBGSCHED, "idle thread is ready");
|
2003-12-06 18:05:27 +00:00
|
|
|
return;
|
2003-12-11 17:48:38 +00:00
|
|
|
}
|
2003-12-06 18:05:27 +00:00
|
|
|
|
2003-09-30 17:47:42 +00:00
|
|
|
assert(t->state == Ready);
|
2004-09-23 03:01:36 +00:00
|
|
|
_threaddebug(DBGSCHED, "readying %d.%d", t->proc->id, t->id);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Add thread to run queue.
|
|
|
|
|
*/
|
2003-09-30 17:47:42 +00:00
|
|
|
q = &t->proc->ready;
|
|
|
|
|
lock(&t->proc->readylock);
|
2004-09-23 03:01:36 +00:00
|
|
|
|
2003-09-30 17:47:42 +00:00
|
|
|
t->next = nil;
|
2004-09-23 03:01:36 +00:00
|
|
|
if(q->head == nil)
|
2003-09-30 17:47:42 +00:00
|
|
|
q->head = t;
|
|
|
|
|
else
|
|
|
|
|
q->tail->next = t;
|
|
|
|
|
q->tail = t;
|
2004-09-23 03:01:36 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Wake proc scheduler if it is sleeping.
|
|
|
|
|
*/
|
2003-09-30 17:47:42 +00:00
|
|
|
if(q->asleep){
|
2003-11-23 18:18:00 +00:00
|
|
|
assert(q->asleep == 1);
|
2003-09-30 17:47:42 +00:00
|
|
|
q->asleep = 0;
|
2004-09-17 03:34:32 +00:00
|
|
|
_procwakeup(&t->proc->rend);
|
|
|
|
|
}
|
|
|
|
|
unlock(&t->proc->readylock);
|
2003-09-30 17:47:42 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* Mark the given thread as the idle thread.
|
|
|
|
|
* Since the idle thread was just created, it is sitting
|
|
|
|
|
* somewhere on the ready queue.
|
|
|
|
|
*/
|
2003-12-06 18:05:27 +00:00
|
|
|
void
|
2004-09-23 03:01:36 +00:00
|
|
|
_threadsetidle(int id)
|
2003-12-06 18:05:27 +00:00
|
|
|
{
|
|
|
|
|
Tqueue *q;
|
2004-09-23 03:01:36 +00:00
|
|
|
Thread *t, **l, *last;
|
2003-12-06 18:05:27 +00:00
|
|
|
Proc *p;
|
|
|
|
|
|
|
|
|
|
p = _threadgetproc();
|
2004-09-23 03:01:36 +00:00
|
|
|
|
2003-12-06 18:05:27 +00:00
|
|
|
lock(&p->readylock);
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* 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);
|
2004-05-11 17:51:27 +00:00
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
/*
|
|
|
|
|
* 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);
|
2003-09-30 17:47:42 +00:00
|
|
|
}
|
|
|
|
|
|
2004-11-08 16:03:20 +00:00
|
|
|
/*
|
|
|
|
|
* Mark proc as internal so that if all but internal procs exit, we exit.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
_threadinternalproc(void)
|
|
|
|
|
{
|
|
|
|
|
Proc *p;
|
|
|
|
|
|
|
|
|
|
p = _threadgetproc();
|
|
|
|
|
if(p->internal)
|
|
|
|
|
return;
|
|
|
|
|
lock(&_threadpq.lock);
|
|
|
|
|
if(p->internal == 0){
|
|
|
|
|
p->internal = 1;
|
|
|
|
|
--_threadnprocs;
|
|
|
|
|
}
|
|
|
|
|
unlock(&_threadpq.lock);
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
static void
|
|
|
|
|
schedexit(Proc *p)
|
2004-03-05 01:12:11 +00:00
|
|
|
{
|
2004-09-23 03:01:36 +00:00
|
|
|
char ex[ERRMAX];
|
|
|
|
|
int n;
|
|
|
|
|
Proc **l;
|
2004-03-05 01:12:11 +00:00
|
|
|
|
2004-09-23 03:01:36 +00:00
|
|
|
_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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-11-08 16:03:20 +00:00
|
|
|
if(p->internal)
|
|
|
|
|
n = _threadnprocs;
|
|
|
|
|
else
|
|
|
|
|
n = --_threadnprocs;
|
2004-09-23 03:01:36 +00:00
|
|
|
unlock(&_threadpq.lock);
|
|
|
|
|
|
|
|
|
|
strncpy(ex, p->exitstr, sizeof ex);
|
|
|
|
|
ex[sizeof ex-1] = '\0';
|
|
|
|
|
free(p);
|
2004-10-22 17:15:30 +00:00
|
|
|
if(n == 0){
|
|
|
|
|
_threaddebug(DBGSCHED, "procexit; no more procs");
|
2004-11-08 16:03:20 +00:00
|
|
|
_kthreadexitallproc(ex);
|
2004-10-22 17:15:30 +00:00
|
|
|
}else{
|
|
|
|
|
_threaddebug(DBGSCHED, "procexit");
|
2004-11-08 16:03:20 +00:00
|
|
|
_kthreadexitproc(ex);
|
2004-10-22 17:15:30 +00:00
|
|
|
}
|
2004-03-05 01:12:11 +00:00
|
|
|
}
|
2004-09-23 03:01:36 +00:00
|
|
|
|