Duncan Coutts pushed to branch wip/dcoutts/capability-yield at Glasgow Haskell Compiler / GHC

Commits:

6 changed files:

Changes:

  • rts/Capability.c
    ... ... @@ -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;
    

  • rts/Capability.h
    ... ... @@ -262,16 +262,13 @@ void moreCapabilities (uint32_t from, uint32_t to);
    262 262
     // ASSUMES: cap->running_task is the current Task.
    
    263 263
     //
    
    264 264
     #if defined(THREADED_RTS)
    
    265
    -void releaseCapability           (Capability* cap);
    
    266
    -void releaseAndWakeupCapability  (Capability* cap);
    
    267
    -void releaseCapability_ (Capability* cap, bool always_wakeup);
    
    265
    +void releaseCapability  (Capability* cap);
    
    266
    +void releaseCapability_ (Capability* cap);
    
    268 267
     // assumes cap->lock is held
    
    269 268
     #else
    
    270 269
     // releaseCapability() is empty in non-threaded RTS
    
    271 270
     INLINE_HEADER void releaseCapability  (Capability* cap STG_UNUSED) {};
    
    272
    -INLINE_HEADER void releaseAndWakeupCapability  (Capability* cap STG_UNUSED) {};
    
    273
    -INLINE_HEADER void releaseCapability_ (Capability* cap STG_UNUSED,
    
    274
    -                                       bool always_wakeup STG_UNUSED) {};
    
    271
    +INLINE_HEADER void releaseCapability_ (Capability* cap STG_UNUSED) {};
    
    275 272
     #endif
    
    276 273
     
    
    277 274
     // declared in rts/include/rts/Threads.h:
    

  • rts/Messages.c
    ... ... @@ -49,11 +49,7 @@ void sendMessage(Capability *from_cap, Capability *to_cap, Message *msg)
    49 49
         recordClosureMutated(from_cap,(StgClosure*)msg);
    
    50 50
     
    
    51 51
         if (to_cap->running_task == NULL) {
    
    52
    -        /* Precond for releaseCapability_ is: running_task || always_wakeup.
    
    53
    -         * We have running_task == NULL, hence we must use always_wakeup. This
    
    54
    -         * is ok since the inbox is now non-empty, so we wake a task anyway.
    
    55
    -         */
    
    56
    -        releaseCapability_(to_cap, true /*always_wakeup*/);
    
    52
    +        releaseCapability_(to_cap);
    
    57 53
         } else {
    
    58 54
             interruptCapability(to_cap);
    
    59 55
         }
    

  • rts/RtsAPI.c
    ... ... @@ -691,7 +691,7 @@ rts_unlock (Capability *cap)
    691 691
         // random point in the future, which causes problems for
    
    692 692
         // freeTaskManager().
    
    693 693
         ACQUIRE_LOCK(&cap->lock);
    
    694
    -    releaseCapability_(cap,false);
    
    694
    +    releaseCapability_(cap);
    
    695 695
     
    
    696 696
         // Finally, we can release the Task to the free list.
    
    697 697
         exitMyTask();
    

  • rts/Schedule.c
    ... ... @@ -283,6 +283,10 @@ schedule (Capability *initialCapability, Task *task)
    283 283
             barf("sched_state: %" FMT_Word, sched_state);
    
    284 284
         }
    
    285 285
     
    
    286
    +    // Reset the interrupt flag upon starting the scheduler loop.
    
    287
    +    // See Note [prodCapability reliability].
    
    288
    +    RELAXED_STORE(&cap->interrupt, false);
    
    289
    +
    
    286 290
         scheduleFindWork(&cap);
    
    287 291
     
    
    288 292
         /* work pushing, currently relevant only for THREADED_RTS:
    
    ... ... @@ -430,9 +434,6 @@ run_thread:
    430 434
         SetLastError(t->saved_winerror);
    
    431 435
     #endif
    
    432 436
     
    
    433
    -    // reset the interrupt flag before running Haskell code
    
    434
    -    RELAXED_STORE(&cap->interrupt, false);
    
    435
    -
    
    436 437
         cap->in_haskell = true;
    
    437 438
         RELAXED_STORE(&cap->idle, false);
    
    438 439
     
    
    ... ... @@ -837,13 +838,9 @@ schedulePushWork(Capability *cap USED_IF_THREADS,
    837 838
             // release the capabilities
    
    838 839
             for (i = 0; i < n_free_caps; i++) {
    
    839 840
                 task->cap = free_caps[i];
    
    840
    -            if (sparkPoolSizeCap(cap) > 0) {
    
    841
    -                // If we have sparks to steal, wake up a worker on the
    
    842
    -                // capability, even if it has no threads to run.
    
    843
    -                releaseAndWakeupCapability(free_caps[i]);
    
    844
    -            } else {
    
    845
    -                releaseCapability(free_caps[i]);
    
    846
    -            }
    
    841
    +            // If there are sparks available, this will wake up a Task to run
    
    842
    +            // the Capability, even if it has no threads to run.
    
    843
    +            releaseCapability(free_caps[i]);
    
    847 844
             }
    
    848 845
         }
    
    849 846
         task->cap = cap; // reset to point to our Capability.
    
    ... ... @@ -1905,7 +1902,7 @@ forkProcess(HsStablePtr *entry
    1905 1902
     #endif
    
    1906 1903
     
    
    1907 1904
             for (i=0; i < n_capabilities; i++) {
    
    1908
    -            releaseCapability_(getCapability(i),false);
    
    1905
    +            releaseCapability_(getCapability(i));
    
    1909 1906
                 RELEASE_LOCK(&getCapability(i)->lock);
    
    1910 1907
             }
    
    1911 1908
     
    
    ... ... @@ -2320,7 +2317,7 @@ suspendThread (StgRegTable *reg, bool interruptible)
    2320 2317
     
    
    2321 2318
       suspendTask(cap,task);
    
    2322 2319
       cap->in_haskell = false;
    
    2323
    -  releaseCapability_(cap,false);
    
    2320
    +  releaseCapability_(cap);
    
    2324 2321
     
    
    2325 2322
       RELEASE_LOCK(&cap->lock);
    
    2326 2323
     
    
    ... ... @@ -2513,7 +2510,7 @@ void scheduleWorker (Capability *cap, Task *task)
    2513 2510
         // Capability has been shut down.
    
    2514 2511
         //
    
    2515 2512
         ACQUIRE_LOCK(&cap->lock);
    
    2516
    -    releaseCapability_(cap,false);
    
    2513
    +    releaseCapability_(cap);
    
    2517 2514
         workerTaskStop(task);
    
    2518 2515
         RELEASE_LOCK(&cap->lock);
    
    2519 2516
     }
    

  • rts/sm/GC.c
    ... ... @@ -1523,7 +1523,6 @@ waitForGcThreads (Capability *cap, bool idle_cap[])
    1523 1523
                 if (i == me || idle_cap[i]) { continue; }
    
    1524 1524
                 if (SEQ_CST_LOAD(&gc_threads[i]->wakeup) != GC_THREAD_STANDING_BY) {
    
    1525 1525
                     prodCapability(getCapability(i));
    
    1526
    -                interruptCapability(getCapability(i));
    
    1527 1526
                 }
    
    1528 1527
             }
    
    1529 1528
             // this 1ms timeout is not well justified. It's the shortest timeout we