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"
25 // Task lists and global counters.
26 // Locks required: sched_mutex.
27 Task *all_tasks = NULL;
28 static Task *task_free_list = NULL; // singly-linked
30 #define DEFAULT_MAX_WORKERS 64
31 static nat maxWorkers; // we won't create more workers than this
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;
60 maxWorkers = DEFAULT_MAX_WORKERS;
62 #if defined(THREADED_RTS)
63 newThreadLocalKey(¤tTaskKey);
70 stopTaskManager (void)
72 IF_DEBUG(scheduler, sched_belch("stopping task manager, %d tasks still running", tasksRunning));
79 #if defined(THREADED_RTS)
80 Ticks currentElapsedTime, currentUserTime;
84 task = stgMallocBytes(sizeof(Task), "newTask");
87 task->stopped = rtsFalse;
88 task->suspended_tso = NULL;
90 task->stat = NoStatus;
93 #if defined(THREADED_RTS)
94 initCondition(&task->cond);
95 initMutex(&task->lock);
96 task->wakeup = rtsFalse;
99 #if defined(THREADED_RTS)
100 currentUserTime = getThreadCPUTime();
101 currentElapsedTime = getProcessElapsedTime();
102 task->mut_time = 0.0;
103 task->mut_etime = 0.0;
105 task->gc_etime = 0.0;
106 task->muttimestart = currentUserTime;
107 task->elapsedtimestart = currentElapsedTime;
112 task->return_link = NULL;
114 task->all_link = all_tasks;
128 ASSERT_LOCK_HELD(&sched_mutex);
129 if (task_free_list == NULL) {
132 task = task_free_list;
133 task_free_list = task->next;
136 task->stopped = rtsFalse;
138 #if defined(THREADED_RTS)
139 task->id = osThreadId();
141 ASSERT(task->cap == NULL);
147 IF_DEBUG(scheduler,sched_belch("new task (taskCount: %d)", taskCount););
152 boundTaskExiting (Task *task)
154 task->stopped = rtsTrue;
157 #if defined(THREADED_RTS)
158 ASSERT(osThreadId() == task->id);
160 ASSERT(myTask() == task);
161 setMyTask(task->prev_stack);
165 // sadly, we need a lock around the free task list. Todo: eliminate.
166 ACQUIRE_LOCK(&sched_mutex);
167 task->next = task_free_list;
168 task_free_list = task;
169 RELEASE_LOCK(&sched_mutex);
171 IF_DEBUG(scheduler,sched_belch("task exiting"));
175 discardTask (Task *task)
177 ASSERT_LOCK_HELD(&sched_mutex);
178 if (!task->stopped) {
179 IF_DEBUG(scheduler,sched_belch("discarding task %p",(void *)task->id));
182 task->stopped = rtsTrue;
184 task->next = task_free_list;
185 task_free_list = task;
190 taskStop (Task *task)
192 #if defined(THREADED_RTS)
194 Ticks currentElapsedTime, currentUserTime, elapsedGCTime;
197 ASSERT(task->id == id);
198 ASSERT(myTask() == task);
200 currentUserTime = getThreadCPUTime();
201 currentElapsedTime = getProcessElapsedTime();
203 // XXX this is wrong; we want elapsed GC time since the
205 elapsedGCTime = stat_getElapsedGCTime();
208 currentUserTime - task->muttimestart - task->gc_time;
210 currentElapsedTime - task->elapsedtimestart - elapsedGCTime;
212 if (task->mut_time < 0.0) { task->mut_time = 0.0; }
213 if (task->mut_etime < 0.0) { task->mut_etime = 0.0; }
216 task->stopped = rtsTrue;
221 resetTaskManagerAfterFork (void)
227 #if defined(THREADED_RTS)
230 startWorkerTask (Capability *cap,
231 void OSThreadProcAttr (*taskStart)(Task *task))
237 if (workerCount >= maxWorkers) {
238 barf("too many workers; runaway worker creation?");
242 // A worker always gets a fresh Task structure.
247 // The lock here is to synchronise with taskStart(), to make sure
248 // that we have finished setting up the Task structure before the
249 // worker thread reads it.
250 ACQUIRE_LOCK(&task->lock);
254 // Give the capability directly to the worker; we can't let anyone
255 // else get in, because the new worker Task has nowhere to go to
256 // sleep so that it could be woken up again.
257 ASSERT_LOCK_HELD(&cap->lock);
258 cap->running_task = task;
260 r = createOSThread(&tid, (OSThreadProc *)taskStart, task);
262 barf("startTask: Can't create new task");
265 IF_DEBUG(scheduler,sched_belch("new worker task (taskCount: %d)", taskCount););
269 // ok, finished with the Task struct.
270 RELEASE_LOCK(&task->lock);
273 #endif /* THREADED_RTS */
277 static void *taskId(Task *task)
280 return (void *)task->id;
286 void printAllTasks(void);
292 for (task = all_tasks; task != NULL; task = task->all_link) {
293 debugBelch("task %p is %s, ", taskId(task), task->stopped ? "stopped" : "alive");
294 if (!task->stopped) {
296 debugBelch("on capability %d, ", task->cap->no);
299 debugBelch("bound to thread %d", task->tso->id);
301 debugBelch("worker");