1 /* -----------------------------------------------------------------------------
3 * (c) The GHC Team 2001-
5 * The task manager subsystem. Tasks execute STG code, with this
6 * module providing the API which the Scheduler uses to control their
7 * creation and destruction.
9 * Two kinds of RTS builds uses 'tasks' - the SMP and the
10 * 'native thread-friendly' builds.
12 * The SMP build lets multiple tasks concurrently execute STG code,
13 * all sharing vital internal RTS data structures in a controlled manner
14 * (see details elsewhere...ToDo: fill in ref!)
16 * The 'threads' build has at any one time only one task executing STG
17 * code, other tasks are either busy executing code outside the RTS
18 * (e.g., a C call) or waiting for their turn to (again) evaluate some
19 * STG code. A task relinquishes its RTS token when it is asked to
20 * evaluate an external (C) call.
22 * -------------------------------------------------------------------------*/
24 #if defined(RTS_SUPPORTS_THREADS) /* to the end */
26 #include "OSThreads.h"
31 /* There's not all that much code that is shared between the
32 * SMP and threads version of the 'task manager.' A sign
33 * that the code ought to be structured differently..(Maybe ToDo).
37 * The following Task Manager-local variables are assumed to be
38 * accessed with the RTS lock in hand.
41 static TaskInfo* taskTable;
43 /* upper bound / the number of tasks created. */
45 /* number of tasks currently created */
47 static nat tasksAvailable;
52 startTaskManager( nat maxCount, void (*taskStart)(void) )
55 static int initialized = 0;
61 /* allocate table holding task metadata */
64 taskTable = stgMallocBytes(maxCount * sizeof(TaskInfo),
65 "startTaskManager:tasks");
67 /* and eagerly create them all. */
68 for (i = 0; i < maxCount; i++) {
78 startTask ( void (*taskStart)(void) )
83 r = createOSThread(&tid,taskStart);
85 barf("startTask: Can't create new task");
88 taskTable[taskCount].id = tid;
89 taskTable[taskCount].mut_time = 0.0;
90 taskTable[taskCount].mut_etime = 0.0;
91 taskTable[taskCount].gc_time = 0.0;
92 taskTable[taskCount].gc_etime = 0.0;
93 taskTable[taskCount].elapsedtimestart = stat_getElapsedTime();
95 IF_DEBUG(scheduler,fprintf(stderr,"scheduler: Started task: %ld\n",tid););
104 /* Don't want to use pthread_cancel, since we'd have to install
105 * these silly exception handlers (pthread_cleanup_{push,pop}) around
109 /* Cancel all our tasks */
110 for (i = 0; i < RtsFlags.ParFlags.nNodes; i++) {
111 pthread_cancel(taskTable[i].id);
114 /* Wait for all the tasks to terminate */
115 for (i = 0; i < maxCount; i++) {
116 IF_DEBUG(scheduler,fprintf(stderr,"scheduler: waiting for task %ld\n",
118 pthread_join(taskTable[i].id, NULL);
122 /* Send 'em all a SIGHUP. That should shut 'em up. */
123 await_death = maxCount;
124 for (i = 0; i < maxCount; i++) {
125 pthread_kill(taskTable[i].id,SIGTERM);
127 while (await_death > 0) {
135 /************ THREADS version *****************/
138 startTaskManager( nat maxCount, void (*taskStart)(void) )
140 /* In the threaded case, maxCount is used to limit the
141 the creation of worker tasks. Tasks are created lazily, i.e.,
142 when the current task gives up the token on executing
151 startTask ( void (*taskStart)(void) )
156 /* Locks assumed: rts_mutex */
158 /* If there are threads known to be waiting to do
159 useful work, no need to create a new task. */
160 if (tasksAvailable > 0) {
161 IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: %d tasks available, not creating new one.\n",tasksAvailable););
165 /* If the task limit has been reached, just return. */
166 if (maxTasks > 0 && taskCount == maxTasks) {
167 IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: task limit (%d) reached, not creating new one.\n",maxTasks));
172 r = createOSThread(&tid,taskStart);
174 barf("startTask: Can't create new task");
179 IF_DEBUG(scheduler,fprintf(stderr,"scheduler: Started task (%d): %ld\n", taskCount, tid););
184 * When the RTS thread ends up performing a call-out,
185 * we need to know whether there'll be other tasks/threads
186 * to take over RTS responsibilities. The 'tasksAvailable'
187 * variable holds the number of threads that are _blocked
188 * waiting to enter the RTS_ (or soon will be). Equipped
189 * with that count, startTask() is able to make an informed
190 * decision on whether or not to create a new thread.
192 * Two functions control increments / decrements of
195 * - taskNotAvailable() : called whenever a task/thread
196 * has acquired the RTS lock, i.e., always called by
197 * a thread that holds the rts_mutex lock.
199 * - taskAvailable(): called whenever a task/thread
200 * is about to try to grab the RTS lock. The task manager
201 * and scheduler will only call this whenever it is
202 * in possession of the rts_mutex lock, i.e.,
203 * - when a new task is created in startTask().
204 * - when the scheduler gives up the RTS token to
205 * let threads waiting to return from an external
212 if (tasksAvailable > 0) {
242 #endif /* RTS_SUPPORTS_THREADS */