1 /* -----------------------------------------------------------------------------
2 * $Id: Schedule.c,v 1.2 1998/12/02 13:28:44 simonm Exp $
6 * ---------------------------------------------------------------------------*/
14 #include "StgStartup.h"
18 #include "StgMiscClosures.h"
20 #include "Evaluator.h"
24 #include "StablePtr.h"
25 #include "Profiling.h"
28 StgTSO *run_queue_hd, *run_queue_tl;
29 StgTSO *blocked_queue_hd, *blocked_queue_tl;
30 StgTSO *ccalling_threads;
32 #define MAX_SCHEDULE_NESTING 256
34 StgTSO *main_threads[MAX_SCHEDULE_NESTING];
36 static void GetRoots(void);
37 static StgTSO *threadStackOverflow(StgTSO *tso);
39 /* flag set by signal handler to precipitate a context switch */
41 /* if this flag is set as well, give up execution */
42 static nat interrupted;
44 /* Next thread ID to allocate */
45 StgThreadID next_thread_id = 1;
48 * Pointers to the state of the current thread.
49 * Rule of thumb: if CurrentTSO != NULL, then we're running a Haskell
50 * thread. If CurrentTSO == NULL, then we're at the scheduler level.
53 StgRegTable MainRegTable;
56 * The thread state for the main thread.
60 /* The smallest stack size that makes any sense is:
61 * RESERVED_STACK_WORDS (so we can get back from the stack overflow)
62 * + sizeofW(StgStopFrame) (the stg_stop_thread_info frame)
63 * + 1 (the realworld token for an IO thread)
64 * + 1 (the closure to enter)
66 * A thread with this stack will bomb immediately with a stack
67 * overflow, which will increase its stack size.
70 #define MIN_STACK_WORDS (RESERVED_STACK_WORDS + sizeofW(StgStopFrame) + 2)
72 /* -----------------------------------------------------------------------------
75 The new thread starts with the given stack size. Before the
76 scheduler can run, however, this thread needs to have a closure
77 (and possibly some arguments) pushed on its stack. See
78 pushClosure() in Schedule.h.
80 createGenThread() and createIOThread() (in Schedule.h) are
81 convenient packaged versions of this function.
82 -------------------------------------------------------------------------- */
85 createThread(nat stack_size)
89 tso = (StgTSO *)allocate(stack_size);
91 initThread(tso, stack_size);
96 initThread(StgTSO *tso, nat stack_size)
98 stack_size -= TSO_STRUCT_SIZEW;
100 /* catch ridiculously small stack sizes */
101 if (stack_size < MIN_STACK_WORDS) {
102 stack_size = MIN_STACK_WORDS;
105 SET_INFO(tso,&TSO_info);
106 tso->whatNext = ThreadEnterGHC;
107 tso->state = tso_state_runnable;
108 tso->id = next_thread_id++;
110 tso->splim = (P_)&(tso->stack) + RESERVED_STACK_WORDS;
111 tso->stack_size = stack_size;
112 tso->max_stack_size = RtsFlags.GcFlags.maxStkSize - TSO_STRUCT_SIZEW;
113 tso->sp = (P_)&(tso->stack) + stack_size;
116 tso->prof.CCCS = CCS_MAIN;
119 /* put a stop frame on the stack */
120 tso->sp -= sizeofW(StgStopFrame);
121 SET_HDR(stgCast(StgClosure*,tso->sp),
122 (StgInfoTable *)&stg_stop_thread_info,
124 tso->su = stgCast(StgUpdateFrame*,tso->sp);
126 IF_DEBUG(scheduler,belch("Initialised thread %lld, stack size = %lx words\n",
127 tso->id, tso->stack_size));
129 /* Put the new thread on the head of the runnable queue.
130 * The caller of createThread better push an appropriate closure
131 * on this thread's stack before the scheduler is invoked.
133 tso->link = run_queue_hd;
135 if (run_queue_tl == END_TSO_QUEUE) {
139 IF_DEBUG(scheduler,printTSO(tso));
142 /* -----------------------------------------------------------------------------
143 Delete a thread - reverting all blackholes to (something
144 equivalent to) their former state.
146 We create an AP_UPD for every UpdateFrame on the stack.
147 Entering one of these AP_UPDs pushes everything from the corresponding
148 update frame upwards onto the stack. (Actually, it pushes everything
149 up to the next update frame plus a pointer to the next AP_UPD
150 object. Entering the next AP_UPD object pushes more onto the
151 stack until we reach the last AP_UPD object - at which point
152 the stack should look exactly as it did when we killed the TSO
153 and we can continue execution by entering the closure on top of
155 -------------------------------------------------------------------------- */
157 void deleteThread(StgTSO *tso)
159 StgUpdateFrame* su = tso->su;
162 /* Thread already dead? */
163 if (tso->whatNext == ThreadComplete || tso->whatNext == ThreadKilled) {
167 IF_DEBUG(scheduler, belch("Killing thread %lld.", tso->id));
169 tso->whatNext = ThreadKilled; /* changed to ThreadComplete in schedule() */
170 tso->link = END_TSO_QUEUE; /* Just to be on the safe side... */
172 /* Threads that finish normally leave Su pointing to the word
173 * beyond the top of the stack, and Sp pointing to the last word
174 * on the stack, which is the return value of the thread.
176 if ((P_)tso->su >= tso->stack + tso->stack_size
177 || get_itbl(tso->su)->type == STOP_FRAME) {
182 fprintf(stderr, "Freezing TSO stack\n");
186 /* The stack freezing code assumes there's a closure pointer on
187 * the top of the stack. This isn't always the case with compiled
188 * code, so we have to push a dummy closure on the top which just
189 * returns to the next return address on the stack.
191 if (LOOKS_LIKE_GHC_INFO(*sp)) {
192 *(--sp) = (W_)&dummy_ret_closure;
196 int words = (stgCast(StgPtr,su) - stgCast(StgPtr,sp)) - 1;
198 StgAP_UPD* ap = stgCast(StgAP_UPD*,allocate(AP_sizeW(words)));
200 /* First build an AP_UPD consisting of the stack chunk above the
201 * current update frame, with the top word on the stack as the
206 /* if (words == 0) { -- optimisation
207 ap = stgCast(StgAP_UPD*,*stgCast(StgPtr*,sp)++);
210 ap->fun = stgCast(StgClosure*,*stgCast(StgPtr*,sp)++);
211 for(i=0; i < (nat)words; ++i) {
212 payloadWord(ap,i) = *sp++;
216 switch (get_itbl(su)->type) {
220 SET_HDR(ap,&AP_UPD_info,su->header.prof.ccs /* ToDo */);
223 fprintf(stderr, "Updating ");
224 printPtr(stgCast(StgPtr,su->updatee));
225 fprintf(stderr, " with ");
226 printObj(stgCast(StgClosure*,ap));
229 /* Replace the updatee with an indirection - happily
230 * this will also wake up any threads currently
231 * waiting on the result.
233 UPD_IND(su->updatee,ap); /* revert the black hole */
235 sp += sizeofW(StgUpdateFrame) -1;
236 sp[0] = stgCast(StgWord,ap); /* push onto stack */
242 StgCatchFrame *cf = (StgCatchFrame *)su;
245 /* We want a PAP, not an AP_UPD. Fortunately, the
248 SET_HDR(ap,&PAP_info,su->header.prof.ccs /* ToDo */);
250 /* now build o = FUN(catch,ap,handler) */
251 o = stgCast(StgClosure*, allocate(sizeofW(StgClosure)+2));
252 SET_HDR(o,&catch_info,su->header.prof.ccs /* ToDo */);
253 payloadCPtr(o,0) = stgCast(StgClosure*,ap);
254 payloadCPtr(o,1) = cf->handler;
257 fprintf(stderr, "Built ");
258 printObj(stgCast(StgClosure*,o));
261 /* pop the old handler and put o on the stack */
263 sp += sizeofW(StgCatchFrame) - 1;
270 StgSeqFrame *sf = (StgSeqFrame *)su;
273 SET_HDR(ap,&PAP_info,su->header.prof.ccs /* ToDo */);
275 /* now build o = FUN(seq,ap) */
276 o = stgCast(StgClosure*, allocate(sizeofW(StgClosure)+1));
277 SET_HDR(o,&seq_info,su->header.prof.ccs /* ToDo */);
278 payloadCPtr(o,0) = stgCast(StgClosure*,ap);
281 fprintf(stderr, "Built ");
282 printObj(stgCast(StgClosure*,o));
285 /* pop the old handler and put o on the stack */
287 sp += sizeofW(StgSeqFrame) - 1;
301 void initScheduler(void)
303 run_queue_hd = END_TSO_QUEUE;
304 run_queue_tl = END_TSO_QUEUE;
305 blocked_queue_hd = END_TSO_QUEUE;
306 blocked_queue_tl = END_TSO_QUEUE;
307 ccalling_threads = END_TSO_QUEUE;
308 next_main_thread = 0;
313 enteredCAFs = END_CAF_LIST;
316 /* -----------------------------------------------------------------------------
317 Main scheduling loop.
319 We use round-robin scheduling, each thread returning to the
320 scheduler loop when one of these conditions is detected:
324 * timer expires (thread yields)
327 -------------------------------------------------------------------------- */
329 SchedulerStatus schedule(StgTSO *main, StgClosure **ret_val)
332 StgThreadReturnCode ret;
336 /* Return value is NULL by default, it is only filled in if the
337 * main thread completes successfully.
339 if (ret_val) { *ret_val = NULL; }
341 /* Save away a pointer to the main thread so that we can keep track
342 * of it should a garbage collection happen. We keep a stack of
343 * main threads in order to support scheduler re-entry. We can't
344 * use the normal TSO linkage for this stack, because the main TSO
345 * may need to be linked onto other queues.
347 main_threads[next_main_thread] = main;
348 MainTSO = &main_threads[next_main_thread];
351 fprintf(stderr, "Scheduler entered: nesting = %d\n",
354 /* Are we being re-entered?
356 if (CurrentTSO != NULL) {
357 /* This happens when a _ccall_gc from Haskell ends up re-entering
360 * Block the current thread (put it on the ccalling_queue) and
361 * continue executing. The calling thread better have stashed
362 * away its state properly and left its stack with a proper stack
365 threadPaused(CurrentTSO);
366 CurrentTSO->link = ccalling_threads;
367 ccalling_threads = CurrentTSO;
368 in_ccall_gc = rtsTrue;
370 fprintf(stderr, "Re-entry, thread %lld did a _ccall_gc\n",
373 in_ccall_gc = rtsFalse;
376 /* Take a thread from the run queue.
379 if (t != END_TSO_QUEUE) {
380 run_queue_hd = t->link;
381 t->link = END_TSO_QUEUE;
382 if (run_queue_hd == END_TSO_QUEUE) {
383 run_queue_tl = END_TSO_QUEUE;
387 while (t != END_TSO_QUEUE) {
390 /* If we have more threads on the run queue, set up a context
391 * switch at some point in the future.
393 if (run_queue_hd != END_TSO_QUEUE) {
398 IF_DEBUG(scheduler, belch("Running thread %lld...\n", t->id));
400 /* Run the current thread */
401 switch (t->whatNext) {
404 /* thread already killed. Drop it and carry on. */
407 ret = StgRun((StgFunPtr) stg_enterStackTop);
410 ret = StgRun((StgFunPtr) stg_returnToStackTop);
412 case ThreadEnterHugs:
415 IF_DEBUG(scheduler,belch("entering Hugs"));
417 /* CHECK_SENSIBLE_REGS(); */
419 StgClosure* c = stgCast(StgClosure*,*Sp);
427 barf("Panic: entered a BCO but no bytecode interpreter in this build");
430 barf("schedule: invalid whatNext field");
433 /* We may have garbage collected while running the thread
434 * (eg. something nefarious like _ccall_GC_ performGC), and hence
435 * CurrentTSO may have moved. Update t to reflect this.
440 /* Costs for the scheduler are assigned to CCS_SYSTEM */
448 IF_DEBUG(scheduler,belch("Thread %lld stopped: HeapOverflow\n", t->id));
450 PUSH_ON_RUN_QUEUE(t);
451 GarbageCollect(GetRoots);
455 IF_DEBUG(scheduler,belch("Thread %lld stopped, StackOverflow\n", t->id));
458 /* enlarge the stack */
459 StgTSO *new_t = threadStackOverflow(t);
461 /* This TSO has moved, so update any pointers to it from the
462 * main thread stack. It better not be on any other queues...
465 for (i = 0; i < next_main_thread; i++) {
466 if (main_threads[i] == t) {
467 main_threads[i] = new_t;
472 PUSH_ON_RUN_QUEUE(t);
477 if (t->whatNext == ThreadEnterHugs) {
478 /* ToDo: or maybe a timer expired when we were in Hugs?
479 * or maybe someone hit ctrl-C
481 belch("Thread %lld stopped to switch to Hugs\n", t->id);
483 belch("Thread %lld stopped, timer expired\n", t->id);
488 IF_DEBUG(scheduler,belch("Scheduler interrupted - returning"));
490 while (run_queue_hd != END_TSO_QUEUE) {
491 run_queue_hd = t->link;
494 run_queue_tl = END_TSO_QUEUE;
495 /* ToDo: should I do the same with blocked queues? */
499 /* Put the thread back on the run queue, at the end.
500 * t->link is already set to END_TSO_QUEUE.
502 ASSERT(t->link == END_TSO_QUEUE);
503 if (run_queue_tl != END_TSO_QUEUE) {
504 ASSERT(get_itbl(run_queue_tl)->type == TSO);
505 if (run_queue_hd == run_queue_tl) {
506 run_queue_hd->link = t;
509 run_queue_tl->link = t;
512 run_queue_hd = run_queue_tl = t;
517 IF_DEBUG(scheduler,belch("Thread %lld stopped, blocking\n", t->id));
519 /* assume the thread has put itself on some blocked queue
525 IF_DEBUG(scheduler,belch("Thread %lld finished\n", t->id));
527 t->whatNext = ThreadComplete;
531 barf("schedule: invalid thread return code");
534 /* check for signals each time around the scheduler */
535 if (signals_pending()) {
536 start_signal_handlers();
539 /* If our main thread has finished or been killed, return.
540 * If we were re-entered as a result of a _ccall_gc, then
541 * pop the blocked thread off the ccalling_threads stack back
544 if ((*MainTSO)->whatNext == ThreadComplete
545 || (*MainTSO)->whatNext == ThreadKilled) {
548 CurrentTSO = ccalling_threads;
549 ccalling_threads = ccalling_threads->link;
551 if ((*MainTSO)->whatNext == ThreadComplete) {
552 /* we finished successfully, fill in the return value */
553 if (ret_val) { *ret_val = (StgClosure *)(*MainTSO)->sp[0]; };
562 if (t != END_TSO_QUEUE) {
563 run_queue_hd = t->link;
564 t->link = END_TSO_QUEUE;
565 if (run_queue_hd == END_TSO_QUEUE) {
566 run_queue_tl = END_TSO_QUEUE;
571 if (blocked_queue_hd != END_TSO_QUEUE) {
578 /* -----------------------------------------------------------------------------
579 Where are the roots that we know about?
581 - all the threads on the runnable queue
582 - all the threads on the blocked queue
583 - all the thread currently executing a _ccall_GC
584 - all the "main threads"
586 -------------------------------------------------------------------------- */
588 static void GetRoots(void)
592 run_queue_hd = (StgTSO *)MarkRoot((StgClosure *)run_queue_hd);
593 run_queue_tl = (StgTSO *)MarkRoot((StgClosure *)run_queue_tl);
595 blocked_queue_hd = (StgTSO *)MarkRoot((StgClosure *)blocked_queue_hd);
596 blocked_queue_tl = (StgTSO *)MarkRoot((StgClosure *)blocked_queue_tl);
598 ccalling_threads = (StgTSO *)MarkRoot((StgClosure *)ccalling_threads);
600 for (i = 0; i < next_main_thread; i++) {
601 main_threads[i] = (StgTSO *)MarkRoot((StgClosure *)main_threads[i]);
604 markStablePtrTable();
607 /* -----------------------------------------------------------------------------
610 This is the interface to the garbage collector from Haskell land.
611 We provide this so that external C code can allocate and garbage
612 collect when called from Haskell via _ccall_GC.
614 It might be useful to provide an interface whereby the programmer
615 can specify more roots (ToDo).
616 -------------------------------------------------------------------------- */
618 void (*extra_roots)(void);
623 GarbageCollect(GetRoots);
629 GetRoots(); /* the scheduler's roots */
630 extra_roots(); /* the user's roots */
634 performGCWithRoots(void (*get_roots)(void))
636 extra_roots = get_roots;
638 GarbageCollect(AllRoots);
641 /* -----------------------------------------------------------------------------
644 If the thread has reached its maximum stack size,
645 then bomb out. Otherwise relocate the TSO into a larger chunk of
646 memory and adjust its stack size appropriately.
647 -------------------------------------------------------------------------- */
650 threadStackOverflow(StgTSO *tso)
652 nat new_stack_size, new_tso_size, diff, stack_words;
656 if (tso->stack_size >= tso->max_stack_size) {
657 /* ToDo: just kill this thread? */
659 /* If we're debugging, just print out the top of the stack */
660 printStackChunk(tso->sp, stg_min(tso->stack+tso->stack_size,
663 stackOverflow(tso->max_stack_size);
666 /* Try to double the current stack size. If that takes us over the
667 * maximum stack size for this thread, then use the maximum instead.
668 * Finally round up so the TSO ends up as a whole number of blocks.
670 new_stack_size = stg_min(tso->stack_size * 2, tso->max_stack_size);
671 new_tso_size = (nat)BLOCK_ROUND_UP(new_stack_size * sizeof(W_) +
672 TSO_STRUCT_SIZE)/sizeof(W_);
673 new_stack_size = new_tso_size - TSO_STRUCT_SIZEW;
675 IF_DEBUG(scheduler, fprintf(stderr,"increasing stack size from %d words to %d.\n", tso->stack_size, new_stack_size));
677 dest = (StgTSO *)allocate(new_tso_size);
679 /* copy the TSO block and the old stack into the new area */
680 memcpy(dest,tso,TSO_STRUCT_SIZE);
681 stack_words = tso->stack + tso->stack_size - tso->sp;
682 new_sp = (P_)dest + new_tso_size - stack_words;
683 memcpy(new_sp, tso->sp, stack_words * sizeof(W_));
685 /* relocate the stack pointers... */
686 diff = (P_)new_sp - (P_)tso->sp; /* In *words* */
687 dest->su = (StgUpdateFrame *) ((P_)dest->su + diff);
689 dest->splim = (P_)dest->splim + (nat)((P_)dest - (P_)tso);
690 dest->stack_size = new_stack_size;
692 /* and relocate the update frame list */
693 relocate_TSO(tso, dest);
695 IF_DEBUG(sanity,checkTSO(tso,0)); /* Step 0 because we're not GC'ing. */
697 IF_DEBUG(scheduler,printTSO(dest));
699 if (tso == MainTSO) { /* hack */
705 /* -----------------------------------------------------------------------------
706 Wake up a queue that was blocked on some resource (usually a
707 computation in progress).
708 -------------------------------------------------------------------------- */
710 void awaken_blocked_queue(StgTSO *q)
714 while (q != END_TSO_QUEUE) {
715 ASSERT(get_itbl(q)->type == TSO);
718 PUSH_ON_RUN_QUEUE(tso);
719 IF_DEBUG(scheduler,belch("Waking up thread %lld", tso->id));
723 /* -----------------------------------------------------------------------------
725 - usually called inside a signal handler so it mustn't do anything fancy.
726 -------------------------------------------------------------------------- */
728 void interruptStgRts(void)