freeTaskManager: don't free Tasks that are still in use
[ghc-hetmet.git] / rts / Task.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team 2001-2005
4  *
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.
8  * 
9  * -------------------------------------------------------------------------*/
10
11 #include "Rts.h"
12 #include "RtsUtils.h"
13 #include "OSThreads.h"
14 #include "Task.h"
15 #include "Capability.h"
16 #include "Stats.h"
17 #include "RtsFlags.h"
18 #include "Storage.h"
19 #include "Schedule.h"
20 #include "Hash.h"
21 #include "Trace.h"
22
23 #if HAVE_SIGNAL_H
24 #include <signal.h>
25 #endif
26
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
31 static nat taskCount;
32 static nat tasksRunning;
33 static nat workerCount;
34
35 /* -----------------------------------------------------------------------------
36  * Remembering the current thread's Task
37  * -------------------------------------------------------------------------- */
38
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;
43 #else
44 Task *my_task;
45 #endif
46
47 /* -----------------------------------------------------------------------------
48  * Rest of the Task API
49  * -------------------------------------------------------------------------- */
50
51 void
52 initTaskManager (void)
53 {
54     static int initialized = 0;
55
56     if (!initialized) {
57         taskCount = 0;
58         workerCount = 0;
59         tasksRunning = 0;
60         initialized = 1;
61 #if defined(THREADED_RTS)
62         newThreadLocalKey(&currentTaskKey);
63 #endif
64     }
65 }
66
67
68 void
69 stopTaskManager (void)
70 {
71     debugTrace(DEBUG_sched, 
72                "stopping task manager, %d tasks still running",
73                tasksRunning);
74     /* nothing to do */
75 }
76
77
78 void
79 freeTaskManager (void)
80 {
81     Task *task, *next;
82
83     debugTrace(DEBUG_sched, "freeing task manager");
84
85     ACQUIRE_LOCK(&sched_mutex);
86     for (task = all_tasks; task != NULL; task = next) {
87         next = task->all_link;
88         if (task->stopped) {
89             // We only free resources if the Task is not in use.  A
90             // Task may still be in use if we have a Haskell thread in
91             // a foreign call while we are attempting to shut down the
92             // RTS (see conc059).
93 #if defined(THREADED_RTS)
94             closeCondition(&task->cond);
95             closeMutex(&task->lock);
96 #endif
97             stgFree(task);
98         }
99     }
100     all_tasks = NULL;
101     task_free_list = NULL;
102     RELEASE_LOCK(&sched_mutex);
103 }
104
105
106 static Task*
107 newTask (void)
108 {
109 #if defined(THREADED_RTS)
110     Ticks currentElapsedTime, currentUserTime;
111 #endif
112     Task *task;
113
114     task = stgMallocBytes(sizeof(Task), "newTask");
115     
116     task->cap  = NULL;
117     task->stopped = rtsFalse;
118     task->suspended_tso = NULL;
119     task->tso  = NULL;
120     task->stat = NoStatus;
121     task->ret  = NULL;
122     
123 #if defined(THREADED_RTS)
124     initCondition(&task->cond);
125     initMutex(&task->lock);
126     task->wakeup = rtsFalse;
127 #endif
128
129 #if defined(THREADED_RTS)
130     currentUserTime = getThreadCPUTime();
131     currentElapsedTime = getProcessElapsedTime();
132     task->mut_time = 0;
133     task->mut_etime = 0;
134     task->gc_time = 0;
135     task->gc_etime = 0;
136     task->muttimestart = currentUserTime;
137     task->elapsedtimestart = currentElapsedTime;
138 #endif
139
140     task->prev = NULL;
141     task->next = NULL;
142     task->return_link = NULL;
143
144     task->all_link = all_tasks;
145     all_tasks = task;
146
147     taskCount++;
148     workerCount++;
149
150     return task;
151 }
152
153 Task *
154 newBoundTask (void)
155 {
156     Task *task;
157
158     ASSERT_LOCK_HELD(&sched_mutex);
159     if (task_free_list == NULL) {
160         task = newTask();
161     } else {
162         task = task_free_list;
163         task_free_list = task->next;
164         task->next = NULL;
165         task->prev = NULL;
166         task->stopped = rtsFalse;
167     }
168 #if defined(THREADED_RTS)
169     task->id = osThreadId();
170 #endif
171     ASSERT(task->cap == NULL);
172
173     tasksRunning++;
174
175     taskEnter(task);
176
177     debugTrace(DEBUG_sched, "new task (taskCount: %d)", taskCount);
178     return task;
179 }
180
181 void
182 boundTaskExiting (Task *task)
183 {
184     task->stopped = rtsTrue;
185     task->cap = NULL;
186
187 #if defined(THREADED_RTS)
188     ASSERT(osThreadId() == task->id);
189 #endif
190     ASSERT(myTask() == task);
191     setMyTask(task->prev_stack);
192
193     tasksRunning--;
194
195     // sadly, we need a lock around the free task list. Todo: eliminate.
196     ACQUIRE_LOCK(&sched_mutex);
197     task->next = task_free_list;
198     task_free_list = task;
199     RELEASE_LOCK(&sched_mutex);
200
201     debugTrace(DEBUG_sched, "task exiting");
202 }
203
204 #ifdef THREADED_RTS
205 #define TASK_ID(t) (t)->id
206 #else
207 #define TASK_ID(t) (t)
208 #endif
209
210 void
211 discardTask (Task *task)
212 {
213     ASSERT_LOCK_HELD(&sched_mutex);
214     if (!task->stopped) {
215         debugTrace(DEBUG_sched, "discarding task %ld", (long)TASK_ID(task));
216         task->cap = NULL;
217         task->tso = NULL;
218         task->stopped = rtsTrue;
219         tasksRunning--;
220         task->next = task_free_list;
221         task_free_list = task;
222     }
223 }
224
225 void
226 taskTimeStamp (Task *task USED_IF_THREADS)
227 {
228 #if defined(THREADED_RTS)
229     Ticks currentElapsedTime, currentUserTime, elapsedGCTime;
230
231     currentUserTime = getThreadCPUTime();
232     currentElapsedTime = getProcessElapsedTime();
233
234     // XXX this is wrong; we want elapsed GC time since the
235     // Task started.
236     elapsedGCTime = stat_getElapsedGCTime();
237     
238     task->mut_time = 
239         currentUserTime - task->muttimestart - task->gc_time;
240     task->mut_etime = 
241         currentElapsedTime - task->elapsedtimestart - elapsedGCTime;
242
243     if (task->mut_time  < 0) { task->mut_time  = 0; }
244     if (task->mut_etime < 0) { task->mut_etime = 0; }
245 #endif
246 }
247
248 void
249 workerTaskStop (Task *task)
250 {
251 #if defined(THREADED_RTS)
252     OSThreadId id;
253     id = osThreadId();
254     ASSERT(task->id == id);
255     ASSERT(myTask() == task);
256 #endif
257
258     taskTimeStamp(task);
259     task->stopped = rtsTrue;
260     tasksRunning--;
261
262     ACQUIRE_LOCK(&sched_mutex);
263     task->next = task_free_list;
264     task_free_list = task;
265     RELEASE_LOCK(&sched_mutex);
266 }
267
268 void
269 resetTaskManagerAfterFork (void)
270 {
271     // TODO!
272     taskCount = 0;
273 }
274
275 #if defined(THREADED_RTS)
276
277 void
278 startWorkerTask (Capability *cap, 
279                  void OSThreadProcAttr (*taskStart)(Task *task))
280 {
281   int r;
282   OSThreadId tid;
283   Task *task;
284
285   workerCount++;
286
287   // A worker always gets a fresh Task structure.
288   task = newTask();
289
290   tasksRunning++;
291
292   // The lock here is to synchronise with taskStart(), to make sure
293   // that we have finished setting up the Task structure before the
294   // worker thread reads it.
295   ACQUIRE_LOCK(&task->lock);
296
297   task->cap = cap;
298
299   // Give the capability directly to the worker; we can't let anyone
300   // else get in, because the new worker Task has nowhere to go to
301   // sleep so that it could be woken up again.
302   ASSERT_LOCK_HELD(&cap->lock);
303   cap->running_task = task;
304
305   r = createOSThread(&tid, (OSThreadProc *)taskStart, task);
306   if (r != 0) {
307     sysErrorBelch("failed to create OS thread");
308     stg_exit(EXIT_FAILURE);
309   }
310
311   debugTrace(DEBUG_sched, "new worker task (taskCount: %d)", taskCount);
312
313   task->id = tid;
314
315   // ok, finished with the Task struct.
316   RELEASE_LOCK(&task->lock);
317 }
318
319 #endif /* THREADED_RTS */
320
321 #ifdef DEBUG
322
323 static void *taskId(Task *task)
324 {
325 #ifdef THREADED_RTS
326     return (void *)task->id;
327 #else
328     return (void *)task;
329 #endif
330 }
331
332 void printAllTasks(void);
333
334 void
335 printAllTasks(void)
336 {
337     Task *task;
338     for (task = all_tasks; task != NULL; task = task->all_link) {
339         debugBelch("task %p is %s, ", taskId(task), task->stopped ? "stopped" : "alive");
340         if (!task->stopped) {
341             if (task->cap) {
342                 debugBelch("on capability %d, ", task->cap->no);
343             }
344             if (task->tso) {
345               debugBelch("bound to thread %lu", (unsigned long)task->tso->id);
346             } else {
347                 debugBelch("worker");
348             }
349         }
350         debugBelch("\n");
351     }
352 }                      
353
354 #endif
355