| ... |
... |
@@ -560,7 +560,7 @@ giveCapabilityToTask (Capability *cap USED_IF_DEBUG, Task *task) |
|
560
|
560
|
#endif
|
|
561
|
561
|
|
|
562
|
562
|
/* ----------------------------------------------------------------------------
|
|
563
|
|
- * releaseCapability_
|
|
|
563
|
+ * releaseCapability and releaseCapability_
|
|
564
|
564
|
*
|
|
565
|
565
|
* This serves two purposes:
|
|
566
|
566
|
*
|
| ... |
... |
@@ -570,32 +570,37 @@ giveCapabilityToTask (Capability *cap USED_IF_DEBUG, Task *task) |
|
570
|
570
|
*
|
|
571
|
571
|
* 2. There is no current task (cap->task == NULL), and thus the Capability
|
|
572
|
572
|
* is idle, and we want to wake up an idle Task to animate the Capability.
|
|
573
|
|
- * In this case set always_wakeup. See also prodCapability.
|
|
|
573
|
+ * See also prodCapability.
|
|
574
|
574
|
*
|
|
575
|
|
- * Setting the always_wakeup parameter (almost) ensures that the capability is
|
|
576
|
|
- * not left idle: even if there is no known work to do, the capability will be
|
|
577
|
|
- * given to a worker task. There are two exceptions to this:
|
|
578
|
|
- * 1. if there is a pending sync then the capability is left idle, but in
|
|
579
|
|
- * anticipation of whichever task initiated the sync picking it up shortly.
|
|
580
|
|
- * 2. if the scheduler is shutting down and there are no threads on the run
|
|
581
|
|
- * queue and there are no spare workers then the capability is left idle.
|
|
582
|
|
- * It is not entirely clear if this corner case is intentional.
|
|
583
|
|
- *
|
|
584
|
|
- * The caller must hold cap->lock and will still hold it after the call returns.
|
|
|
575
|
+ * Difference:
|
|
|
576
|
+ * - releaseCapability the caller /must not/ hold cap->lock.
|
|
|
577
|
+ * - releaseCapability_ the caller /must/ hold cap->lock.
|
|
585
|
578
|
*
|
|
586
|
579
|
* N.B. May need to take all_tasks_mutex, if it needs to start a new task.
|
|
587
|
580
|
*
|
|
588
|
581
|
* ------------------------------------------------------------------------- */
|
|
589
|
582
|
|
|
590
|
583
|
#if defined(THREADED_RTS)
|
|
591
|
|
-void
|
|
592
|
|
-releaseCapability_ (Capability* cap,
|
|
593
|
|
- bool always_wakeup)
|
|
|
584
|
+static void releaseCapability__ (Capability* cap, bool wakeup_worker);
|
|
|
585
|
+
|
|
|
586
|
+void releaseCapability (Capability* cap)
|
|
|
587
|
+{
|
|
|
588
|
+ ACQUIRE_LOCK(&cap->lock);
|
|
|
589
|
+ releaseCapability__(cap, false /*wakeup_worker*/);
|
|
|
590
|
+ RELEASE_LOCK(&cap->lock);
|
|
|
591
|
+}
|
|
|
592
|
+
|
|
|
593
|
+void releaseCapability_ (Capability* cap)
|
|
|
594
|
+{
|
|
|
595
|
+ releaseCapability__(cap, false /*wakeup_worker*/);
|
|
|
596
|
+}
|
|
|
597
|
+
|
|
|
598
|
+static void releaseCapability__ (Capability* cap,
|
|
|
599
|
+ bool wakeup_worker)
|
|
594
|
600
|
{
|
|
595
|
601
|
{
|
|
596
|
602
|
Task *task = cap->running_task;
|
|
597
|
603
|
|
|
598
|
|
- ASSERT(task || always_wakeup);
|
|
599
|
604
|
// To cover purpose 2 above, we allow the cap->running_task to be
|
|
600
|
605
|
// NULL, to handle cases where a thread (that is not itself a Task)
|
|
601
|
606
|
// needs to wake up an idle task for the capability.
|
| ... |
... |
@@ -609,6 +614,33 @@ releaseCapability_ (Capability* cap, |
|
609
|
614
|
// Remove the current Task owning the Capability (if any, see purpose 2).
|
|
610
|
615
|
RELAXED_STORE(&cap->running_task, NULL);
|
|
611
|
616
|
|
|
|
617
|
+ // We now look for a task to give the capability to, or otherwise we leave
|
|
|
618
|
+ // the capability free.
|
|
|
619
|
+ //
|
|
|
620
|
+ // We take one of these guarded actions, in priority order:
|
|
|
621
|
+ //
|
|
|
622
|
+ // 1. If there's a pending synchronisation of all capabilities (e.g. GC),
|
|
|
623
|
+ // then give the capability to the task performing the sync.
|
|
|
624
|
+ // 2. If there's a task returning (e.g. from safe FFI) on this capability,
|
|
|
625
|
+ // then give the capability to the first such task.
|
|
|
626
|
+ // 3. If the next runnable thread on this capability is a bound thread,
|
|
|
627
|
+ // then give the capability to the corresponding bound task.
|
|
|
628
|
+ // 4. If there are no spare worker tasks for this capability,
|
|
|
629
|
+ // then start one and give the capability to the new task.
|
|
|
630
|
+ // 5. If there is some work to do on this capability (e.g. runnable thread),
|
|
|
631
|
+ // then give the capability to a worker task.
|
|
|
632
|
+ // 6. Otherwise leave the capability free/idle.
|
|
|
633
|
+ //
|
|
|
634
|
+ // There is one modifier to this priority list:
|
|
|
635
|
+ //
|
|
|
636
|
+ // * Setting wakeup_worker skips cases 2 & 3. This prioritises waking a
|
|
|
637
|
+ // worker over returning tasks or bound tasks.
|
|
|
638
|
+
|
|
|
639
|
+
|
|
|
640
|
+ // Guarded action 1:
|
|
|
641
|
+ // If there's a pending synchronisation of all capabilities (e.g. GC),
|
|
|
642
|
+ // then give the capability to the task performing the sync.
|
|
|
643
|
+ //
|
|
612
|
644
|
// If there is a pending sync, the task that requested the sync will
|
|
613
|
645
|
// subsequently use acquireAllCapabilities to place itself on the (front of
|
|
614
|
646
|
// the) returning_task list (of all capabilities). We will then be in one
|
| ... |
... |
@@ -656,17 +688,22 @@ releaseCapability_ (Capability* cap, |
|
656
|
688
|
return;
|
|
657
|
689
|
}
|
|
658
|
690
|
|
|
659
|
|
- // Check to see whether a worker thread can be given
|
|
660
|
|
- // the go-ahead to return the result of an external call..
|
|
661
|
|
- if (cap->n_returning_tasks != 0) {
|
|
|
691
|
+ // Skip guarded actions 2 & 3 if wakeup_worker. See the list of actions and
|
|
|
692
|
+ // modifiers above.
|
|
|
693
|
+
|
|
|
694
|
+ // Guarded action 2:
|
|
|
695
|
+ // If there's a task returning (e.g. from safe FFI) on this capability,
|
|
|
696
|
+ // then give the capability to the first such task.
|
|
|
697
|
+ if (!wakeup_worker && cap->n_returning_tasks != 0) {
|
|
662
|
698
|
giveCapabilityToTask(cap,cap->returning_tasks_hd);
|
|
663
|
699
|
// The Task pops itself from the queue (see waitForCapability())
|
|
664
|
700
|
return;
|
|
665
|
701
|
}
|
|
666
|
702
|
|
|
667
|
|
- // If the next thread on the run queue is a bound thread,
|
|
668
|
|
- // give this Capability to the appropriate Task.
|
|
669
|
|
- if (!emptyRunQueue(cap) && peekRunQueue(cap)->bound) {
|
|
|
703
|
+ // Guarded action 3:
|
|
|
704
|
+ // If the next runnable thread on this capability is a bound thread,
|
|
|
705
|
+ // then give the capability to the bound thread's corresponding task.
|
|
|
706
|
+ if (!wakeup_worker && !emptyRunQueue(cap) && peekRunQueue(cap)->bound) {
|
|
670
|
707
|
// Make sure we're not about to try to wake ourselves up
|
|
671
|
708
|
// ASSERT(task != cap->run_queue_hd->bound);
|
|
672
|
709
|
// assertion is false: in schedule() we force a yield after
|
| ... |
... |
@@ -677,11 +714,13 @@ releaseCapability_ (Capability* cap, |
|
677
|
714
|
return;
|
|
678
|
715
|
}
|
|
679
|
716
|
|
|
|
717
|
+ // Guarded action 4:
|
|
|
718
|
+ // If there are no spare worker tasks for this capability,
|
|
|
719
|
+ // then start one and give the capability to the new task.
|
|
680
|
720
|
if (!cap->spare_workers) {
|
|
681
|
|
- // Create a worker thread if we don't have one. If the system
|
|
682
|
|
- // is interrupted, we only create a worker task if there
|
|
683
|
|
- // are threads that need to be completed. If the system is
|
|
684
|
|
- // shutting down, we never create a new worker.
|
|
|
721
|
+ // If the system is interrupted, we only create a worker task if there
|
|
|
722
|
+ // are threads that need to be completed. If the system is shutting
|
|
|
723
|
+ // down, we never create a new worker.
|
|
685
|
724
|
if (getSchedState() < SCHED_SHUTTING_DOWN || !emptyRunQueue(cap)) {
|
|
686
|
725
|
debugTrace(DEBUG_sched,
|
|
687
|
726
|
"starting new worker on capability %d", cap->no);
|
| ... |
... |
@@ -690,10 +729,14 @@ releaseCapability_ (Capability* cap, |
|
690
|
729
|
}
|
|
691
|
730
|
}
|
|
692
|
731
|
|
|
693
|
|
- // If we have an unbound thread on the run queue, or if there's
|
|
694
|
|
- // anything else to do, give the Capability to a worker thread.
|
|
695
|
|
- if (always_wakeup ||
|
|
696
|
|
- !emptyRunQueue(cap) || !emptyInbox(cap) ||
|
|
|
732
|
+ // Guarded action 5:
|
|
|
733
|
+ // If there is some work to do on this capability (e.g. runnable thread),
|
|
|
734
|
+ // then give the capability to a worker task.
|
|
|
735
|
+ //
|
|
|
736
|
+ // We also check the cap->interrupt flag to avoid a race condition.
|
|
|
737
|
+ // See Note [prodCapability reliability].
|
|
|
738
|
+ //
|
|
|
739
|
+ if (!emptyRunQueue(cap) || !emptyInbox(cap) || cap->interrupt ||
|
|
697
|
740
|
(!cap->disabled && !emptySparkPoolCap(cap)) || globalWorkToDo()) {
|
|
698
|
741
|
if (cap->spare_workers) {
|
|
699
|
742
|
giveCapabilityToTask(cap, cap->spare_workers);
|
| ... |
... |
@@ -702,59 +745,14 @@ releaseCapability_ (Capability* cap, |
|
702
|
745
|
}
|
|
703
|
746
|
}
|
|
704
|
747
|
|
|
|
748
|
+ // Guarded action 6:
|
|
|
749
|
+ // Otherwise leave the capability free/idle.
|
|
705
|
750
|
#if defined(PROFILING)
|
|
706
|
751
|
cap->r.rCCCS = CCS_IDLE;
|
|
707
|
752
|
#endif
|
|
708
|
753
|
RELAXED_STORE(&last_free_capability[cap->node], cap);
|
|
709
|
754
|
debugTrace(DEBUG_sched, "freeing capability %d", cap->no);
|
|
710
|
755
|
}
|
|
711
|
|
-
|
|
712
|
|
-void
|
|
713
|
|
-releaseCapability (Capability* cap)
|
|
714
|
|
-{
|
|
715
|
|
- ACQUIRE_LOCK(&cap->lock);
|
|
716
|
|
- releaseCapability_(cap, false);
|
|
717
|
|
- RELEASE_LOCK(&cap->lock);
|
|
718
|
|
-}
|
|
719
|
|
-
|
|
720
|
|
-void
|
|
721
|
|
-releaseAndWakeupCapability (Capability* cap)
|
|
722
|
|
-{
|
|
723
|
|
- ACQUIRE_LOCK(&cap->lock);
|
|
724
|
|
- releaseCapability_(cap, true);
|
|
725
|
|
- RELEASE_LOCK(&cap->lock);
|
|
726
|
|
-}
|
|
727
|
|
-
|
|
728
|
|
-static void
|
|
729
|
|
-enqueueWorker (Capability* cap)
|
|
730
|
|
-{
|
|
731
|
|
- Task *task;
|
|
732
|
|
-
|
|
733
|
|
- task = cap->running_task;
|
|
734
|
|
-
|
|
735
|
|
- // If the Task is stopped, we shouldn't be yielding, we should
|
|
736
|
|
- // be just exiting.
|
|
737
|
|
- ASSERT(!task->stopped);
|
|
738
|
|
- ASSERT(task->worker);
|
|
739
|
|
-
|
|
740
|
|
- if (cap->n_spare_workers < MAX_SPARE_WORKERS)
|
|
741
|
|
- {
|
|
742
|
|
- task->next = cap->spare_workers;
|
|
743
|
|
- cap->spare_workers = task;
|
|
744
|
|
- cap->n_spare_workers++;
|
|
745
|
|
- }
|
|
746
|
|
- else
|
|
747
|
|
- {
|
|
748
|
|
- debugTrace(DEBUG_sched, "%d spare workers already, exiting",
|
|
749
|
|
- cap->n_spare_workers);
|
|
750
|
|
- releaseCapability_(cap,false);
|
|
751
|
|
- // hold the lock until after workerTaskStop; c.f. scheduleWorker()
|
|
752
|
|
- workerTaskStop(task);
|
|
753
|
|
- RELEASE_LOCK(&cap->lock);
|
|
754
|
|
- shutdownThread();
|
|
755
|
|
- }
|
|
756
|
|
-}
|
|
757
|
|
-
|
|
758
|
756
|
#endif
|
|
759
|
757
|
|
|
760
|
758
|
/*
|
| ... |
... |
@@ -1051,12 +1049,6 @@ static void waitForCapability_ (Task *task, |
|
1051
|
1049
|
* when either we know that the Capability should be given to another Task, or
|
|
1052
|
1050
|
* there is nothing to do right now. One of the following is true:
|
|
1053
|
1051
|
*
|
|
1054
|
|
- * - The current Task is a worker, and there's a bound thread at the head of
|
|
1055
|
|
- * the run queue (or vice versa)
|
|
1056
|
|
- *
|
|
1057
|
|
- * - The run queue is empty. We'll be woken up again when there's work to
|
|
1058
|
|
- * do.
|
|
1059
|
|
- *
|
|
1060
|
1052
|
* - Another Task is trying to do parallel GC (pending_sync == SYNC_GC_PAR).
|
|
1061
|
1053
|
* We should become a GC worker for a while.
|
|
1062
|
1054
|
*
|
| ... |
... |
@@ -1064,12 +1056,21 @@ static void waitForCapability_ (Task *task, |
|
1064
|
1056
|
* SYNC_GC_PAR), either to do a sequential GC, forkProcess, or
|
|
1065
|
1057
|
* setNumCapabilities. We should give up the Capability temporarily.
|
|
1066
|
1058
|
*
|
|
|
1059
|
+ * - There is a Task returning from a safe FFI call.
|
|
|
1060
|
+ *
|
|
|
1061
|
+ * - The current Task is a worker, and there's a bound thread at the head of
|
|
|
1062
|
+ * the run queue (or vice versa)
|
|
|
1063
|
+ *
|
|
|
1064
|
+ * - There is no work to do (empty run queue, inbox etc). We'll be woken up
|
|
|
1065
|
+ * again when there's work to do.
|
|
|
1066
|
+ *
|
|
1067
|
1067
|
* When yieldCapability returns *pCap will have been updated to the new
|
|
1068
|
1068
|
* capability held by the caller.
|
|
1069
|
1069
|
*
|
|
1070
|
1070
|
* ------------------------------------------------------------------------- */
|
|
1071
|
1071
|
|
|
1072
|
1072
|
#if defined(THREADED_RTS)
|
|
|
1073
|
+static bool tryEnqueueWorker (Capability* cap);
|
|
1073
|
1074
|
|
|
1074
|
1075
|
/* See Note [GC livelock] in Schedule.c for why we have gcAllowed
|
|
1075
|
1076
|
and return the bool */
|
| ... |
... |
@@ -1124,28 +1125,61 @@ yieldCapability |
|
1124
|
1125
|
// We must now release the capability and wait to be woken up again.
|
|
1125
|
1126
|
task->wakeup = false;
|
|
1126
|
1127
|
|
|
|
1128
|
+ // What happens next is a bit complicated. It has the following outline:
|
|
|
1129
|
+ //
|
|
|
1130
|
+ // 1. take the cap->lock
|
|
|
1131
|
+ // 2. "various stuff part A", pre-releaseCapability_ holding cap->lock
|
|
|
1132
|
+ // 3. release the capability
|
|
|
1133
|
+ // 4. "various stuff part B", post-releaseCapability_ holding cap->lock
|
|
|
1134
|
+ // 5. release the cap->lock
|
|
|
1135
|
+ // 6. "various stuff part C", post release cap->lock
|
|
|
1136
|
+ //
|
|
|
1137
|
+ // Much of the "various stuff" is also conditional, which complicates
|
|
|
1138
|
+ // matters further. To try and maintain clarity we use the following
|
|
|
1139
|
+ // variables in the conditions for the in-between steps.
|
|
|
1140
|
+ //
|
|
|
1141
|
+ bool terminate_worker = false;
|
|
|
1142
|
+ bool task_is_worker = isWorker(task);
|
|
|
1143
|
+ bool task_is_bound = isBoundTask(task);
|
|
|
1144
|
+
|
|
|
1145
|
+ // Step 1: take the cap->lock
|
|
1127
|
1146
|
ACQUIRE_LOCK(&cap->lock);
|
|
1128
|
1147
|
|
|
1129
|
|
- // If this is a worker thread, put it on the spare_workers queue
|
|
1130
|
|
- if (isWorker(task)) {
|
|
1131
|
|
- enqueueWorker(cap);
|
|
|
1148
|
+ // Step 2: "various stuff part A", pre-releaseCapability_ holding cap->lock
|
|
|
1149
|
+ if (task_is_worker) {
|
|
|
1150
|
+ // If this is a worker thread, try to put it on the spare_workers
|
|
|
1151
|
+ // queue or if it is surplus then we will terminate it.
|
|
|
1152
|
+ terminate_worker = !tryEnqueueWorker(cap);
|
|
1132
|
1153
|
}
|
|
1133
|
1154
|
|
|
1134
|
|
- releaseCapability_(cap, false);
|
|
|
1155
|
+ // Step 3: release the capability
|
|
|
1156
|
+ releaseCapability_(cap);
|
|
1135
|
1157
|
|
|
1136
|
|
- if (isWorker(task) || isBoundTask(task)) {
|
|
1137
|
|
- RELEASE_LOCK(&cap->lock);
|
|
1138
|
|
- cap = waitForWorkerCapability(task);
|
|
1139
|
|
- } else {
|
|
|
1158
|
+ // Step 4: "various stuff part B", post-releaseCapability_ holding cap->lock
|
|
|
1159
|
+ if (terminate_worker) {
|
|
|
1160
|
+ // hold the lock until after workerTaskStop; c.f. scheduleWorker()
|
|
|
1161
|
+ workerTaskStop(task);
|
|
|
1162
|
+ } else if (!task_is_worker && !task_is_bound) {
|
|
1140
|
1163
|
// Not a worker Task, or a bound Task. The only way we can be woken up
|
|
1141
|
1164
|
// again is to put ourselves on the returning_tasks queue, so that's
|
|
1142
|
|
- // what we do. We still hold cap->lock at this point
|
|
1143
|
|
- // The Task waiting for this Capability does not have it
|
|
1144
|
|
- // yet, so we can be sure to be woken up later. (see #10545)
|
|
|
1165
|
+ // what we do. We still hold cap->lock at this point. The Task waiting
|
|
|
1166
|
+ // for this Capability does not have it yet, so we can be sure to be
|
|
|
1167
|
+ // woken up later. (see #10545)
|
|
1145
|
1168
|
appendToReturningTaskQueue(cap,task);
|
|
1146
|
|
- RELEASE_LOCK(&cap->lock);
|
|
|
1169
|
+ }
|
|
|
1170
|
+
|
|
|
1171
|
+ // Step 5: release the cap->lock
|
|
|
1172
|
+ RELEASE_LOCK(&cap->lock);
|
|
|
1173
|
+
|
|
|
1174
|
+ // Step 6. "various stuff part C", post release cap->lock
|
|
|
1175
|
+ if (terminate_worker) {
|
|
|
1176
|
+ shutdownThread();
|
|
|
1177
|
+ } else if (task_is_worker || task_is_bound) {
|
|
|
1178
|
+ cap = waitForWorkerCapability(task);
|
|
|
1179
|
+ } else {
|
|
1147
|
1180
|
cap = waitForReturnCapability(task);
|
|
1148
|
1181
|
}
|
|
|
1182
|
+ // End of step 6.
|
|
1149
|
1183
|
|
|
1150
|
1184
|
debugTrace(DEBUG_sched, "resuming capability %d", cap->no);
|
|
1151
|
1185
|
ASSERT(cap->running_task == task);
|
| ... |
... |
@@ -1161,6 +1195,33 @@ yieldCapability |
|
1161
|
1195
|
return false;
|
|
1162
|
1196
|
}
|
|
1163
|
1197
|
|
|
|
1198
|
+// Returns true if it could enqueue, and false if the worker is surplus to
|
|
|
1199
|
+// requirements and should be terminated.
|
|
|
1200
|
+static bool tryEnqueueWorker (Capability* cap)
|
|
|
1201
|
+{
|
|
|
1202
|
+ Task *task = cap->running_task;
|
|
|
1203
|
+
|
|
|
1204
|
+ // If the Task is stopped, we shouldn't be yielding, we should
|
|
|
1205
|
+ // be just exiting.
|
|
|
1206
|
+ ASSERT(!task->stopped);
|
|
|
1207
|
+ ASSERT(task->worker);
|
|
|
1208
|
+ ASSERT_LOCK_HELD(&cap->lock);
|
|
|
1209
|
+
|
|
|
1210
|
+ if (cap->n_spare_workers < MAX_SPARE_WORKERS)
|
|
|
1211
|
+ {
|
|
|
1212
|
+ task->next = cap->spare_workers;
|
|
|
1213
|
+ cap->spare_workers = task;
|
|
|
1214
|
+ cap->n_spare_workers++;
|
|
|
1215
|
+ return true;
|
|
|
1216
|
+ }
|
|
|
1217
|
+ else
|
|
|
1218
|
+ {
|
|
|
1219
|
+ debugTrace(DEBUG_sched, "%d spare workers already, exiting",
|
|
|
1220
|
+ cap->n_spare_workers);
|
|
|
1221
|
+ return false;
|
|
|
1222
|
+ }
|
|
|
1223
|
+}
|
|
|
1224
|
+
|
|
1164
|
1225
|
#endif /* THREADED_RTS */
|
|
1165
|
1226
|
|
|
1166
|
1227
|
|
| ... |
... |
@@ -1391,8 +1452,40 @@ void releaseAllCapabilities(uint32_t n, Capability *keep_cap, Task *task) |
|
1391
|
1452
|
/* ----------------------------------------------------------------------------
|
|
1392
|
1453
|
* prodCapability
|
|
1393
|
1454
|
*
|
|
1394
|
|
- * If a Capability is currently idle, wake up a Task on it. Used to
|
|
1395
|
|
- * get every Capability into the GC.
|
|
|
1455
|
+ * If a Capability is currently idle, wake up a Task on it. If it is not idle,
|
|
|
1456
|
+ * interrupt it.
|
|
|
1457
|
+ *
|
|
|
1458
|
+ * Used to get every Capability into the GC. Also used for ctl-C handling
|
|
|
1459
|
+ * to get the capability to run the scheduler.
|
|
|
1460
|
+ *
|
|
|
1461
|
+ * Note [prodCapability reliability]
|
|
|
1462
|
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
1463
|
+ *
|
|
|
1464
|
+ * There's a potential race condition with prodCapability: a task running a
|
|
|
1465
|
+ * capability may be just about to yield when it is prodded and then let the
|
|
|
1466
|
+ * capability go idle, thus missing the prod. To avoid this we must check if
|
|
|
1467
|
+ * the capability has been prodded in a reliable fashion when the task is
|
|
|
1468
|
+ * yielding the capability. This is much like the issue of the race between
|
|
|
1469
|
+ * sending a capability a message and the capability going idle (where it's
|
|
|
1470
|
+ * vital that we don't let a capability go idle if there's a pending message).
|
|
|
1471
|
+ * The solution we use is much the same as the solution for messages: rely on
|
|
|
1472
|
+ * a shared variable set and tested while holding the cap->lock.
|
|
|
1473
|
+ *
|
|
|
1474
|
+ * The scheme is as follows:
|
|
|
1475
|
+ * 1. Set the cap->interrupt flag in prodCapability while holding the cap->lock
|
|
|
1476
|
+ * 2. Test the cap->interrupt flag in releaseCapability_ while holding the
|
|
|
1477
|
+ * cap->lock. If the flag is set then make sure to pass the capability to
|
|
|
1478
|
+ * a worker task (which often would be the task that was just releasing it).
|
|
|
1479
|
+ * 3. Make sure to reset the cap->interrupt flag at the start of the scheduler
|
|
|
1480
|
+ * loop. (Historically it was only reset when running a Haskell thread.)
|
|
|
1481
|
+ * We must do this before the scheduler yields again or we could loop
|
|
|
1482
|
+ * indefinitely.
|
|
|
1483
|
+ *
|
|
|
1484
|
+ * This scheme ensures that we run the scheduler loop once more, which will
|
|
|
1485
|
+ * react to the prod or reset the flag and yield again. In particular for
|
|
|
1486
|
+ * getting tasks into GC they will do that in yieldCapability, and for ctl-c
|
|
|
1487
|
+ * the scheduler will asks the I/O manager to poll for events which will pick
|
|
|
1488
|
+ * up pending signals.
|
|
1396
|
1489
|
* ------------------------------------------------------------------------- */
|
|
1397
|
1490
|
|
|
1398
|
1491
|
#if defined(THREADED_RTS)
|
| ... |
... |
@@ -1401,9 +1494,22 @@ void |
|
1401
|
1494
|
prodCapability (Capability *cap)
|
|
1402
|
1495
|
{
|
|
1403
|
1496
|
ACQUIRE_LOCK(&cap->lock);
|
|
1404
|
|
- if (!cap->running_task) {
|
|
1405
|
|
- releaseCapability_(cap,true);
|
|
|
1497
|
+ if (cap->running_task) {
|
|
|
1498
|
+ /* Set the cap->interrupt so that the capability will not go idle
|
|
|
1499
|
+ * before attending to the reason for the interrupt.
|
|
|
1500
|
+ * See Note [prodCapability reliability].
|
|
|
1501
|
+ */
|
|
|
1502
|
+ interruptCapability(cap);
|
|
|
1503
|
+ } else {
|
|
|
1504
|
+ /* Set the cap->interrupt first so that releaseCapability_ will see
|
|
|
1505
|
+ * that there is something to do on this capability and ensure the
|
|
|
1506
|
+ * cap is given to a task. See Note [prodCapability reliability].
|
|
|
1507
|
+ */
|
|
|
1508
|
+ interruptCapability(cap);
|
|
|
1509
|
+ releaseCapability_(cap);
|
|
1406
|
1510
|
}
|
|
|
1511
|
+ /* Notice that we use interruptCapability either way, but for different
|
|
|
1512
|
+ * reasons */
|
|
1407
|
1513
|
RELEASE_LOCK(&cap->lock);
|
|
1408
|
1514
|
}
|
|
1409
|
1515
|
|
| ... |
... |
@@ -1523,7 +1629,7 @@ shutdownCapability (Capability *cap USED_IF_THREADS, |
|
1523
|
1629
|
if (!emptyRunQueue(cap) || cap->spare_workers) {
|
|
1524
|
1630
|
debugTrace(DEBUG_sched,
|
|
1525
|
1631
|
"runnable threads or workers still alive, yielding");
|
|
1526
|
|
- releaseCapability_(cap,false); // this will wake up a worker
|
|
|
1632
|
+ releaseCapability_(cap); // this will wake up a worker
|
|
1527
|
1633
|
RELEASE_LOCK(&cap->lock);
|
|
1528
|
1634
|
yieldThread();
|
|
1529
|
1635
|
continue;
|