[project @ 2005-04-05 12:19:54 by simonmar]
[ghc-hetmet.git] / ghc / rts / Task.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team 2001-
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  * Two kinds of RTS builds uses 'tasks' - the SMP and the
10  * 'native thread-friendly' builds. 
11  * 
12  * The SMP build lets multiple tasks concurrently execute STG code,
13  * all sharing vital internal RTS data structures in a controlled manner
14  * (see details elsewhere...ToDo: fill in ref!)
15  *
16  * The 'threads' build has at any one time only one task executing STG
17  * code, other tasks are either busy executing code outside the RTS
18  * (e.g., a C call) or waiting for their turn to (again) evaluate some
19  * STG code. A task relinquishes its RTS token when it is asked to
20  * evaluate an external (C) call.
21  *
22  * -------------------------------------------------------------------------*/
23 #include "Rts.h"
24 #if defined(RTS_SUPPORTS_THREADS) /* to the end */
25 #include "RtsUtils.h"
26 #include "OSThreads.h"
27 #include "Task.h"
28 #include "Stats.h"
29 #include "RtsFlags.h"
30 #include "Schedule.h"
31
32 #if HAVE_SIGNAL_H
33 #include <signal.h>
34 #endif
35
36 /* There's not all that much code that is shared between the
37  * SMP and threads version of the 'task manager.' A sign
38  * that the code ought to be structured differently..(Maybe ToDo).
39  */
40
41 /* 
42  * The following Task Manager-local variables are assumed to be
43  * accessed with the RTS lock in hand.
44  */
45 #if defined(SMP)
46 TaskInfo* taskTable;
47 #endif
48 /* upper bound / the number of tasks created. */
49 static nat maxTasks;  
50 /* number of tasks currently created */
51 static nat taskCount; 
52 static nat awaitDeath;
53
54 #if defined(SMP)
55 void
56 startTaskManager( nat maxCount, void (*taskStart)(void) )
57 {
58   nat i;
59   static int initialized = 0;
60   
61   if (!initialized) {
62   
63     taskCount = 0;
64     maxTasks = maxCount;
65     /* allocate table holding task metadata */
66   
67     if (maxCount > 0) {
68       taskTable = stgMallocBytes(maxCount * sizeof(TaskInfo),
69                                "startTaskManager:tasks");
70
71       /* and eagerly create them all. */
72       for (i = 0; i < maxCount; i++) {
73         startTask(taskStart);
74         taskCount++;
75       }
76     }
77     initialized = 1;
78   }
79 }
80
81 rtsBool
82 startTask ( void (*taskStart)(void) )
83 {
84   int r;
85   OSThreadId tid;
86
87   r = createOSThread(&tid,taskStart);
88   if (r != 0) {
89     barf("startTask: Can't create new task");
90   }
91
92   taskTable[taskCount].id = tid;
93   taskTable[taskCount].mut_time = 0.0;
94   taskTable[taskCount].mut_etime = 0.0;
95   taskTable[taskCount].gc_time = 0.0;
96   taskTable[taskCount].gc_etime = 0.0;
97   taskTable[taskCount].elapsedtimestart = stat_getElapsedTime();
98
99   IF_DEBUG(scheduler,debugBelch("scheduler: Started task: %ld\n",tid););
100   return rtsTrue;
101 }
102
103 void
104 stopTaskManager (void)
105 {
106   nat i;
107   OSThreadId tid = osThreadId();
108
109   /* Don't want to use pthread_cancel, since we'd have to install
110    * these silly exception handlers (pthread_cleanup_{push,pop}) around
111    * all our locks.
112    */
113 #if 0
114   /* Cancel all our tasks */
115   for (i = 0; i < RtsFlags.ParFlags.nNodes; i++) {
116     pthread_cancel(taskTable[i].id);
117   }
118   
119   /* Wait for all the tasks to terminate */
120   for (i = 0; i < maxCount; i++) {
121     IF_DEBUG(scheduler,debugBelch("scheduler: waiting for task %ld\n", 
122                                taskTable[i].id));
123     pthread_join(taskTable[i].id, NULL);
124   }
125 #endif
126
127   /* Send 'em all a SIGHUP.  That should shut 'em up. */
128   awaitDeath = taskCount==0 ? 0 : taskCount-1;
129   for (i = 0; i < taskCount; i++) {
130     /* don't cancel the thread running this piece of code. */
131     if ( taskTable[i].id != tid ) {
132       pthread_kill(taskTable[i].id,SIGTERM);
133     }
134   }
135   while (awaitDeath > 0) {
136     sched_yield();
137   }
138   
139   return;
140 }
141
142 void
143 resetTaskManagerAfterFork (void)
144 {
145         barf("resetTaskManagerAfterFork not implemented for SMP");
146 }
147
148 #else
149 /************ THREADS version *****************/
150
151 void
152 startTaskManager( nat maxCount,
153                   void (*taskStart)(void) STG_UNUSED )
154 {
155   /* In the threaded case, maxCount is used to limit the
156      the creation of worker tasks. Tasks are created lazily, i.e.,
157      when the current task gives up the token on executing
158      STG code.
159   */
160   maxTasks = maxCount;
161   taskCount = 0;
162 }
163
164 rtsBool
165 startTask ( void (*taskStart)(void) )
166 {
167   int r;
168   OSThreadId tid;
169   
170   /* If more than one worker thread is known to be blocked waiting
171      on thread_ready_cond, don't create a new one.
172   */
173   if ( rts_n_waiting_tasks > 0) {
174     IF_DEBUG(scheduler,debugBelch(
175                                "scheduler: startTask: %d tasks waiting, not creating new one.\n", 
176                                rts_n_waiting_tasks););
177     // the task will run as soon as a capability is available,
178     // so there's no need to wake it.
179     return rtsFalse;
180   }
181
182   /* If the task limit has been reached, just return. */
183   if (maxTasks > 0 && taskCount == maxTasks) {
184     IF_DEBUG(scheduler,debugBelch("scheduler: startTask: task limit (%d) reached, not creating new one.\n",maxTasks));
185     return rtsFalse;
186   }
187   
188   r = createOSThread(&tid,taskStart);
189   if (r != 0) {
190     barf("startTask: Can't create new task");
191   }
192   taskCount++;
193
194   IF_DEBUG(scheduler,debugBelch("scheduler: startTask: new task %ld (total_count: %d; waiting: %d)\n", tid, taskCount, rts_n_waiting_tasks););
195   return rtsTrue;
196 }
197
198
199
200 void
201 stopTaskManager ()
202 {
203
204 }
205
206 void
207 resetTaskManagerAfterFork ( void )
208 {
209         rts_n_waiting_tasks = 0;
210         taskCount = 0;
211 }
212 #endif
213
214
215 #endif /* RTS_SUPPORTS_THREADS */