blob: ac982da86645d289a17c03c786ef1fefdb9e7174 [file] [log] [blame]
rsc7966faa2004-09-23 03:01:36 +00001/*
2 * Thread scheduler.
3 */
rsccd7ddc92003-11-23 18:18:00 +00004#include "threadimpl.h"
rsc76193d72003-09-30 17:47:42 +00005
rsc7966faa2004-09-23 03:01:36 +00006static Thread *runthread(Proc*);
7static void schedexit(Proc*);
rsc76193d72003-09-30 17:47:42 +00008
rsc7966faa2004-09-23 03:01:36 +00009/*
10 * Main scheduling loop.
11 */
rsc76193d72003-09-30 17:47:42 +000012void
rsc7966faa2004-09-23 03:01:36 +000013_threadscheduler(void *arg)
rscc6687d42004-09-21 01:11:28 +000014{
15 Proc *p;
16 Thread *t;
17
rsc76193d72003-09-30 17:47:42 +000018 p = arg;
rsc7966faa2004-09-23 03:01:36 +000019
20 _threadlinkmain();
rsc19564552004-11-08 16:03:20 +000021 _threadsetproc(p);
rscc6687d42004-09-21 01:11:28 +000022
23 for(;;){
rsc7966faa2004-09-23 03:01:36 +000024 /*
25 * Clean up zombie children.
26 */
rsc7966faa2004-09-23 03:01:36 +000027
28 /*
29 * Find next thread to run.
30 */
31 _threaddebug(DBGSCHED, "runthread");
rscc6687d42004-09-21 01:11:28 +000032 t = runthread(p);
rsc7966faa2004-09-23 03:01:36 +000033 if(t == nil)
34 schedexit(p);
35
36 /*
37 * If it's ready, run it (might instead be marked to die).
38 */
rscc6687d42004-09-21 01:11:28 +000039 lock(&p->lock);
rsc7966faa2004-09-23 03:01:36 +000040 if(t->state == Ready){
41 _threaddebug(DBGSCHED, "running %d.%d", p->id, t->id);
42 t->state = Running;
43 t->nextstate = Ready;
44 p->thread = t;
45 unlock(&p->lock);
46 _swaplabel(&p->context, &t->context);
47 lock(&p->lock);
48 p->thread = nil;
49 }
50
51 /*
52 * If thread needs to die, kill it.
rsc5093c3f2004-10-22 18:45:08 +000053 * t->proc == p may not be true if we're
54 * trying to jump into the exec proc (see exec-unix.c).
rsc7966faa2004-09-23 03:01:36 +000055 */
rsc76193d72003-09-30 17:47:42 +000056 if(t->moribund){
rsc7966faa2004-09-23 03:01:36 +000057 _threaddebug(DBGSCHED, "moribund %d.%d", p->id, t->id);
rsc5093c3f2004-10-22 18:45:08 +000058 if(t->moribund != 1)
59 print("moribund broke %p %d\n", &t->moribund, t->moribund);
rsc76193d72003-09-30 17:47:42 +000060 assert(t->moribund == 1);
61 t->state = Dead;
rsc5093c3f2004-10-22 18:45:08 +000062 _procdelthread(p, t);
rsc76193d72003-09-30 17:47:42 +000063 unlock(&p->lock);
rsc7966faa2004-09-23 03:01:36 +000064 _threadfree(t);
rsc76193d72003-09-30 17:47:42 +000065 t = nil;
rscc6687d42004-09-21 01:11:28 +000066 continue;
rsc76193d72003-09-30 17:47:42 +000067 }
rsc5093c3f2004-10-22 18:45:08 +000068
69 /*
70 * If the thread has asked to move to another proc,
71 * let it go (only to be used in *very* special situations).
72 if(t->nextproc != p)
73 _procdelthread(p, t);
74 */
75
rsc7966faa2004-09-23 03:01:36 +000076 unlock(&p->lock);
77
78 /*
rsc5093c3f2004-10-22 18:45:08 +000079 * If the thread has asked to move to another proc,
80 * add it to the new proc.
81 */
82 if(t->nextproc != p){
83 // lock(&t->nextproc->lock);
84 // _procaddthread(t->nextproc, t);
85 // unlock(&t->nextproc->lock);
86 t->proc = t->nextproc;
87 }
88
89 /*
rsc7966faa2004-09-23 03:01:36 +000090 * If there is a request to run a function on the
91 * scheduling stack, do so.
92 */
93 if(p->schedfn){
94 _threaddebug(DBGSCHED, "schedfn");
95 p->schedfn(p);
96 p->schedfn = nil;
97 _threaddebug(DBGSCHED, "schedfn ended");
rsc76193d72003-09-30 17:47:42 +000098 }
rsc7966faa2004-09-23 03:01:36 +000099
100 /*
101 * Move the thread along.
102 */
rsc76193d72003-09-30 17:47:42 +0000103 t->state = t->nextstate;
rsc5093c3f2004-10-22 18:45:08 +0000104 _threaddebug(DBGSCHED, "moveon %d.%d", t->proc->id, t->id);
rsc76193d72003-09-30 17:47:42 +0000105 if(t->state == Ready)
106 _threadready(t);
107 }
rscc6687d42004-09-21 01:11:28 +0000108}
109
rsc7966faa2004-09-23 03:01:36 +0000110/*
111 * Called by thread to give up control of processor to scheduler.
112 */
rscc6687d42004-09-21 01:11:28 +0000113int
114_sched(void)
115{
116 Proc *p;
117 Thread *t;
118
119 p = _threadgetproc();
120 t = p->thread;
121 assert(t != nil);
rsc7966faa2004-09-23 03:01:36 +0000122 _swaplabel(&t->context, &p->context);
rscc6687d42004-09-21 01:11:28 +0000123 return p->nsched++;
rsc76193d72003-09-30 17:47:42 +0000124}
125
rsc7966faa2004-09-23 03:01:36 +0000126/*
127 * Called by thread to yield the processor to other threads.
128 * Returns number of other threads run between call and return.
129 */
rscc4097c22004-05-11 17:51:27 +0000130int
rsc76193d72003-09-30 17:47:42 +0000131yield(void)
132{
rscc4097c22004-05-11 17:51:27 +0000133 Proc *p;
134 int nsched;
135
136 p = _threadgetproc();
137 nsched = p->nsched;
138 return _sched() - nsched;
rsc76193d72003-09-30 17:47:42 +0000139}
140
rsc7966faa2004-09-23 03:01:36 +0000141/*
142 * Choose the next thread to run.
143 */
144static Thread*
145runthread(Proc *p)
rsc02a1a5c2004-03-05 01:12:11 +0000146{
rsc02a1a5c2004-03-05 01:12:11 +0000147 Thread *t;
rsc7966faa2004-09-23 03:01:36 +0000148 Tqueue *q;
149
150 /*
151 * No threads left?
152 */
153 if(p->nthreads==0 || (p->nthreads==1 && p->idle))
154 return nil;
155
rsc19564552004-11-08 16:03:20 +0000156 _threadschednote();
rsc7966faa2004-09-23 03:01:36 +0000157 lock(&p->readylock);
158 q = &p->ready;
159 if(q->head == nil){
160 /*
161 * Is this a single-process program with an idle thread?
162 */
163 if(p->idle){
164 /*
165 * The idle thread had better be ready!
166 */
167 if(p->idle->state != Ready)
168 sysfatal("all threads are asleep");
169
170 /*
171 * Run the idle thread.
172 */
173 unlock(&p->readylock);
174 _threaddebug(DBGSCHED, "running idle thread", p->nthreads);
175 return p->idle;
176 }
177
178 /*
179 * Wait until one of our threads is readied (by another proc!).
180 */
181 q->asleep = 1;
182 p->rend.l = &p->readylock;
rsc19564552004-11-08 16:03:20 +0000183 while(q->asleep){
184 _procsleep(&p->rend);
185 _threadschednote();
186 }
rsc7966faa2004-09-23 03:01:36 +0000187
188 /*
189 * Maybe we were awakened to exit?
190 */
rscba15d712004-10-22 17:15:30 +0000191 if(_threadexitsallstatus){
192 _threaddebug(DBGSCHED, "time to exit");
rsc7966faa2004-09-23 03:01:36 +0000193 _exits(_threadexitsallstatus);
rscba15d712004-10-22 17:15:30 +0000194 }
rsc7966faa2004-09-23 03:01:36 +0000195 assert(q->head != nil);
196 }
197
198 t = q->head;
199 q->head = t->next;
200 unlock(&p->readylock);
201
202 return t;
203}
204
205/*
206 * Add a newly-ready thread to its proc's run queue.
207 */
208void
209_threadready(Thread *t)
210{
211 Tqueue *q;
212
213 /*
214 * The idle thread does not go on the run queue.
215 */
216 if(t == t->proc->idle){
217 _threaddebug(DBGSCHED, "idle thread is ready");
218 return;
219 }
220
221 assert(t->state == Ready);
222 _threaddebug(DBGSCHED, "readying %d.%d", t->proc->id, t->id);
223
224 /*
225 * Add thread to run queue.
226 */
227 q = &t->proc->ready;
228 lock(&t->proc->readylock);
229
230 t->next = nil;
231 if(q->head == nil)
232 q->head = t;
233 else
234 q->tail->next = t;
235 q->tail = t;
236
237 /*
238 * Wake proc scheduler if it is sleeping.
239 */
240 if(q->asleep){
241 assert(q->asleep == 1);
242 q->asleep = 0;
243 _procwakeup(&t->proc->rend);
244 }
245 unlock(&t->proc->readylock);
246}
247
248/*
249 * Mark the given thread as the idle thread.
250 * Since the idle thread was just created, it is sitting
251 * somewhere on the ready queue.
252 */
253void
254_threadsetidle(int id)
255{
256 Tqueue *q;
257 Thread *t, **l, *last;
258 Proc *p;
rsc02a1a5c2004-03-05 01:12:11 +0000259
260 p = _threadgetproc();
rsc7966faa2004-09-23 03:01:36 +0000261
262 lock(&p->readylock);
263
264 /*
265 * Find thread on ready queue.
266 */
267 q = &p->ready;
268 for(l=&q->head, last=nil; (t=*l) != nil; l=&t->next, last=t)
269 if(t->id == id)
270 break;
271 assert(t != nil);
272
273 /*
274 * Remove it from ready queue.
275 */
276 *l = t->next;
277 if(t == q->head)
278 q->head = t->next;
279 if(t->next == nil)
280 q->tail = last;
281
282 /*
283 * Set as idle thread.
284 */
285 p->idle = t;
286 _threaddebug(DBGSCHED, "p->idle is %d\n", t->id);
287 unlock(&p->readylock);
rsc02a1a5c2004-03-05 01:12:11 +0000288}
rsc7966faa2004-09-23 03:01:36 +0000289
rsc19564552004-11-08 16:03:20 +0000290/*
291 * Mark proc as internal so that if all but internal procs exit, we exit.
292 */
293void
294_threadinternalproc(void)
295{
296 Proc *p;
297
298 p = _threadgetproc();
299 if(p->internal)
300 return;
301 lock(&_threadpq.lock);
302 if(p->internal == 0){
303 p->internal = 1;
304 --_threadnprocs;
305 }
306 unlock(&_threadpq.lock);
307}
308
rsc7966faa2004-09-23 03:01:36 +0000309static void
310schedexit(Proc *p)
311{
312 char ex[ERRMAX];
313 int n;
314 Proc **l;
315
316 _threaddebug(DBGSCHED, "exiting proc %d", p->id);
317 lock(&_threadpq.lock);
318 for(l=&_threadpq.head; *l; l=&(*l)->next){
319 if(*l == p){
320 *l = p->next;
321 if(*l == nil)
322 _threadpq.tail = l;
323 break;
324 }
325 }
rsc19564552004-11-08 16:03:20 +0000326 if(p->internal)
327 n = _threadnprocs;
328 else
329 n = --_threadnprocs;
rsc7966faa2004-09-23 03:01:36 +0000330 unlock(&_threadpq.lock);
331
332 strncpy(ex, p->exitstr, sizeof ex);
333 ex[sizeof ex-1] = '\0';
334 free(p);
rscba15d712004-10-22 17:15:30 +0000335 if(n == 0){
336 _threaddebug(DBGSCHED, "procexit; no more procs");
rsc19564552004-11-08 16:03:20 +0000337 _kthreadexitallproc(ex);
rscba15d712004-10-22 17:15:30 +0000338 }else{
339 _threaddebug(DBGSCHED, "procexit");
rsc19564552004-11-08 16:03:20 +0000340 _kthreadexitproc(ex);
rscba15d712004-10-22 17:15:30 +0000341 }
rsc7966faa2004-09-23 03:01:36 +0000342}
343