8e28911dfc4219b0aac034cb5c586908da9daf51
[ghc-hetmet.git] / ghc / rts / Capability.c
1 /* ---------------------------------------------------------------------------
2  * (c) The GHC Team, 2003
3  *
4  * Capabilities
5  *
6  * A Capability represent the token required to execute STG code,
7  * and all the state an OS thread/task needs to run Haskell code:
8  * its STG registers, a pointer to its TSO, a nursery etc. During
9  * STG execution, a pointer to the capabilitity is kept in a
10  * register (BaseReg).
11  *
12  * Only in an SMP build will there be multiple capabilities, for
13  * the threaded RTS and other non-threaded builds, there is only
14  * one global capability, namely MainCapability.
15  * 
16  * --------------------------------------------------------------------------*/
17
18 #include "PosixSource.h"
19 #include "Rts.h"
20 #include "RtsUtils.h"
21 #include "RtsFlags.h"
22 #include "OSThreads.h"
23 #include "Capability.h"
24 #include "Schedule.h"  /* to get at EMPTY_RUN_QUEUE() */
25
26 #if !defined(SMP)
27 Capability MainCapability;     /* for non-SMP, we have one global capability */
28 #endif
29
30 Capability *capabilities = NULL;
31 nat rts_n_free_capabilities;
32
33 #if defined(RTS_SUPPORTS_THREADS)
34
35 /* returning_worker_cond: when a worker thread returns from executing an
36  * external call, it needs to wait for an RTS Capability before passing
37  * on the result of the call to the Haskell thread that made it.
38  * 
39  * returning_worker_cond is signalled in Capability.releaseCapability().
40  *
41  */
42 Condition returning_worker_cond = INIT_COND_VAR;
43
44 /*
45  * To avoid starvation of threads blocked on worker_thread_cond,
46  * the task(s) that enter the Scheduler will check to see whether
47  * there are one or more worker threads blocked waiting on
48  * returning_worker_cond.
49  */
50 nat rts_n_waiting_workers = 0;
51
52 /* thread_ready_cond: when signalled, a thread has become runnable for a
53  * task to execute.
54  *
55  * In the non-SMP case, it also implies that the thread that is woken up has
56  * exclusive access to the RTS and all its data structures (that are not
57  * locked by the Scheduler's mutex).
58  *
59  * thread_ready_cond is signalled whenever
60  *      !noCapabilities && !EMPTY_RUN_QUEUE().
61  */
62 Condition thread_ready_cond = INIT_COND_VAR;
63
64 /*
65  * To be able to make an informed decision about whether or not 
66  * to create a new task when making an external call, keep track of
67  * the number of tasks currently blocked waiting on thread_ready_cond.
68  * (if > 0 => no need for a new task, just unblock an existing one).
69  *
70  * waitForWorkCapability() takes care of keeping it up-to-date;
71  * Task.startTask() uses its current value.
72  */
73 nat rts_n_waiting_tasks = 0;
74
75 static Condition *passTarget = NULL;
76 static rtsBool passingCapability = rtsFalse;
77 #endif
78
79 #if defined(SMP)
80 /*
81  * Free capability list. 
82  */
83 Capability *free_capabilities;
84 #endif
85
86 #ifdef SMP
87 #define UNUSED_IF_NOT_SMP
88 #else
89 #define UNUSED_IF_NOT_SMP STG_UNUSED
90 #endif
91
92 #if defined(RTS_USER_SIGNALS)
93 #define ANY_WORK_TO_DO() (!EMPTY_RUN_QUEUE() || interrupted || blackholes_need_checking || signals_pending())
94 #else
95 #define ANY_WORK_TO_DO() (!EMPTY_RUN_QUEUE() || interrupted || blackholes_need_checking)
96 #endif
97
98 /* ----------------------------------------------------------------------------
99    Initialisation
100    ------------------------------------------------------------------------- */
101
102 static void
103 initCapability( Capability *cap )
104 {
105     cap->f.stgGCEnter1     = (F_)__stg_gc_enter_1;
106     cap->f.stgGCFun        = (F_)__stg_gc_fun;
107 }
108
109 /* ---------------------------------------------------------------------------
110  * Function:  initCapabilities()
111  *
112  * Purpose:   set up the Capability handling. For the SMP build,
113  *            we keep a table of them, the size of which is
114  *            controlled by the user via the RTS flag RtsFlags.ParFlags.nNodes
115  *
116  * ------------------------------------------------------------------------- */
117 void
118 initCapabilities( void )
119 {
120 #if defined(SMP)
121     nat i,n;
122
123     n = RtsFlags.ParFlags.nNodes;
124     capabilities = stgMallocBytes(n * sizeof(Capability), "initCapabilities");
125
126     for (i = 0; i < n; i++) {
127         initCapability(&capabilities[i]);
128         capabilities[i].link = &capabilities[i+1];
129     }
130     capabilities[n-1].link = NULL;
131     
132     free_capabilities = &capabilities[0];
133     rts_n_free_capabilities = n;
134
135     IF_DEBUG(scheduler, sched_belch("allocated %d capabilities", n));
136 #else
137     capabilities = &MainCapability;
138     initCapability(&MainCapability);
139     rts_n_free_capabilities = 1;
140 #endif
141
142 #if defined(RTS_SUPPORTS_THREADS)
143     initCondition(&returning_worker_cond);
144     initCondition(&thread_ready_cond);
145 #endif
146 }
147
148 /* ----------------------------------------------------------------------------
149    grabCapability( Capability** )
150
151    (only externally visible when !RTS_SUPPORTS_THREADS.  In the
152    threaded RTS, clients must use waitFor*Capability()).
153    ------------------------------------------------------------------------- */
154
155 #if defined(RTS_SUPPORTS_THREADS)
156 static
157 #endif
158 void
159 grabCapability( Capability** cap )
160 {
161 #if defined(SMP)
162   ASSERT(rts_n_free_capabilities > 0);
163   *cap = free_capabilities;
164   free_capabilities = (*cap)->link;
165   rts_n_free_capabilities--;
166 #else
167 # if defined(RTS_SUPPORTS_THREADS)
168   ASSERT(rts_n_free_capabilities == 1);
169   rts_n_free_capabilities = 0;
170 # endif
171   *cap = &MainCapability;
172 #endif
173 #if defined(RTS_SUPPORTS_THREADS)
174   IF_DEBUG(scheduler, sched_belch("worker: got capability"));
175 #endif
176 }
177
178 /* ----------------------------------------------------------------------------
179  * Function:  releaseCapability(Capability*)
180  *
181  * Purpose:   Letting go of a capability. Causes a
182  *            'returning worker' thread or a 'waiting worker'
183  *            to wake up, in that order.
184  * ------------------------------------------------------------------------- */
185
186 void
187 releaseCapability( Capability* cap UNUSED_IF_NOT_SMP )
188 {
189     // Precondition: sched_mutex is held.
190 #if defined(RTS_SUPPORTS_THREADS)
191 #if !defined(SMP)
192     ASSERT(rts_n_free_capabilities == 0);
193 #endif
194 #if defined(SMP)
195     cap->link = free_capabilities;
196     free_capabilities = cap;
197 #endif
198     // Check to see whether a worker thread can be given
199     // the go-ahead to return the result of an external call..
200     if (rts_n_waiting_workers > 0) {
201         // Decrement the counter here to avoid livelock where the
202         // thread that is yielding its capability will repeatedly
203         // signal returning_worker_cond.
204
205         rts_n_waiting_workers--;
206         signalCondition(&returning_worker_cond);
207         IF_DEBUG(scheduler, sched_belch("worker: released capability to returning worker"));
208     } else if (passingCapability) {
209         if (passTarget == NULL) {
210             signalCondition(&thread_ready_cond);
211             startSchedulerTaskIfNecessary();
212         } else {
213             signalCondition(passTarget);
214         }
215 #if defined(SMP)
216         rts_n_free_capabilities++;
217 #else
218         rts_n_free_capabilities = 1;
219 #endif
220         IF_DEBUG(scheduler, sched_belch("worker: released capability, passing it"));
221
222     } else {
223 #if defined(SMP)
224         rts_n_free_capabilities++;
225 #else
226         rts_n_free_capabilities = 1;
227 #endif
228         // Signal that a capability is available
229         if (rts_n_waiting_tasks > 0 && ANY_WORK_TO_DO()) {
230             signalCondition(&thread_ready_cond);
231         }
232         startSchedulerTaskIfNecessary();
233         IF_DEBUG(scheduler, sched_belch("worker: released capability"));
234     }
235 #endif
236     return;
237 }
238
239 #if defined(RTS_SUPPORTS_THREADS)
240 /*
241  * When a native thread has completed the execution of an external
242  * call, it needs to communicate the result back. This is done
243  * as follows:
244  *
245  *  - in resumeThread(), the thread calls waitForReturnCapability().
246  *  - If no capabilities are readily available, waitForReturnCapability()
247  *    increments a counter rts_n_waiting_workers, and blocks
248  *    waiting for the condition returning_worker_cond to become
249  *    signalled.
250  *  - upon entry to the Scheduler, a worker thread checks the
251  *    value of rts_n_waiting_workers. If > 0, the worker thread
252  *    will yield its capability to let a returning worker thread
253  *    proceed with returning its result -- this is done via
254  *    yieldToReturningWorker().
255  *  - the worker thread that yielded its capability then tries
256  *    to re-grab a capability and re-enter the Scheduler.
257  */
258
259 /* ----------------------------------------------------------------------------
260  * waitForReturnCapability( Mutext *pMutex, Capability** )
261  *
262  * Purpose:  when an OS thread returns from an external call,
263  * it calls grabReturnCapability() (via Schedule.resumeThread())
264  * to wait for permissions to enter the RTS & communicate the
265  * result of the external call back to the Haskell thread that
266  * made it.
267  *
268  * ------------------------------------------------------------------------- */
269
270 void
271 waitForReturnCapability( Mutex* pMutex, Capability** pCap )
272 {
273     // Pre-condition: pMutex is held.
274
275     IF_DEBUG(scheduler, 
276              sched_belch("worker: returning; workers waiting: %d",
277                          rts_n_waiting_workers));
278
279     if ( noCapabilities() || passingCapability ) {
280         rts_n_waiting_workers++;
281         context_switch = 1;     // make sure it's our turn soon
282         waitCondition(&returning_worker_cond, pMutex);
283 #if defined(SMP)
284         *pCap = free_capabilities;
285         free_capabilities = (*pCap)->link;
286         ASSERT(pCap != NULL);
287 #else
288         *pCap = &MainCapability;
289         ASSERT(rts_n_free_capabilities == 0);
290 #endif
291     } else {
292         grabCapability(pCap);
293     }
294
295     // Post-condition: pMutex is held, pCap points to a capability
296     // which is now held by the current thread.
297     return;
298 }
299
300
301 /* ----------------------------------------------------------------------------
302  * yieldCapability( Mutex* pMutex, Capability** pCap )
303  * ------------------------------------------------------------------------- */
304
305 void
306 yieldCapability( Capability** pCap )
307 {
308     // Pre-condition:  pMutex is assumed held, the current thread
309     // holds the capability pointed to by pCap.
310
311     if ( rts_n_waiting_workers > 0 || passingCapability || !ANY_WORK_TO_DO()) {
312         IF_DEBUG(scheduler, 
313                  if (rts_n_waiting_workers > 0) {
314                      sched_belch("worker: giving up capability (returning wkr)");
315                  } else if (passingCapability) {
316                      sched_belch("worker: giving up capability (passing capability)");
317                  } else {
318                      sched_belch("worker: giving up capability (no threads to run)");
319                  }
320             );
321         releaseCapability(*pCap);
322         *pCap = NULL;
323     }
324
325     // Post-condition:  either:
326     //
327     //  1. *pCap is NULL, in which case the current thread does not
328     //     hold a capability now, or
329     //  2. *pCap is not NULL, in which case the current thread still
330     //     holds the capability.
331     //
332     return;
333 }
334
335
336 /* ----------------------------------------------------------------------------
337  * waitForCapability( Mutex*, Capability**, Condition* )
338  *
339  * Purpose:  wait for a Capability to become available. In
340  *           the process of doing so, updates the number
341  *           of tasks currently blocked waiting for a capability/more
342  *           work. That counter is used when deciding whether or
343  *           not to create a new worker thread when an external
344  *           call is made.
345  *           If pThreadCond is not NULL, a capability can be specifically
346  *           passed to this thread using passCapability.
347  * ------------------------------------------------------------------------- */
348  
349 void
350 waitForCapability( Mutex* pMutex, Capability** pCap, Condition* pThreadCond )
351 {
352     // Pre-condition: pMutex is held.
353
354     while ( noCapabilities() ||
355             (passingCapability && passTarget != pThreadCond) ||
356             !ANY_WORK_TO_DO()) {
357         IF_DEBUG(scheduler,
358                  sched_belch("worker: wait for capability (cond: %p)",
359                              pThreadCond));
360
361         if (pThreadCond != NULL) {
362             waitCondition(pThreadCond, pMutex);
363             IF_DEBUG(scheduler, sched_belch("worker: get passed capability"));
364         } else {
365             rts_n_waiting_tasks++;
366             waitCondition(&thread_ready_cond, pMutex);
367             rts_n_waiting_tasks--;
368             IF_DEBUG(scheduler, sched_belch("worker: get normal capability"));
369         }
370     }
371     passingCapability = rtsFalse;
372     grabCapability(pCap);
373
374     // Post-condition: pMutex is held and *pCap is held by the current thread
375     return;
376 }
377
378 /* ----------------------------------------------------------------------------
379    passCapability, passCapabilityToWorker
380    ------------------------------------------------------------------------- */
381
382 void
383 passCapability( Condition *pTargetThreadCond )
384 {
385     // Pre-condition: pMutex is held and cap is held by the current thread
386
387     passTarget = pTargetThreadCond;
388     passingCapability = rtsTrue;
389     IF_DEBUG(scheduler, sched_belch("worker: passCapability"));
390
391     // Post-condition: pMutex is held; cap is still held, but will be
392     //                 passed to the target thread when next released.
393 }
394
395 void
396 passCapabilityToWorker( void )
397 {
398     // Pre-condition: pMutex is held and cap is held by the current thread
399
400     passTarget = NULL;
401     passingCapability = rtsTrue;
402     IF_DEBUG(scheduler, sched_belch("worker: passCapabilityToWorker"));
403
404     // Post-condition: pMutex is held; cap is still held, but will be
405     //                 passed to a worker thread when next released.
406 }
407
408 #endif /* RTS_SUPPORTS_THREADS */
409
410 /* ----------------------------------------------------------------------------
411    threadRunnable()
412
413    Signals that a thread has been placed on the run queue, so a worker
414    might need to be woken up to run it.
415
416    ToDo: should check whether the thread at the front of the queue is
417    bound, and if so wake up the appropriate worker.
418    -------------------------------------------------------------------------- */
419 void
420 threadRunnable ( void )
421 {
422 #if defined(RTS_SUPPORTS_THREADS)
423     if ( !noCapabilities() && ANY_WORK_TO_DO() && rts_n_waiting_tasks > 0 ) {
424         signalCondition(&thread_ready_cond);
425     }
426     startSchedulerTaskIfNecessary();
427 #endif
428 }
429
430
431 /* ----------------------------------------------------------------------------
432    prodWorker()
433
434    Wake up... time to die.
435    -------------------------------------------------------------------------- */
436 void
437 prodWorker ( void )
438 {
439 #if defined(RTS_SUPPORTS_THREADS)
440     signalCondition(&thread_ready_cond);
441 #endif
442 }