[project @ 2002-02-13 07:51:01 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 #include "Schedule.h"
31
32 /* There's not all that much code that is shared between the
33  * SMP and threads version of the 'task manager.' A sign
34  * that the code ought to be structured differently..(Maybe ToDo).
35  */
36
37 /* 
38  * The following Task Manager-local variables are assumed to be
39  * accessed with the RTS lock in hand.
40  */
41 #if defined(SMP)
42 static TaskInfo* taskTable;
43 #endif
44 /* upper bound / the number of tasks created. */
45 static nat maxTasks;  
46 /* number of tasks currently created */
47 static nat taskCount; 
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 }
148
149 void
150 startTask ( void (*taskStart)(void) )
151 {
152   int r;
153   OSThreadId tid;
154   
155   /* Locks assumed: rts_mutex */
156   
157   /* If more than one worker thread is known to be blocked waiting
158      on thread_ready_cond, signal it rather than creating a new one.
159   */
160   if ( rts_n_waiting_tasks > 0) {
161     IF_DEBUG(scheduler,fprintf(stderr,
162                                "scheduler: startTask: %d tasks waiting, not creating new one.\n", 
163                                rts_n_waiting_tasks););
164     signalCondition(&thread_ready_cond);
165     /* Not needed, but gives more 'interesting' thread schedules when testing */
166     yieldThread();
167     return;
168   }
169
170   /* If the task limit has been reached, just return. */
171   if (maxTasks > 0 && taskCount == maxTasks) {
172     IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: task limit (%d) reached, not creating new one.\n",maxTasks));
173     return;
174   }
175   
176
177   r = createOSThread(&tid,taskStart);
178   if (r != 0) {
179     barf("startTask: Can't create new task");
180   }
181   taskCount++;
182
183   IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: new task %ld (total_count: %d; waiting: %d)\n", tid, taskCount, rts_n_waiting_tasks););
184   return;
185 }
186
187 void
188 stopTaskManager ()
189 {
190
191 }
192 #endif
193
194
195 #endif /* RTS_SUPPORTS_THREADS */