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;
34 static int tasksInitialized = 0;
36 /* -----------------------------------------------------------------------------
37 * Remembering the current thread's Task
38 * -------------------------------------------------------------------------- */
40 // A thread-local-storage key that we can use to get access to the
41 // current thread's Task structure.
42 #if defined(THREADED_RTS)
43 ThreadLocalKey currentTaskKey;
48 /* -----------------------------------------------------------------------------
49 * Rest of the Task API
50 * -------------------------------------------------------------------------- */
53 initTaskManager (void)
55 if (!tasksInitialized) {
60 #if defined(THREADED_RTS)
61 newThreadLocalKey(¤tTaskKey);
67 freeTaskManager (void)
71 ASSERT_LOCK_HELD(&sched_mutex);
73 debugTrace(DEBUG_sched, "freeing task manager, %d tasks still running",
76 for (task = all_tasks; task != NULL; task = next) {
77 next = task->all_link;
79 // We only free resources if the Task is not in use. A
80 // Task may still be in use if we have a Haskell thread in
81 // a foreign call while we are attempting to shut down the
83 #if defined(THREADED_RTS)
84 closeCondition(&task->cond);
85 closeMutex(&task->lock);
91 task_free_list = NULL;
92 #if defined(THREADED_RTS)
93 freeThreadLocalKey(¤tTaskKey);
105 #if defined(THREADED_RTS)
106 Ticks currentElapsedTime, currentUserTime;
110 #define ROUND_TO_CACHE_LINE(x) ((((x)+63) / 64) * 64)
111 task = stgMallocBytes(ROUND_TO_CACHE_LINE(sizeof(Task)), "newTask");
114 task->stopped = rtsFalse;
115 task->suspended_tso = NULL;
117 task->stat = NoStatus;
120 #if defined(THREADED_RTS)
121 initCondition(&task->cond);
122 initMutex(&task->lock);
123 task->wakeup = rtsFalse;
126 #if defined(THREADED_RTS)
127 currentUserTime = getThreadCPUTime();
128 currentElapsedTime = getProcessElapsedTime();
133 task->muttimestart = currentUserTime;
134 task->elapsedtimestart = currentElapsedTime;
139 task->return_link = NULL;
141 task->all_link = all_tasks;
154 if (!tasksInitialized) {
155 errorBelch("newBoundTask: RTS is not initialised; call hs_init() first");
156 stg_exit(EXIT_FAILURE);
159 // ToDo: get rid of this lock in the common case. We could store
160 // a free Task in thread-local storage, for example. That would
161 // leave just one lock on the path into the RTS: cap->lock when
162 // acquiring the Capability.
163 ACQUIRE_LOCK(&sched_mutex);
165 if (task_free_list == NULL) {
168 task = task_free_list;
169 task_free_list = task->next;
172 task->stopped = rtsFalse;
174 #if defined(THREADED_RTS)
175 task->id = osThreadId();
177 ASSERT(task->cap == NULL);
183 RELEASE_LOCK(&sched_mutex);
185 debugTrace(DEBUG_sched, "new task (taskCount: %d)", taskCount);
190 boundTaskExiting (Task *task)
193 task->stopped = rtsTrue;
196 #if defined(THREADED_RTS)
197 ASSERT(osThreadId() == task->id);
199 ASSERT(myTask() == task);
200 setMyTask(task->prev_stack);
204 // sadly, we need a lock around the free task list. Todo: eliminate.
205 ACQUIRE_LOCK(&sched_mutex);
206 task->next = task_free_list;
207 task_free_list = task;
208 RELEASE_LOCK(&sched_mutex);
210 debugTrace(DEBUG_sched, "task exiting");
214 #define TASK_ID(t) (t)->id
216 #define TASK_ID(t) (t)
220 discardTask (Task *task)
222 ASSERT_LOCK_HELD(&sched_mutex);
223 if (!task->stopped) {
224 debugTrace(DEBUG_sched, "discarding task %ld", (long)TASK_ID(task));
226 if (task->tso == NULL) {
231 task->stopped = rtsTrue;
233 task->next = task_free_list;
234 task_free_list = task;
239 taskTimeStamp (Task *task USED_IF_THREADS)
241 #if defined(THREADED_RTS)
242 Ticks currentElapsedTime, currentUserTime, elapsedGCTime;
244 currentUserTime = getThreadCPUTime();
245 currentElapsedTime = getProcessElapsedTime();
247 // XXX this is wrong; we want elapsed GC time since the
249 elapsedGCTime = stat_getElapsedGCTime();
252 currentUserTime - task->muttimestart - task->gc_time;
254 currentElapsedTime - task->elapsedtimestart - elapsedGCTime;
256 if (task->mut_time < 0) { task->mut_time = 0; }
257 if (task->mut_etime < 0) { task->mut_etime = 0; }
262 workerTaskStop (Task *task)
264 #if defined(THREADED_RTS)
267 ASSERT(task->id == id);
268 ASSERT(myTask() == task);
273 task->stopped = rtsTrue;
277 ACQUIRE_LOCK(&sched_mutex);
278 task->next = task_free_list;
279 task_free_list = task;
280 RELEASE_LOCK(&sched_mutex);
284 resetTaskManagerAfterFork (void)
290 #if defined(THREADED_RTS)
293 startWorkerTask (Capability *cap,
294 void OSThreadProcAttr (*taskStart)(Task *task))
302 // A worker always gets a fresh Task structure.
307 // The lock here is to synchronise with taskStart(), to make sure
308 // that we have finished setting up the Task structure before the
309 // worker thread reads it.
310 ACQUIRE_LOCK(&task->lock);
314 // Give the capability directly to the worker; we can't let anyone
315 // else get in, because the new worker Task has nowhere to go to
316 // sleep so that it could be woken up again.
317 ASSERT_LOCK_HELD(&cap->lock);
318 cap->running_task = task;
320 r = createOSThread(&tid, (OSThreadProc *)taskStart, task);
322 sysErrorBelch("failed to create OS thread");
323 stg_exit(EXIT_FAILURE);
326 debugTrace(DEBUG_sched, "new worker task (taskCount: %d)", taskCount);
330 // ok, finished with the Task struct.
331 RELEASE_LOCK(&task->lock);
334 #endif /* THREADED_RTS */
338 static void *taskId(Task *task)
341 return (void *)task->id;
347 void printAllTasks(void);
353 for (task = all_tasks; task != NULL; task = task->all_link) {
354 debugBelch("task %p is %s, ", taskId(task), task->stopped ? "stopped" : "alive");
355 if (!task->stopped) {
357 debugBelch("on capability %d, ", task->cap->no);
360 debugBelch("bound to thread %lu", (unsigned long)task->tso->id);
362 debugBelch("worker");