1 /* ---------------------------------------------------------------------------
3 * (c) The GHC Team, 2002
7 * A Capability represent the token required to execute STG code,
8 * and all the state an OS thread/task needs to run Haskell code:
9 * its STG registers, a pointer to its TSO, a nursery etc. During
10 * STG execution, a pointer to the capabilitity is kept in a
13 * Only in an SMP build will there be multiple capabilities, for
14 * the threaded RTS and other non-threaded builds, there is only
15 * one global capability, namely MainCapability.
17 * --------------------------------------------------------------------------*/
18 #include "PosixSource.h"
22 #include "Capability.h"
25 Capability MainCapability; /* for non-SMP, we have one global capability */
28 nat rts_n_free_capabilities;
30 #if defined(RTS_SUPPORTS_THREADS)
31 /* returning_worker_cond: when a worker thread returns from executing an
32 * external call, it needs to wait for an RTS Capability before passing
33 * on the result of the call to the Haskell thread that made it.
35 * returning_worker_cond is signalled in Capability.releaseCapability().
38 Condition returning_worker_cond = INIT_COND_VAR;
41 * To avoid starvation of threads blocked on worker_thread_cond,
42 * the task(s) that enter the Scheduler will check to see whether
43 * there are one or more worker threads blocked waiting on
44 * returning_worker_cond.
46 * Locks needed: sched_mutex
48 nat rts_n_waiting_workers = 0;
53 initCapability( Capability *cap )
55 cap->f.stgChk0 = (F_)__stg_chk_0;
56 cap->f.stgChk1 = (F_)__stg_chk_1;
57 cap->f.stgGCEnter1 = (F_)__stg_gc_enter_1;
58 cap->f.stgUpdatePAP = (F_)__stg_update_PAP;
62 static void initCapabilities_(nat n);
66 * Function: initCapabilities()
68 * Purpose: set up the Capability handling. For the SMP build,
69 * we keep a table of them, the size of which is
70 * controlled by the user via the RTS flag RtsFlags.ParFlags.nNodes
72 * Pre-conditions: no locks assumed held.
77 #if defined(RTS_SUPPORTS_THREADS)
78 initCondition(&returning_worker_cond);
82 initCapabilities_(RtsFlags.ParFlags.nNodes);
84 initCapability(&MainCapability);
85 rts_n_free_capabilities = 1;
91 /* Free capability list.
92 * Locks required: sched_mutex.
95 static Capability *free_capabilities; /* Available capabilities for running threads */
99 * Function: grabCapability(Capability**)
101 * Purpose: the act of grabbing a capability is easy; just
102 * remove one from the free capabilities list (which
103 * may just have one entry). In threaded builds, worker
104 * threads are prevented from doing so willy-nilly
105 * through the use of the sched_mutex lock along with
106 * condition variables thread_ready_cond and
107 * returning_worker_cond.
109 * Pre-condition: sched_mutex is held (in threaded builds only).
112 void grabCapability(Capability** cap)
115 rts_n_free_capabilities = 0;
116 *cap = &MainCapability;
118 *cap = free_capabilities;
119 free_capabilities = (*cap)->link;
120 rts_n_free_capabilities--;
125 * Function: releaseCapability(Capability*)
127 * Purpose: Letting go of a capability.
129 * Pre-condition: sched_mutex is assumed held by current thread.
132 void releaseCapability(Capability* cap
139 cap->link = free_capabilities;
140 free_capabilities = cap;
141 rts_n_free_capabilities++;
143 rts_n_free_capabilities = 1;
146 #if defined(RTS_SUPPORTS_THREADS)
147 /* Check to see whether a worker thread can be given
148 the go-ahead to return the result of an external call..*/
149 if (rts_n_waiting_workers > 0) {
150 /* Decrement the counter here to avoid livelock where the
151 * thread that is yielding its capability will repeatedly
152 * signal returning_worker_cond.
154 rts_n_waiting_workers--;
155 signalCondition(&returning_worker_cond);
156 } else if ( !EMPTY_RUN_QUEUE() ) {
157 /* Signal that work is available */
158 signalCondition(&thread_ready_cond);
164 #if defined(RTS_SUPPORTS_THREADS)
166 * When a native thread has completed the execution of an external
167 * call, it needs to communicate the result back. This is done
170 * - in resumeThread(), the thread calls grabReturnCapability().
171 * - If no capabilities are readily available, grabReturnCapability()
172 * increments a counter rts_n_waiting_workers, and blocks
173 * waiting for the condition returning_worker_cond to become
175 * - upon entry to the Scheduler, a worker thread checks the
176 * value of rts_n_waiting_workers. If > 0, the worker thread
177 * will yield its capability to let a returning worker thread
178 * proceed with returning its result -- this is done via
180 * - the worker thread that yielded its capability then tries
181 * to re-grab a capability and re-enter the Scheduler.
185 * Function: grabReturnCapability(Capability**)
187 * Purpose: when an OS thread returns from an external call,
188 * it calls grabReturningCapability() (via Schedule.resumeThread())
189 * to wait for permissions to enter the RTS & communicate the
190 * result of the ext. call back to the Haskell thread that
193 * Pre-condition: sched_mutex isn't held.
194 * Post-condition: sched_mutex is held and a capability has
195 * been assigned to the worker thread.
198 grabReturnCapability(Capability** pCap)
201 fprintf(stderr,"worker (%ld): returning, waiting for sched. lock.\n", osThreadId()));
202 ACQUIRE_LOCK(&sched_mutex);
203 rts_n_waiting_workers++;
205 fprintf(stderr,"worker (%ld): returning; workers waiting: %d\n",
206 osThreadId(), rts_n_waiting_workers));
207 while ( noCapabilities() ) {
208 waitCondition(&returning_worker_cond, &sched_mutex);
211 grabCapability(pCap);
216 * Function: yieldCapability(Capability**)
218 * Purpose: when, upon entry to the Scheduler, an OS worker thread
219 * spots that one or more threads are blocked waiting for
220 * permission to return back their result, it gives up
223 * Pre-condition: sched_mutex is held and the thread possesses
225 * Post-condition: sched_mutex isn't held and the Capability has
229 yieldCapability(Capability* cap)
232 fprintf(stderr,"worker thread (%ld): giving up RTS token\n", osThreadId()));
233 releaseCapability(cap);
234 RELEASE_LOCK(&sched_mutex);
236 /* At this point, sched_mutex has been given up & we've
237 * forced a thread context switch. Guaranteed to be
238 * enough for the signalled worker thread to race
244 #endif /* RTS_SUPPORTS_THREADS */
248 * Function: initCapabilities_(nat)
250 * Purpose: upon startup, allocate and fill in table
251 * holding 'n' Capabilities. Only for SMP, since
252 * it is the only build that supports multiple
253 * capabilities within the RTS.
255 * Pre-condition: sched_mutex is held.
259 initCapabilities_(nat n)
262 Capability *cap, *prev;
265 for (i = 0; i < n; i++) {
266 cap = stgMallocBytes(sizeof(Capability), "initCapabilities");
271 free_capabilities = cap;
272 rts_n_free_capabilities = n;
273 IF_DEBUG(scheduler,fprintf(stderr,"scheduler: Allocated %d capabilities\n", n_free_capabilities););