Don't overwrite old memory with 0xaa when doing a realloc
[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 #if defined(THREADED_RTS)
88         closeCondition(&task->cond);
89         closeMutex(&task->lock);
90 #endif
91         next = task->all_link;
92         stgFree(task);
93     }
94     all_tasks = NULL;
95     task_free_list = NULL;
96     RELEASE_LOCK(&sched_mutex);
97 }
98
99
100 static Task*
101 newTask (void)
102 {
103 #if defined(THREADED_RTS)
104     Ticks currentElapsedTime, currentUserTime;
105 #endif
106     Task *task;
107
108     task = stgMallocBytes(sizeof(Task), "newTask");
109     
110     task->cap  = NULL;
111     task->stopped = rtsFalse;
112     task->suspended_tso = NULL;
113     task->tso  = NULL;
114     task->stat = NoStatus;
115     task->ret  = NULL;
116     
117 #if defined(THREADED_RTS)
118     initCondition(&task->cond);
119     initMutex(&task->lock);
120     task->wakeup = rtsFalse;
121 #endif
122
123 #if defined(THREADED_RTS)
124     currentUserTime = getThreadCPUTime();
125     currentElapsedTime = getProcessElapsedTime();
126     task->mut_time = 0;
127     task->mut_etime = 0;
128     task->gc_time = 0;
129     task->gc_etime = 0;
130     task->muttimestart = currentUserTime;
131     task->elapsedtimestart = currentElapsedTime;
132 #endif
133
134     task->prev = NULL;
135     task->next = NULL;
136     task->return_link = NULL;
137
138     task->all_link = all_tasks;
139     all_tasks = task;
140
141     taskCount++;
142     workerCount++;
143
144     return task;
145 }
146
147 Task *
148 newBoundTask (void)
149 {
150     Task *task;
151
152     ASSERT_LOCK_HELD(&sched_mutex);
153     if (task_free_list == NULL) {
154         task = newTask();
155     } else {
156         task = task_free_list;
157         task_free_list = task->next;
158         task->next = NULL;
159         task->prev = NULL;
160         task->stopped = rtsFalse;
161     }
162 #if defined(THREADED_RTS)
163     task->id = osThreadId();
164 #endif
165     ASSERT(task->cap == NULL);
166
167     tasksRunning++;
168
169     taskEnter(task);
170
171     debugTrace(DEBUG_sched, "new task (taskCount: %d)", taskCount);
172     return task;
173 }
174
175 void
176 boundTaskExiting (Task *task)
177 {
178     task->stopped = rtsTrue;
179     task->cap = NULL;
180
181 #if defined(THREADED_RTS)
182     ASSERT(osThreadId() == task->id);
183 #endif
184     ASSERT(myTask() == task);
185     setMyTask(task->prev_stack);
186
187     tasksRunning--;
188
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);
194
195     debugTrace(DEBUG_sched, "task exiting");
196 }
197
198 #ifdef THREADED_RTS
199 #define TASK_ID(t) (t)->id
200 #else
201 #define TASK_ID(t) (t)
202 #endif
203
204 void
205 discardTask (Task *task)
206 {
207     ASSERT_LOCK_HELD(&sched_mutex);
208     if (!task->stopped) {
209         debugTrace(DEBUG_sched, "discarding task %ld", (long)TASK_ID(task));
210         task->cap = NULL;
211         task->tso = NULL;
212         task->stopped = rtsTrue;
213         tasksRunning--;
214         task->next = task_free_list;
215         task_free_list = task;
216     }
217 }
218
219 void
220 taskTimeStamp (Task *task USED_IF_THREADS)
221 {
222 #if defined(THREADED_RTS)
223     Ticks currentElapsedTime, currentUserTime, elapsedGCTime;
224
225     currentUserTime = getThreadCPUTime();
226     currentElapsedTime = getProcessElapsedTime();
227
228     // XXX this is wrong; we want elapsed GC time since the
229     // Task started.
230     elapsedGCTime = stat_getElapsedGCTime();
231     
232     task->mut_time = 
233         currentUserTime - task->muttimestart - task->gc_time;
234     task->mut_etime = 
235         currentElapsedTime - task->elapsedtimestart - elapsedGCTime;
236
237     if (task->mut_time  < 0) { task->mut_time  = 0; }
238     if (task->mut_etime < 0) { task->mut_etime = 0; }
239 #endif
240 }
241
242 void
243 workerTaskStop (Task *task)
244 {
245 #if defined(THREADED_RTS)
246     OSThreadId id;
247     id = osThreadId();
248     ASSERT(task->id == id);
249     ASSERT(myTask() == task);
250 #endif
251
252     taskTimeStamp(task);
253     task->stopped = rtsTrue;
254     tasksRunning--;
255
256     ACQUIRE_LOCK(&sched_mutex);
257     task->next = task_free_list;
258     task_free_list = task;
259     RELEASE_LOCK(&sched_mutex);
260 }
261
262 void
263 resetTaskManagerAfterFork (void)
264 {
265     // TODO!
266     taskCount = 0;
267 }
268
269 #if defined(THREADED_RTS)
270
271 void
272 startWorkerTask (Capability *cap, 
273                  void OSThreadProcAttr (*taskStart)(Task *task))
274 {
275   int r;
276   OSThreadId tid;
277   Task *task;
278
279   workerCount++;
280
281   // A worker always gets a fresh Task structure.
282   task = newTask();
283
284   tasksRunning++;
285
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);
290
291   task->cap = cap;
292
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;
298
299   r = createOSThread(&tid, (OSThreadProc *)taskStart, task);
300   if (r != 0) {
301     sysErrorBelch("failed to create OS thread");
302     stg_exit(EXIT_FAILURE);
303   }
304
305   debugTrace(DEBUG_sched, "new worker task (taskCount: %d)", taskCount);
306
307   task->id = tid;
308
309   // ok, finished with the Task struct.
310   RELEASE_LOCK(&task->lock);
311 }
312
313 #endif /* THREADED_RTS */
314
315 #ifdef DEBUG
316
317 static void *taskId(Task *task)
318 {
319 #ifdef THREADED_RTS
320     return (void *)task->id;
321 #else
322     return (void *)task;
323 #endif
324 }
325
326 void printAllTasks(void);
327
328 void
329 printAllTasks(void)
330 {
331     Task *task;
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) {
335             if (task->cap) {
336                 debugBelch("on capability %d, ", task->cap->no);
337             }
338             if (task->tso) {
339               debugBelch("bound to thread %lu", (unsigned long)task->tso->id);
340             } else {
341                 debugBelch("worker");
342             }
343         }
344         debugBelch("\n");
345     }
346 }                      
347
348 #endif
349