[project @ 2005-04-12 12:24:44 by simonmar]
[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         rts_n_free_capabilities++;
216         IF_DEBUG(scheduler, sched_belch("worker: released capability, passing it"));
217
218     } else {
219         rts_n_free_capabilities++;
220         // Signal that a capability is available
221         if (rts_n_waiting_tasks > 0 && ANY_WORK_TO_DO()) {
222             signalCondition(&thread_ready_cond);
223         }
224         startSchedulerTaskIfNecessary();
225         IF_DEBUG(scheduler, sched_belch("worker: released capability"));
226     }
227 #endif
228     return;
229 }
230
231 #if defined(RTS_SUPPORTS_THREADS)
232 /*
233  * When a native thread has completed the execution of an external
234  * call, it needs to communicate the result back. This is done
235  * as follows:
236  *
237  *  - in resumeThread(), the thread calls waitForReturnCapability().
238  *  - If no capabilities are readily available, waitForReturnCapability()
239  *    increments a counter rts_n_waiting_workers, and blocks
240  *    waiting for the condition returning_worker_cond to become
241  *    signalled.
242  *  - upon entry to the Scheduler, a worker thread checks the
243  *    value of rts_n_waiting_workers. If > 0, the worker thread
244  *    will yield its capability to let a returning worker thread
245  *    proceed with returning its result -- this is done via
246  *    yieldToReturningWorker().
247  *  - the worker thread that yielded its capability then tries
248  *    to re-grab a capability and re-enter the Scheduler.
249  */
250
251 /* ----------------------------------------------------------------------------
252  * waitForReturnCapability( Mutext *pMutex, Capability** )
253  *
254  * Purpose:  when an OS thread returns from an external call,
255  * it calls grabReturnCapability() (via Schedule.resumeThread())
256  * to wait for permissions to enter the RTS & communicate the
257  * result of the external call back to the Haskell thread that
258  * made it.
259  *
260  * ------------------------------------------------------------------------- */
261
262 void
263 waitForReturnCapability( Mutex* pMutex, Capability** pCap )
264 {
265     // Pre-condition: pMutex is held.
266
267     IF_DEBUG(scheduler, 
268              sched_belch("worker: returning; workers waiting: %d",
269                          rts_n_waiting_workers));
270
271     if ( noCapabilities() || passingCapability ) {
272         rts_n_waiting_workers++;
273         context_switch = 1;     // make sure it's our turn soon
274         waitCondition(&returning_worker_cond, pMutex);
275 #if defined(SMP)
276         *pCap = free_capabilities;
277         free_capabilities = (*pCap)->link;
278         ASSERT(pCap != NULL);
279 #else
280         *pCap = &MainCapability;
281         ASSERT(rts_n_free_capabilities == 0);
282 #endif
283     } else {
284         grabCapability(pCap);
285     }
286
287     // Post-condition: pMutex is held, pCap points to a capability
288     // which is now held by the current thread.
289     return;
290 }
291
292
293 /* ----------------------------------------------------------------------------
294  * yieldCapability( Mutex* pMutex, Capability** pCap )
295  * ------------------------------------------------------------------------- */
296
297 void
298 yieldCapability( Capability** pCap )
299 {
300     // Pre-condition:  pMutex is assumed held, the current thread
301     // holds the capability pointed to by pCap.
302
303     if ( rts_n_waiting_workers > 0 || passingCapability || !ANY_WORK_TO_DO()) {
304         IF_DEBUG(scheduler, 
305                  if (rts_n_waiting_workers > 0) {
306                      sched_belch("worker: giving up capability (returning wkr)");
307                  } else if (passingCapability) {
308                      sched_belch("worker: giving up capability (passing capability)");
309                  } else {
310                      sched_belch("worker: giving up capability (no threads to run)");
311                  }
312             );
313         releaseCapability(*pCap);
314         *pCap = NULL;
315     }
316
317     // Post-condition:  either:
318     //
319     //  1. *pCap is NULL, in which case the current thread does not
320     //     hold a capability now, or
321     //  2. *pCap is not NULL, in which case the current thread still
322     //     holds the capability.
323     //
324     return;
325 }
326
327
328 /* ----------------------------------------------------------------------------
329  * waitForCapability( Mutex*, Capability**, Condition* )
330  *
331  * Purpose:  wait for a Capability to become available. In
332  *           the process of doing so, updates the number
333  *           of tasks currently blocked waiting for a capability/more
334  *           work. That counter is used when deciding whether or
335  *           not to create a new worker thread when an external
336  *           call is made.
337  *           If pThreadCond is not NULL, a capability can be specifically
338  *           passed to this thread using passCapability.
339  * ------------------------------------------------------------------------- */
340  
341 void
342 waitForCapability( Mutex* pMutex, Capability** pCap, Condition* pThreadCond )
343 {
344     // Pre-condition: pMutex is held.
345
346     while ( noCapabilities() ||
347             (passingCapability && passTarget != pThreadCond) ||
348             !ANY_WORK_TO_DO()) {
349         IF_DEBUG(scheduler,
350                  sched_belch("worker: wait for capability (cond: %p)",
351                              pThreadCond));
352
353         if (pThreadCond != NULL) {
354             waitCondition(pThreadCond, pMutex);
355             IF_DEBUG(scheduler, sched_belch("worker: get passed capability"));
356         } else {
357             rts_n_waiting_tasks++;
358             waitCondition(&thread_ready_cond, pMutex);
359             rts_n_waiting_tasks--;
360             IF_DEBUG(scheduler, sched_belch("worker: get normal capability"));
361         }
362     }
363     passingCapability = rtsFalse;
364     grabCapability(pCap);
365
366     // Post-condition: pMutex is held and *pCap is held by the current thread
367     return;
368 }
369
370 /* ----------------------------------------------------------------------------
371    passCapability, passCapabilityToWorker
372    ------------------------------------------------------------------------- */
373
374 void
375 passCapability( Condition *pTargetThreadCond )
376 {
377     // Pre-condition: pMutex is held and cap is held by the current thread
378
379     passTarget = pTargetThreadCond;
380     passingCapability = rtsTrue;
381     IF_DEBUG(scheduler, sched_belch("worker: passCapability"));
382
383     // Post-condition: pMutex is held; cap is still held, but will be
384     //                 passed to the target thread when next released.
385 }
386
387 void
388 passCapabilityToWorker( void )
389 {
390     // Pre-condition: pMutex is held and cap is held by the current thread
391
392     passTarget = NULL;
393     passingCapability = rtsTrue;
394     IF_DEBUG(scheduler, sched_belch("worker: passCapabilityToWorker"));
395
396     // Post-condition: pMutex is held; cap is still held, but will be
397     //                 passed to a worker thread when next released.
398 }
399
400 #endif /* RTS_SUPPORTS_THREADS */
401
402 /* ----------------------------------------------------------------------------
403    threadRunnable()
404
405    Signals that a thread has been placed on the run queue, so a worker
406    might need to be woken up to run it.
407
408    ToDo: should check whether the thread at the front of the queue is
409    bound, and if so wake up the appropriate worker.
410    -------------------------------------------------------------------------- */
411 void
412 threadRunnable ( void )
413 {
414 #if defined(RTS_SUPPORTS_THREADS)
415     if ( !noCapabilities() && ANY_WORK_TO_DO() && rts_n_waiting_tasks > 0 ) {
416         signalCondition(&thread_ready_cond);
417     }
418     startSchedulerTaskIfNecessary();
419 #endif
420 }
421
422
423 /* ----------------------------------------------------------------------------
424    prodWorker()
425
426    Wake up... time to die.
427    -------------------------------------------------------------------------- */
428 void
429 prodWorker ( void )
430 {
431 #if defined(RTS_SUPPORTS_THREADS)
432     signalCondition(&thread_ready_cond);
433 #endif
434 }