- *
- */
-void releaseCapability(Capability* cap
-#if !defined(SMP)
- STG_UNUSED
-#endif
-)
-{ // Precondition: sched_mutex must be held
-#if defined(RTS_SUPPORTS_THREADS)
-#ifndef SMP
- ASSERT(rts_n_free_capabilities == 0);
-#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) {
- /* Decrement the counter here to avoid livelock where the
- * 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;
-#else
-#endif
- rts_n_waiting_workers--;
- signalCondition(&returning_worker_cond);
- } else /*if ( !EMPTY_RUN_QUEUE() )*/ {
-#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 */
- signalCondition(&thread_ready_cond);
- startSchedulerTaskIfNecessary(); // if there is more work to be done,
- // we'll need a new thread
- }
-#endif
-#ifdef RTS_SUPPORTS_THREADS
- IF_DEBUG(scheduler,
- fprintf(stderr,"worker thread (%p): released capability\n",
- osThreadId()));
-#endif
- return;
+ * ------------------------------------------------------------------------- */
+
+#if defined(THREADED_RTS)
+void
+releaseCapability_ (Capability* cap)
+{
+ Task *task;
+
+ task = cap->running_task;
+
+ ASSERT_PARTIAL_CAPABILITY_INVARIANTS(cap,task);
+
+ cap->running_task = NULL;
+
+ // Check to see whether a worker thread can be given
+ // the go-ahead to return the result of an external call..
+ if (cap->returning_tasks_hd != NULL) {
+ giveCapabilityToTask(cap,cap->returning_tasks_hd);
+ // The Task pops itself from the queue (see waitForReturnCapability())
+ return;
+ }
+
+ // If the next thread on the run queue is a bound thread,
+ // give this Capability to the appropriate Task.
+ if (!emptyRunQueue(cap) && cap->run_queue_hd->bound) {
+ // Make sure we're not about to try to wake ourselves up
+ ASSERT(task != cap->run_queue_hd->bound);
+ task = cap->run_queue_hd->bound;
+ giveCapabilityToTask(cap,task);
+ return;
+ }
+
+ if (!cap->spare_workers) {
+ // Create a worker thread if we don't have one. If the system
+ // is interrupted, we only create a worker task if there
+ // are threads that need to be completed. If the system is
+ // shutting down, we never create a new worker.
+ if (sched_state < SCHED_SHUTTING_DOWN || !emptyRunQueue(cap)) {
+ IF_DEBUG(scheduler,
+ sched_belch("starting new worker on capability %d", cap->no));
+ startWorkerTask(cap, workerStart);
+ return;
+ }
+ }
+
+ // If we have an unbound thread on the run queue, or if there's
+ // anything else to do, give the Capability to a worker thread.
+ if (!emptyRunQueue(cap) || !emptyWakeupQueue(cap)
+ || !emptySparkPoolCap(cap) || globalWorkToDo()) {
+ if (cap->spare_workers) {
+ giveCapabilityToTask(cap,cap->spare_workers);
+ // The worker Task pops itself from the queue;
+ return;
+ }
+ }
+
+ last_free_capability = cap;
+ IF_DEBUG(scheduler, sched_belch("freeing capability %d", cap->no));