xref: /plan9-contrib/sys/src/libthread/sched.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "threadimpl.h"
5 
6 static Thread	*runthread(Proc*);
7 
8 static char *_psstate[] = {
9 	"Moribund",
10 	"Dead",
11 	"Exec",
12 	"Fork",
13 	"Running",
14 	"Ready",
15 	"Rendezvous",
16 };
17 
18 char*
19 psstate(int s)
20 {
21 	if(s < 0 || s >= nelem(_psstate))
22 		return "unknown";
23 	return _psstate[s];
24 }
25 
26 void
27 _schedinit(void *arg)
28 {
29 	Proc *p;
30 	Thread *t, **l;
31 
32 	p = arg;
33 	_threadsetproc(p);
34 	p->pid = getpid();
35 	while(setjmp(p->sched))
36 		;
37 	_threaddebug(DBGSCHED, "top of schedinit, _threadexitsallstatus=%p", _threadexitsallstatus);
38 	if(_threadexitsallstatus)
39 		exits(_threadexitsallstatus);
40 	lock(&p->lock);
41 	if((t=p->thread) != nil){
42 		p->thread = nil;
43 		if(t->moribund){
44 			t->state = Dead;
45 			for(l=&p->threads.head; *l; l=&(*l)->nextt)
46 				if(*l == t){
47 					*l = t->nextt;
48 					if(*l==nil)
49 						p->threads.tail = l;
50 					p->nthreads--;
51 					break;
52 				}
53 			unlock(&p->lock);
54 			if(t->inrendez){
55 				_threadflagrendez(t);
56 				_threadbreakrendez();
57 			}
58 			free(t->stk);
59 			free(t->cmdname);
60 			free(t);	/* XXX how do we know there are no references? */
61 			t = nil;
62 			_sched();
63 		}
64 		if(p->needexec){
65 			t->ret = _schedexec(&p->exec);
66 			p->needexec = 0;
67 		}
68 		if(p->newproc){
69 			t->ret = _schedfork(p->newproc);
70 			p->newproc = nil;
71 		}
72 		t->state = t->nextstate;
73 		if(t->state == Ready)
74 			_threadready(t);
75 	}
76 	unlock(&p->lock);
77 	_sched();
78 }
79 
80 void
81 _sched(void)
82 {
83 	Proc *p;
84 	Thread *t;
85 
86 Resched:
87 	p = _threadgetproc();
88 	if((t = p->thread) != nil){
89 		if((ulong)&p < (ulong)t->stk)	/* stack overflow */
90 			abort();
91 		_threaddebug(DBGSCHED, "pausing, state=%s", psstate(t->state));
92 		if(setjmp(t->sched)==0)
93 			longjmp(p->sched, 1);
94 		return;
95 	}else{
96 		t = runthread(p);
97 		if(t == nil){
98 			_threaddebug(DBGSCHED, "all threads gone; exiting");
99 			_schedexit(p);
100 		}
101 		_threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id);
102 		p->thread = t;
103 		if(t->moribund){
104 			_threaddebug(DBGSCHED, "%d.%d marked to die");
105 			goto Resched;
106 		}
107 		t->state = Running;
108 		t->nextstate = Ready;
109 		longjmp(t->sched, 1);
110 	}
111 }
112 
113 static Thread*
114 runthread(Proc *p)
115 {
116 	Thread *t;
117 	Tqueue *q;
118 
119 	if(p->nthreads==0)
120 		return nil;
121 	q = &p->ready;
122 	lock(&p->readylock);
123 	if(q->head == nil){
124 		q->asleep = 1;
125 		_threaddebug(DBGSCHED, "sleeping for more work");
126 		unlock(&p->readylock);
127 		while(rendezvous((ulong)q, 0) == ~0){
128 			if(_threadexitsallstatus)
129 				exits(_threadexitsallstatus);
130 		}
131 		/* lock picked up from _threadready */
132 	}
133 	t = q->head;
134 	q->head = t->next;
135 	unlock(&p->readylock);
136 	return t;
137 }
138 
139 void
140 _threadready(Thread *t)
141 {
142 	Tqueue *q;
143 
144 	assert(t->state == Ready);
145 	_threaddebug(DBGSCHED, "readying %d.%d", t->proc->pid, t->id);
146 	q = &t->proc->ready;
147 	lock(&t->proc->readylock);
148 	t->next = nil;
149 	if(q->head==nil)
150 		q->head = t;
151 	else
152 		*q->tail = t;
153 	q->tail = &t->next;
154 	if(q->asleep){
155 		q->asleep = 0;
156 		/* lock passes to runthread */
157 		_threaddebug(DBGSCHED, "waking process %d", t->proc->pid);
158 		while(rendezvous((ulong)q, 0) == ~0){
159 			if(_threadexitsallstatus)
160 				exits(_threadexitsallstatus);
161 		}
162 	}else
163 		unlock(&t->proc->readylock);
164 }
165 
166 void
167 yield(void)
168 {
169 	_sched();
170 }
171 
172