[project @ 2002-02-14 07:39:16 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,
139                   void (*taskStart)(void) STG_UNUSED )
140 {
141   /* In the threaded case, maxCount is used to limit the
142      the creation of worker tasks. Tasks are created lazily, i.e.,
143      when the current task gives up the token on executing
144      STG code.
145   */
146   maxTasks = maxCount;
147   taskCount = 0;
148 }
149
150 void
151 startTask ( void (*taskStart)(void) )
152 {
153   int r;
154   OSThreadId tid;
155   
156   /* If more than one worker thread is known to be blocked waiting
157      on thread_ready_cond, signal it rather than creating a new one.
158   */
159   if ( rts_n_waiting_tasks > 0) {
160     IF_DEBUG(scheduler,fprintf(stderr,
161                                "scheduler: startTask: %d tasks waiting, not creating new one.\n", 
162                                rts_n_waiting_tasks););
163     signalCondition(&thread_ready_cond);
164     /* Not needed, but gives more 'interesting' thread schedules when testing */
165     yieldThread();
166     return;
167   }
168
169   /* If the task limit has been reached, just return. */
170   if (maxTasks > 0 && taskCount == maxTasks) {
171     IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: task limit (%d) reached, not creating new one.\n",maxTasks));
172     return;
173   }
174   
175
176   r = createOSThread(&tid,taskStart);
177   if (r != 0) {
178     barf("startTask: Can't create new task");
179   }
180   taskCount++;
181
182   IF_DEBUG(scheduler,fprintf(stderr,"scheduler: startTask: new task %ld (total_count: %d; waiting: %d)\n", tid, taskCount, rts_n_waiting_tasks););
183   return;
184 }
185
186 void
187 stopTaskManager ()
188 {
189
190 }
191 #endif
192
193
194 #endif /* RTS_SUPPORTS_THREADS */