[project @ 2002-02-06 01:26:14 by sof]
[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
31 /* There's not all that much code that is shared between the
32  * SMP and threads version of the 'task manager.' A sign
33  * that the code ought to be structured differently..(Maybe ToDo).
34  */
35
36 /* 
37  * The following Task Manager-local variables are assumed to be
38  * accessed with the RTS lock in hand.
39  */
40 #if defined(SMP)
41 static TaskInfo* taskTable;
42 #endif
43 /* upper bound / the number of tasks created. */
44 static nat maxTasks;  
45 /* number of tasks currently created */
46 static nat taskCount; 
47 static nat tasksAvailable;
48
49
50 #if defined(SMP)
51 void
52 startTaskManager( nat maxCount, void (*taskStart)(void) )
53 {
54   nat i;
55   static int initialized = 0;
56   
57   if (!initialized) {
58   
59     taskCount = 0;
60     maxTasks = maxCount;
61     /* allocate table holding task metadata */
62   
63     if (maxCount > 0) {
64       taskTable = stgMallocBytes(maxCount * sizeof(TaskInfo),
65                                "startTaskManager:tasks");
66
67       /* and eagerly create them all. */
68       for (i = 0; i < maxCount; i++) {
69         startTask(taskStart);
70         taskCount++;
71       }
72     }
73     initialized = 1;
74   }
75 }
76
77 void
78 startTask ( void (*taskStart)(void) )
79 {
80   int r;
81   OSThreadId tid;
82
83   r = createOSThread(&tid,taskStart);
84   if (r != 0) {
85     barf("startTask: Can't create new task");
86   }
87
88   taskTable[taskCount].id = tid;
89   taskTable[taskCount].mut_time = 0.0;
90   taskTable[taskCount].mut_etime = 0.0;
91   taskTable[taskCount].gc_time = 0.0;
92   taskTable[taskCount].gc_etime = 0.0;
93   taskTable[taskCount].elapsedtimestart = stat_getElapsedTime();
94
95   IF_DEBUG(scheduler,fprintf(stderr,"scheduler: Started task: %ld\n",tid););
96   return;
97 }
98
99 void
100 stopTaskManager ()
101 {
102   nat i;
103
104   /* Don't want to use pthread_cancel, since we'd have to install
105    * these silly exception handlers (pthread_cleanup_{push,pop}) around
106    * all our locks.
107    */
108 #if 0
109   /* Cancel all our tasks */
110   for (i = 0; i < RtsFlags.ParFlags.nNodes; i++) {
111     pthread_cancel(taskTable[i].id);
112   }
113   
114   /* Wait for all the tasks to terminate */
115   for (i = 0; i < maxCount; i++) {
116     IF_DEBUG(scheduler,fprintf(stderr,"scheduler: waiting for task %ld\n", 
117                                taskTable[i].id));
118     pthread_join(taskTable[i].id, NULL);
119   }
120 #endif
121
122   /* Send 'em all a SIGHUP.  That should shut 'em up. */
123   await_death = maxCount;
124   for (i = 0; i < maxCount; i++) {
125     pthread_kill(taskTable[i].id,SIGTERM);
126   }
127   while (await_death > 0) {
128     sched_yield();
129   }
130   
131   return;
132 }
133
134 #else
135 /************ THREADS version *****************/
136
137 void
138 startTaskManager( nat maxCount, void (*taskStart)(void) )
139 {
140   /* In the threaded case, maxCount is used to limit the
141      the creation of worker tasks. Tasks are created lazily, i.e.,
142      when the current task gives up the token on executing
143      STG code.
144   */
145   maxTasks = maxCount;
146   taskCount = 0;
147   tasksAvailable = 0;
148 }
149
150 void
151 startTask ( void (*taskStart)(void) )
152 {
153   int r;
154   OSThreadId tid;
155   
156   /* Locks assumed: rts_mutex */
157   
158   /* If there are threads known to be waiting to do
159      useful work, no need to create a new task. */
160   if (tasksAvailable > 0) {
161     IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: %d tasks available, not creating new one.\n",tasksAvailable););
162     return;
163   }
164
165   /* If the task limit has been reached, just return. */
166   if (maxTasks > 0 && taskCount == maxTasks) {
167     IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: task limit (%d) reached, not creating new one.\n",maxTasks));
168     return;
169   }
170   
171
172   r = createOSThread(&tid,taskStart);
173   if (r != 0) {
174     barf("startTask: Can't create new task");
175   }
176   taskCount++;
177   tasksAvailable++;
178
179   IF_DEBUG(scheduler,fprintf(stderr,"scheduler: Started task (%d): %ld\n", taskCount, tid););
180   return;
181 }
182
183 /*
184  *   When the RTS thread ends up performing a call-out, 
185  *   we need to know whether there'll be other tasks/threads
186  *   to take over RTS responsibilities. The 'tasksAvailable'
187  *   variable holds the number of threads that are _blocked
188  *   waiting to enter the RTS_ (or soon will be). Equipped
189  *   with that count, startTask() is able to make an informed
190  *   decision on whether or not to create a new thread.
191  * 
192  *   Two functions control increments / decrements of
193  *   'tasksAvailable':
194  *   
195  *      - taskNotAvailable() : called whenever a task/thread
196  *        has acquired the RTS lock, i.e., always called by
197  *        a thread that holds the rts_mutex lock.
198  *
199  *      - taskAvailable():     called whenever a task/thread
200  *        is about to try to grab the RTS lock. The task manager
201  *        and scheduler will only call this whenever it is 
202  *        in possession of the rts_mutex lock, i.e.,
203  *             - when a new task is created in startTask().
204  *             - when the scheduler gives up the RTS token to
205  *               let threads waiting to return from an external
206  *               call continue.
207  * 
208  */
209 void
210 taskNotAvailable()
211 {
212   if (tasksAvailable > 0) {
213     tasksAvailable--;
214   }
215   return;
216 }
217
218 void
219 taskAvailable()
220 {
221   tasksAvailable++;
222   return;
223 }
224
225
226
227 void
228 stopTaskManager ()
229 {
230
231 }
232 #endif
233
234
235 nat
236 getTaskCount ()
237 {
238   return taskCount;
239 }
240
241
242 #endif /* RTS_SUPPORTS_THREADS */