1 /* -----------------------------------------------------------------------------
3 * (c) The GHC Team 2001-2005
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 * -------------------------------------------------------------------------*/
13 #include "OSThreads.h"
15 #include "Capability.h"
27 // Task lists and global counters.
28 // Locks required: sched_mutex.
29 Task *all_tasks = NULL;
30 static Task *task_free_list = NULL; // singly-linked
32 static nat tasksRunning;
33 static nat workerCount;
35 /* -----------------------------------------------------------------------------
36 * Remembering the current thread's Task
37 * -------------------------------------------------------------------------- */
39 // A thread-local-storage key that we can use to get access to the
40 // current thread's Task structure.
41 #if defined(THREADED_RTS)
42 ThreadLocalKey currentTaskKey;
47 /* -----------------------------------------------------------------------------
48 * Rest of the Task API
49 * -------------------------------------------------------------------------- */
52 initTaskManager (void)
54 static int initialized = 0;
61 #if defined(THREADED_RTS)
62 newThreadLocalKey(¤tTaskKey);
69 stopTaskManager (void)
71 debugTrace(DEBUG_sched,
72 "stopping task manager, %d tasks still running",
79 freeTaskManager (void)
83 debugTrace(DEBUG_sched, "freeing task manager");
85 ACQUIRE_LOCK(&sched_mutex);
86 for (task = all_tasks; task != NULL; task = next) {
87 #if defined(THREADED_RTS)
88 closeCondition(&task->cond);
89 closeMutex(&task->lock);
91 next = task->all_link;
95 task_free_list = NULL;
96 RELEASE_LOCK(&sched_mutex);
103 #if defined(THREADED_RTS)
104 Ticks currentElapsedTime, currentUserTime;
108 task = stgMallocBytes(sizeof(Task), "newTask");
111 task->stopped = rtsFalse;
112 task->suspended_tso = NULL;
114 task->stat = NoStatus;
117 #if defined(THREADED_RTS)
118 initCondition(&task->cond);
119 initMutex(&task->lock);
120 task->wakeup = rtsFalse;
123 #if defined(THREADED_RTS)
124 currentUserTime = getThreadCPUTime();
125 currentElapsedTime = getProcessElapsedTime();
130 task->muttimestart = currentUserTime;
131 task->elapsedtimestart = currentElapsedTime;
136 task->return_link = NULL;
138 task->all_link = all_tasks;
152 ASSERT_LOCK_HELD(&sched_mutex);
153 if (task_free_list == NULL) {
156 task = task_free_list;
157 task_free_list = task->next;
160 task->stopped = rtsFalse;
162 #if defined(THREADED_RTS)
163 task->id = osThreadId();
165 ASSERT(task->cap == NULL);
171 debugTrace(DEBUG_sched, "new task (taskCount: %d)", taskCount);
176 boundTaskExiting (Task *task)
178 task->stopped = rtsTrue;
181 #if defined(THREADED_RTS)
182 ASSERT(osThreadId() == task->id);
184 ASSERT(myTask() == task);
185 setMyTask(task->prev_stack);
189 // sadly, we need a lock around the free task list. Todo: eliminate.
190 ACQUIRE_LOCK(&sched_mutex);
191 task->next = task_free_list;
192 task_free_list = task;
193 RELEASE_LOCK(&sched_mutex);
195 debugTrace(DEBUG_sched, "task exiting");
199 #define TASK_ID(t) (t)->id
201 #define TASK_ID(t) (t)
205 discardTask (Task *task)
207 ASSERT_LOCK_HELD(&sched_mutex);
208 if (!task->stopped) {
209 debugTrace(DEBUG_sched, "discarding task %ld", (long)TASK_ID(task));
212 task->stopped = rtsTrue;
214 task->next = task_free_list;
215 task_free_list = task;
220 taskTimeStamp (Task *task USED_IF_THREADS)
222 #if defined(THREADED_RTS)
223 Ticks currentElapsedTime, currentUserTime, elapsedGCTime;
225 currentUserTime = getThreadCPUTime();
226 currentElapsedTime = getProcessElapsedTime();
228 // XXX this is wrong; we want elapsed GC time since the
230 elapsedGCTime = stat_getElapsedGCTime();
233 currentUserTime - task->muttimestart - task->gc_time;
235 currentElapsedTime - task->elapsedtimestart - elapsedGCTime;
237 if (task->mut_time < 0) { task->mut_time = 0; }
238 if (task->mut_etime < 0) { task->mut_etime = 0; }
243 workerTaskStop (Task *task)
245 #if defined(THREADED_RTS)
248 ASSERT(task->id == id);
249 ASSERT(myTask() == task);
253 task->stopped = rtsTrue;
256 ACQUIRE_LOCK(&sched_mutex);
257 task->next = task_free_list;
258 task_free_list = task;
259 RELEASE_LOCK(&sched_mutex);
263 resetTaskManagerAfterFork (void)
269 #if defined(THREADED_RTS)
272 startWorkerTask (Capability *cap,
273 void OSThreadProcAttr (*taskStart)(Task *task))
281 // A worker always gets a fresh Task structure.
286 // The lock here is to synchronise with taskStart(), to make sure
287 // that we have finished setting up the Task structure before the
288 // worker thread reads it.
289 ACQUIRE_LOCK(&task->lock);
293 // Give the capability directly to the worker; we can't let anyone
294 // else get in, because the new worker Task has nowhere to go to
295 // sleep so that it could be woken up again.
296 ASSERT_LOCK_HELD(&cap->lock);
297 cap->running_task = task;
299 r = createOSThread(&tid, (OSThreadProc *)taskStart, task);
301 sysErrorBelch("failed to create OS thread");
302 stg_exit(EXIT_FAILURE);
305 debugTrace(DEBUG_sched, "new worker task (taskCount: %d)", taskCount);
309 // ok, finished with the Task struct.
310 RELEASE_LOCK(&task->lock);
313 #endif /* THREADED_RTS */
317 static void *taskId(Task *task)
320 return (void *)task->id;
326 void printAllTasks(void);
332 for (task = all_tasks; task != NULL; task = task->all_link) {
333 debugBelch("task %p is %s, ", taskId(task), task->stopped ? "stopped" : "alive");
334 if (!task->stopped) {
336 debugBelch("on capability %d, ", task->cap->no);
339 debugBelch("bound to thread %lu", (unsigned long)task->tso->id);
341 debugBelch("worker");