#include "OSThreads.h"
#include "Capability.h"
#include "Schedule.h" /* to get at EMPTY_RUN_QUEUE() */
-#include "Signals.h" /* to get at handleSignalsInThisThread() */
#if !defined(SMP)
Capability MainCapability; /* for non-SMP, we have one global capability */
#endif
-#if defined(RTS_SUPPORTS_THREADS)
-
+Capability *capabilities = NULL;
nat rts_n_free_capabilities;
+#if defined(RTS_SUPPORTS_THREADS)
+
/* returning_worker_cond: when a worker thread returns from executing an
* external call, it needs to wait for an RTS Capability before passing
* on the result of the call to the Haskell thread that made it.
* exclusive access to the RTS and all its data structures (that are not
* locked by the Scheduler's mutex).
*
- * thread_ready_cond is signalled whenever noCapabilities doesn't hold.
- *
+ * thread_ready_cond is signalled whenever
+ * !noCapabilities && !EMPTY_RUN_QUEUE().
*/
Condition thread_ready_cond = INIT_COND_VAR;
static rtsBool passingCapability = rtsFalse;
#endif
+#if defined(SMP)
+/*
+ * Free capability list.
+ */
+Capability *free_capabilities;
+#endif
+
#ifdef SMP
#define UNUSED_IF_NOT_SMP
#else
#define UNUSED_IF_NOT_SMP STG_UNUSED
#endif
+#if defined(RTS_USER_SIGNALS)
+#define ANY_WORK_TO_DO() (!EMPTY_RUN_QUEUE() || interrupted || blackholes_need_checking || signals_pending())
+#else
+#define ANY_WORK_TO_DO() (!EMPTY_RUN_QUEUE() || interrupted || blackholes_need_checking)
+#endif
+
/* ----------------------------------------------------------------------------
Initialisation
------------------------------------------------------------------------- */
static void
initCapability( Capability *cap )
{
+ cap->r.rInHaskell = rtsFalse;
cap->f.stgGCEnter1 = (F_)__stg_gc_enter_1;
cap->f.stgGCFun = (F_)__stg_gc_fun;
}
-#if defined(SMP)
-static void initCapabilities_(nat n);
-#endif
-
/* ---------------------------------------------------------------------------
* Function: initCapabilities()
*
initCapabilities( void )
{
#if defined(SMP)
- initCapabilities_(RtsFlags.ParFlags.nNodes);
+ nat i,n;
+
+ n = RtsFlags.ParFlags.nNodes;
+ capabilities = stgMallocBytes(n * sizeof(Capability), "initCapabilities");
+
+ for (i = 0; i < n; i++) {
+ initCapability(&capabilities[i]);
+ capabilities[i].link = &capabilities[i+1];
+ }
+ capabilities[n-1].link = NULL;
+
+ free_capabilities = &capabilities[0];
+ rts_n_free_capabilities = n;
+
+ IF_DEBUG(scheduler, sched_belch("allocated %d capabilities", n));
#else
- initCapability(&MainCapability);
+ capabilities = &MainCapability;
+ initCapability(&MainCapability);
+ rts_n_free_capabilities = 1;
#endif
#if defined(RTS_SUPPORTS_THREADS)
- initCondition(&returning_worker_cond);
- initCondition(&thread_ready_cond);
- rts_n_free_capabilities = 1;
+ initCondition(&returning_worker_cond);
+ initCondition(&thread_ready_cond);
#endif
-
- return;
}
-#if defined(SMP)
-/* Free capability list. */
-static Capability *free_capabilities; /* Available capabilities for running threads */
-static Capability *returning_capabilities;
- /* Capabilities being passed to returning worker threads */
-#endif
-
/* ----------------------------------------------------------------------------
grabCapability( Capability** )
void
grabCapability( Capability** cap )
{
-#if !defined(SMP)
-#if defined(RTS_SUPPORTS_THREADS)
- ASSERT(rts_n_free_capabilities == 1);
- rts_n_free_capabilities = 0;
-#endif
- *cap = &MainCapability;
- handleSignalsInThisThread();
-#else
+#if defined(SMP)
+ ASSERT(rts_n_free_capabilities > 0);
*cap = free_capabilities;
free_capabilities = (*cap)->link;
rts_n_free_capabilities--;
+#else
+# if defined(RTS_SUPPORTS_THREADS)
+ ASSERT(rts_n_free_capabilities == 1);
+ rts_n_free_capabilities = 0;
+# endif
+ *cap = &MainCapability;
#endif
#if defined(RTS_SUPPORTS_THREADS)
IF_DEBUG(scheduler, sched_belch("worker: got capability"));
{
// Precondition: sched_mutex is held.
#if defined(RTS_SUPPORTS_THREADS)
-#ifndef SMP
+#if !defined(SMP)
ASSERT(rts_n_free_capabilities == 0);
#endif
+#if defined(SMP)
+ cap->link = free_capabilities;
+ free_capabilities = cap;
+#endif
// Check to see whether a worker thread can be given
// the go-ahead to return the result of an external call..
if (rts_n_waiting_workers > 0) {
// thread that is yielding its capability will repeatedly
// signal returning_worker_cond.
-#if defined(SMP)
- // SMP variant untested
- cap->link = returning_capabilities;
- returning_capabilities = cap;
-#endif
-
rts_n_waiting_workers--;
signalCondition(&returning_worker_cond);
IF_DEBUG(scheduler, sched_belch("worker: released capability to returning worker"));
} else {
signalCondition(passTarget);
}
- rts_n_free_capabilities = 1;
+ rts_n_free_capabilities++;
IF_DEBUG(scheduler, sched_belch("worker: released capability, passing it"));
} else {
-#if defined(SMP)
- cap->link = free_capabilities;
- free_capabilities = cap;
rts_n_free_capabilities++;
-#else
- rts_n_free_capabilities = 1;
-#endif
// Signal that a capability is available
- if (rts_n_waiting_tasks > 0) {
+ if (rts_n_waiting_tasks > 0 && ANY_WORK_TO_DO()) {
signalCondition(&thread_ready_cond);
}
startSchedulerTaskIfNecessary();
if ( noCapabilities() || passingCapability ) {
rts_n_waiting_workers++;
- wakeBlockedWorkerThread();
context_switch = 1; // make sure it's our turn soon
waitCondition(&returning_worker_cond, pMutex);
#if defined(SMP)
- *pCap = returning_capabilities;
- returning_capabilities = (*pCap)->link;
+ *pCap = free_capabilities;
+ free_capabilities = (*pCap)->link;
+ ASSERT(pCap != NULL);
#else
*pCap = &MainCapability;
ASSERT(rts_n_free_capabilities == 0);
- handleSignalsInThisThread();
#endif
} else {
grabCapability(pCap);
// Pre-condition: pMutex is assumed held, the current thread
// holds the capability pointed to by pCap.
- if ( rts_n_waiting_workers > 0 || passingCapability ) {
- IF_DEBUG(scheduler, sched_belch("worker: giving up capability"));
+ if ( rts_n_waiting_workers > 0 || passingCapability || !ANY_WORK_TO_DO()) {
+ IF_DEBUG(scheduler,
+ if (rts_n_waiting_workers > 0) {
+ sched_belch("worker: giving up capability (returning wkr)");
+ } else if (passingCapability) {
+ sched_belch("worker: giving up capability (passing capability)");
+ } else {
+ sched_belch("worker: giving up capability (no threads to run)");
+ }
+ );
releaseCapability(*pCap);
*pCap = NULL;
}
- // Post-condition: pMutex is assumed held, and either:
+ // Post-condition: either:
//
// 1. *pCap is NULL, in which case the current thread does not
// hold a capability now, or
* passed to this thread using passCapability.
* ------------------------------------------------------------------------- */
-void
+void
waitForCapability( Mutex* pMutex, Capability** pCap, Condition* pThreadCond )
{
// Pre-condition: pMutex is held.
- while ( noCapabilities() ||
- (passingCapability && passTarget != pThreadCond)) {
+ while ( noCapabilities() ||
+ (passingCapability && passTarget != pThreadCond) ||
+ !ANY_WORK_TO_DO()) {
IF_DEBUG(scheduler,
sched_belch("worker: wait for capability (cond: %p)",
pThreadCond));
#endif /* RTS_SUPPORTS_THREADS */
-/* ------------------------------------------------------------------------- */
+/* ----------------------------------------------------------------------------
+ threadRunnable()
-#if defined(SMP)
-/*
- * Function: initCapabilities_(nat)
- *
- * Purpose: upon startup, allocate and fill in table
- * holding 'n' Capabilities. Only for SMP, since
- * it is the only build that supports multiple
- * capabilities within the RTS.
- */
-static void
-initCapabilities_(nat n)
+ Signals that a thread has been placed on the run queue, so a worker
+ might need to be woken up to run it.
+
+ ToDo: should check whether the thread at the front of the queue is
+ bound, and if so wake up the appropriate worker.
+ -------------------------------------------------------------------------- */
+void
+threadRunnable ( void )
{
- nat i;
- Capability *cap, *prev;
- cap = NULL;
- prev = NULL;
- for (i = 0; i < n; i++) {
- cap = stgMallocBytes(sizeof(Capability), "initCapabilities");
- initCapability(cap);
- cap->link = prev;
- prev = cap;
- }
- free_capabilities = cap;
- rts_n_free_capabilities = n;
- returning_capabilities = NULL;
- IF_DEBUG(scheduler,
- sched_belch("allocated %d capabilities", n_free_capabilities));
+#if defined(RTS_SUPPORTS_THREADS)
+ if ( !noCapabilities() && ANY_WORK_TO_DO() && rts_n_waiting_tasks > 0 ) {
+ signalCondition(&thread_ready_cond);
+ }
+ startSchedulerTaskIfNecessary();
+#endif
}
-#endif /* SMP */
+
+/* ----------------------------------------------------------------------------
+ prodWorker()
+
+ Wake up... time to die.
+ -------------------------------------------------------------------------- */
+void
+prodWorker ( void )
+{
+#if defined(RTS_SUPPORTS_THREADS)
+ signalCondition(&thread_ready_cond);
+#endif
+}