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

Commits:

6 changed files:

Changes:

  • rts/Capability.c
    ... ... @@ -58,11 +58,25 @@ Capability **capabilities;
    58 58
     // locking, so we don't do that.
    
    59 59
     static Capability *last_free_capability[MAX_NUMA_NODES];
    
    60 60
     
    
    61
    +#if defined(THREADED_RTS)
    
    61 62
     /*
    
    62 63
      * Indicates that the RTS wants to synchronise all the Capabilities
    
    63 64
      * for some reason.  All Capabilities should yieldCapability().
    
    65
    + *
    
    66
    + * This is an atomic variable, all accesses must use appropriate atomics.
    
    67
    + */
    
    68
    +PendingSync *pending_sync = NULL;
    
    69
    +
    
    70
    +/*
    
    71
    + * sync_finished_cond allows threads which do not own any capability (e.g. the
    
    72
    + * concurrent mark thread) to participate in the sync protocol. In particular,
    
    73
    + * if such a thread requests a sync while sync is already in progress it will
    
    74
    + * block on sync_finished_cond, which will be signalled when the sync is
    
    75
    + * finished (by releaseAllCapabilities).
    
    64 76
      */
    
    65
    -PendingSync * volatile pending_sync = 0;
    
    77
    +static Condition sync_finished_cond;
    
    78
    +static Mutex sync_finished_mutex;
    
    79
    +#endif
    
    66 80
     
    
    67 81
     // Number of logical NUMA nodes
    
    68 82
     uint32_t n_numa_nodes;
    
    ... ... @@ -204,7 +218,7 @@ anySparks (void)
    204 218
     
    
    205 219
     #if defined(THREADED_RTS)
    
    206 220
     STATIC_INLINE void
    
    207
    -newReturningTask (Capability *cap, Task *task)
    
    221
    +appendToReturningTaskQueue (Capability *cap, Task *task)
    
    208 222
     {
    
    209 223
         ASSERT_LOCK_HELD(&cap->lock);
    
    210 224
         ASSERT(task->next == NULL);
    
    ... ... @@ -222,8 +236,25 @@ newReturningTask (Capability *cap, Task *task)
    222 236
         ASSERT_RETURNING_TASKS(cap,task);
    
    223 237
     }
    
    224 238
     
    
    239
    +STATIC_INLINE void
    
    240
    +prependToReturningTaskQueue (Capability *cap, Task *task)
    
    241
    +{
    
    242
    +    ASSERT_LOCK_HELD(&cap->lock);
    
    243
    +    ASSERT(task->next == NULL);
    
    244
    +    task->next = cap->returning_tasks_hd;
    
    245
    +    cap->returning_tasks_hd = task;
    
    246
    +    if (cap->returning_tasks_tl == NULL) {
    
    247
    +        cap->returning_tasks_tl = task;
    
    248
    +    }
    
    249
    +
    
    250
    +    // See Note [Data race in shouldYieldCapability] in Schedule.c.
    
    251
    +    RELAXED_ADD(&cap->n_returning_tasks, 1);
    
    252
    +
    
    253
    +    ASSERT_RETURNING_TASKS(cap,task);
    
    254
    +}
    
    255
    +
    
    225 256
     STATIC_INLINE Task *
    
    226
    -popReturningTask (Capability *cap)
    
    257
    +popReturningTaskQueue (Capability *cap)
    
    227 258
     {
    
    228 259
         ASSERT_LOCK_HELD(&cap->lock);
    
    229 260
         Task *task;
    
    ... ... @@ -349,6 +380,11 @@ initCapability (Capability *cap, uint32_t i)
    349 380
      * ------------------------------------------------------------------------- */
    
    350 381
     void initCapabilities (void)
    
    351 382
     {
    
    383
    +#if defined(THREADED_RTS)
    
    384
    +    initMutex(&sync_finished_mutex);
    
    385
    +    initCondition(&sync_finished_cond);
    
    386
    +#endif
    
    387
    +
    
    352 388
         /* Declare a couple capability sets representing the process and
    
    353 389
            clock domain. Each capability will get added to these capsets. */
    
    354 390
         traceCapsetCreate(CAPSET_OSPROCESS_DEFAULT, CapsetTypeOsProcess);
    
    ... ... @@ -523,7 +559,7 @@ giveCapabilityToTask (Capability *cap USED_IF_DEBUG, Task *task)
    523 559
     #endif
    
    524 560
     
    
    525 561
     /* ----------------------------------------------------------------------------
    
    526
    - * releaseCapability_
    
    562
    + * releaseCapability and releaseCapability_
    
    527 563
      *
    
    528 564
      * This serves two purposes:
    
    529 565
      *
    
    ... ... @@ -533,32 +569,60 @@ giveCapabilityToTask (Capability *cap USED_IF_DEBUG, Task *task)
    533 569
      *
    
    534 570
      * 2. There is no current task (cap->task == NULL), and thus the Capability
    
    535 571
      *    is idle, and we want to wake up an idle Task to animate the Capability.
    
    536
    - *    In this case set always_wakeup. See also prodCapability.
    
    537
    - *
    
    538
    - * Setting the always_wakeup parameter (almost) ensures that the capability is
    
    539
    - * not left idle: even if there is no known work to do, the capability will be
    
    540
    - * given to a worker task. There are two exceptions to this:
    
    541
    - *  1. if there is a pending sync then the capability is left idle, but in
    
    542
    - *     anticipation of whichever task initiated the sync picking it up shortly.
    
    543
    - *  2. if the scheduler is shutting down and there are no threads on the run
    
    544
    - *     queue and there are no spare workers then the capability is left idle.
    
    545
    - *     It is not entirely clear if this corner case is intentional.
    
    572
    + *    See also prodCapability.
    
    546 573
      *
    
    547
    - * The caller must hold cap->lock and will still hold it after the call returns.
    
    574
    + * Difference:
    
    575
    + * - releaseCapability  the caller /must not/ hold cap->lock.
    
    576
    + * - releaseCapability_ the caller /must/ hold cap->lock.
    
    548 577
      *
    
    549 578
      * N.B. May need to take all_tasks_mutex, if it needs to start a new task.
    
    550 579
      *
    
    551 580
      * ------------------------------------------------------------------------- */
    
    552 581
     
    
    553 582
     #if defined(THREADED_RTS)
    
    554
    -void
    
    555
    -releaseCapability_ (Capability* cap,
    
    556
    -                    bool always_wakeup)
    
    583
    +static void releaseCapability__ (Capability* cap,
    
    584
    +                                 bool always_wakeup,
    
    585
    +                                 bool wakeup_worker);
    
    586
    +
    
    587
    +void releaseCapability (Capability* cap)
    
    588
    +{
    
    589
    +    ACQUIRE_LOCK(&cap->lock);
    
    590
    +    releaseCapability__(cap, false /*always_wakeup*/,
    
    591
    +                             false /*wakeup_worker*/);
    
    592
    +    RELEASE_LOCK(&cap->lock);
    
    593
    +}
    
    594
    +
    
    595
    +void releaseCapability_ (Capability* cap)
    
    596
    +{
    
    597
    +    releaseCapability__(cap, false /*always_wakeup*/,
    
    598
    +                             false /*wakeup_worker*/);
    
    599
    +}
    
    600
    +
    
    601
    +/* The fact that we need an always_wakeup parameter for releaseCapability__ is
    
    602
    + * a design wart. The Capability layer knows about most but not all sources of
    
    603
    + * work for a capability. The always_wakeup parameter is there to account for
    
    604
    + * the ones it does /not/ know about (which is I/O manager stuff: I/O, timers
    
    605
    + * & signals).
    
    606
    + *
    
    607
    + * There are two sane designs:
    
    608
    + * 1. the Capability layer knows nothing about the sources of work that the
    
    609
    + *    scheduler might want to do
    
    610
    + * 2. the Capability layer knows *everything* about the sources of work.
    
    611
    + *
    
    612
    + * In neither sane design would we need this parameter. In the first design we
    
    613
    + * would know externally if we should be waking or releasing a task and would
    
    614
    + * instruct accordingly (probably by splitting releaseCapability to cover the
    
    615
    + * two cases). In the second design, it would simply know about all the sources
    
    616
    + * and so again there would be no need.
    
    617
    + */
    
    618
    +
    
    619
    +static void releaseCapability__ (Capability* cap,
    
    620
    +                                 bool always_wakeup,
    
    621
    +                                 bool wakeup_worker)
    
    557 622
     {
    
    558 623
         {
    
    559 624
             Task *task = cap->running_task;
    
    560 625
     
    
    561
    -        ASSERT(task || always_wakeup);
    
    562 626
             // To cover purpose 2 above, we allow the cap->running_task to be
    
    563 627
             // NULL, to handle cases where a thread (that is not itself a Task)
    
    564 628
             // needs to wake up an idle task for the capability.
    
    ... ... @@ -572,36 +636,98 @@ releaseCapability_ (Capability* cap,
    572 636
         // Remove the current Task owning the Capability (if any, see purpose 2).
    
    573 637
         RELAXED_STORE(&cap->running_task, NULL);
    
    574 638
     
    
    575
    -    // Check to see whether a worker thread can be given
    
    576
    -    // the go-ahead to return the result of an external call..
    
    577
    -    if (cap->n_returning_tasks != 0) {
    
    578
    -        giveCapabilityToTask(cap,cap->returning_tasks_hd);
    
    579
    -        // The Task pops itself from the queue (see waitForCapability())
    
    580
    -        return;
    
    581
    -    }
    
    639
    +    // We now look for a task to give the capability to, or otherwise we leave
    
    640
    +    // the capability free.
    
    641
    +    //
    
    642
    +    // We take one of these guarded actions, in priority order:
    
    643
    +    //
    
    644
    +    // 1. If there's a pending synchronisation of all capabilities (e.g. GC),
    
    645
    +    //    then give the capability to the task performing the sync.
    
    646
    +    // 2. If there's a task returning (e.g. from safe FFI) on this capability,
    
    647
    +    //    then give the capability to the first such task.
    
    648
    +    // 3. If the next runnable thread on this capability is a bound thread,
    
    649
    +    //    then give the capability to the corresponding bound task.
    
    650
    +    // 4. If there are no spare worker tasks for this capability,
    
    651
    +    //    then start one and give the capability to the new task.
    
    652
    +    // 5. If there is some work to do on this capability (e.g. runnable thread),
    
    653
    +    //    then give the capability to a worker task.
    
    654
    +    // 6. Otherwise leave the capability free/idle.
    
    655
    +    //
    
    656
    +    // There are two modifiers to this priority list:
    
    657
    +    //
    
    658
    +    // * Setting always_wakeup modifies the case 5 predicate to be always true.
    
    659
    +    //   This has the effect of giving the cap to a worker task, rather than
    
    660
    +    //   leaving the cap idle, even if there is no obvious work to do.
    
    661
    +    //
    
    662
    +    // * Setting wakeup_worker skips cases 2 & 3. This prioritises waking a
    
    663
    +    //   worker over returning tasks or bound tasks. It is also usually used in
    
    664
    +    //   combination with always_wakeup.
    
    665
    +
    
    582 666
     
    
    583
    -    // If there is a pending sync, then we should just leave the Capability
    
    584
    -    // free.  The thread trying to sync will be about to call
    
    585
    -    // waitForCapability().
    
    667
    +    // Guarded action 1:
    
    668
    +    // If there's a pending synchronisation of all capabilities (e.g. GC),
    
    669
    +    // then give the capability to the task performing the sync.
    
    670
    +    //
    
    671
    +    // If there is a pending sync, then we will be in one of two cases:
    
    672
    +    //
    
    673
    +    // 1. the task that requested the pending sync has put itself onto the
    
    674
    +    //    returning_tasks list; or
    
    675
    +    // 2. the task that requested the pending sync has not yet put itself onto
    
    676
    +    //    the returning_tasks list. This is unlikely but possible depending on
    
    677
    +    //    how the race is resolved.
    
    586 678
         //
    
    587
    -    // Note: this is *after* we check for a returning task above,
    
    588
    -    // because the task attempting to acquire all the capabilities may
    
    589
    -    // be currently in waitForCapability() waiting for this
    
    590
    -    // capability, in which case simply setting it as free would not
    
    591
    -    // wake up the waiting task.
    
    679
    +    // The cap->lock is used by both waitForCapability and releaseCapability_
    
    680
    +    // to ensure we are definitely in one of the two cases above, and not some
    
    681
    +    // hideous mish-mash.
    
    682
    +    //
    
    683
    +    // In the first case we can give the capability to that task. It is highly
    
    684
    +    // likely that the task has prepended itself to the returning task queue,
    
    685
    +    // so we can give the capability to the task at the head of the returning
    
    686
    +    // task queue. It is not a correctness issue however if another returning
    
    687
    +    // task gets run first (indeed this was the historical behaviour).
    
    688
    +    //
    
    689
    +    // Note that there can be false positives for this case: if any other
    
    690
    +    // returning task is queued on the returning tasks list. We will still
    
    691
    +    // incur delays if this occurs, scheduling those returning tasks. It is
    
    692
    +    // likely however that the task performing the sync gets to waiting before
    
    693
    +    // the task running the capability responds to the interrupt signal.
    
    694
    +    //
    
    695
    +    // In the second case we leave the capability free since the task trying to
    
    696
    +    // sync will be about to call waitForCapability().
    
    592 697
         //
    
    593 698
         // FIXME: this pending_sync approach is a poor design, hard to understand
    
    594 699
         // and subject to various unnecessary delays. See issues #27460 and #27473.
    
    595 700
         //
    
    596 701
         PendingSync *sync = SEQ_CST_LOAD(&pending_sync);
    
    597 702
         if (sync && (sync->type != SYNC_GC_PAR || sync->idle[cap->no])) {
    
    598
    -        debugTrace(DEBUG_sched, "sync pending, freeing capability %d", cap->no);
    
    703
    +        if (cap->n_returning_tasks != 0) {
    
    704
    +            // The task doing the sync should have used waitForCapability_
    
    705
    +            // using high_priority, so it should be at the head of the queue:
    
    706
    +            debugTrace(DEBUG_sched, "sync pending, passing capability %d", cap->no);
    
    707
    +            giveCapabilityToTask(cap,cap->returning_tasks_hd);
    
    708
    +            // The Task pops itself from the queue (see waitForCapability())
    
    709
    +        } else {
    
    710
    +            debugTrace(DEBUG_sched, "sync pending, freeing capability %d", cap->no);
    
    711
    +        }
    
    599 712
             return;
    
    600 713
         }
    
    601 714
     
    
    602
    -    // If the next thread on the run queue is a bound thread,
    
    603
    -    // give this Capability to the appropriate Task.
    
    604
    -    if (!emptyRunQueue(cap) && peekRunQueue(cap)->bound) {
    
    715
    +    // Skip guarded actions 2 & 3 if wakeup_worker. See the list of actions and
    
    716
    +    // modifiers above.
    
    717
    +
    
    718
    +    // Guarded action 2:
    
    719
    +    // If there's a task returning (e.g. from safe FFI) on this capability,
    
    720
    +    // then give the capability to the first such task.
    
    721
    +    if (!wakeup_worker && cap->n_returning_tasks != 0) {
    
    722
    +        giveCapabilityToTask(cap,cap->returning_tasks_hd);
    
    723
    +        // The Task pops itself from the queue (see waitForCapability())
    
    724
    +        return;
    
    725
    +    }
    
    726
    +
    
    727
    +    // Guarded action 3:
    
    728
    +    // If the next runnable thread on this capability is a bound thread,
    
    729
    +    // then give the capability to the bound thread's corresponding task.
    
    730
    +    if (!wakeup_worker && !emptyRunQueue(cap) && peekRunQueue(cap)->bound) {
    
    605 731
             // Make sure we're not about to try to wake ourselves up
    
    606 732
             // ASSERT(task != cap->run_queue_hd->bound);
    
    607 733
             // assertion is false: in schedule() we force a yield after
    
    ... ... @@ -612,11 +738,13 @@ releaseCapability_ (Capability* cap,
    612 738
             return;
    
    613 739
         }
    
    614 740
     
    
    741
    +    // Guarded action 4:
    
    742
    +    // If there are no spare worker tasks for this capability,
    
    743
    +    // then start one and give the capability to the new task.
    
    615 744
         if (!cap->spare_workers) {
    
    616
    -        // Create a worker thread if we don't have one.  If the system
    
    617
    -        // is interrupted, we only create a worker task if there
    
    618
    -        // are threads that need to be completed.  If the system is
    
    619
    -        // shutting down, we never create a new worker.
    
    745
    +        // If the system is interrupted, we only create a worker task if there
    
    746
    +        // are threads that need to be completed.  If the system is shutting
    
    747
    +        // down, we never create a new worker.
    
    620 748
             if (getSchedState() < SCHED_SHUTTING_DOWN || !emptyRunQueue(cap)) {
    
    621 749
                 debugTrace(DEBUG_sched,
    
    622 750
                            "starting new worker on capability %d", cap->no);
    
    ... ... @@ -625,8 +753,9 @@ releaseCapability_ (Capability* cap,
    625 753
             }
    
    626 754
         }
    
    627 755
     
    
    628
    -    // If we have an unbound thread on the run queue, or if there's
    
    629
    -    // anything else to do, give the Capability to a worker thread.
    
    756
    +    // Guarded action 5:
    
    757
    +    // If there is some work to do on this capability (e.g. runnable thread),
    
    758
    +    // then give the capability to a worker task.
    
    630 759
         if (always_wakeup ||
    
    631 760
             !emptyRunQueue(cap) || !emptyInbox(cap) ||
    
    632 761
             (!cap->disabled && !emptySparkPoolCap(cap)) || globalWorkToDo()) {
    
    ... ... @@ -637,59 +766,14 @@ releaseCapability_ (Capability* cap,
    637 766
             }
    
    638 767
         }
    
    639 768
     
    
    769
    +    // Guarded action 6:
    
    770
    +    // Otherwise leave the capability free/idle.
    
    640 771
     #if defined(PROFILING)
    
    641 772
         cap->r.rCCCS = CCS_IDLE;
    
    642 773
     #endif
    
    643 774
         RELAXED_STORE(&last_free_capability[cap->node], cap);
    
    644 775
         debugTrace(DEBUG_sched, "freeing capability %d", cap->no);
    
    645 776
     }
    
    646
    -
    
    647
    -void
    
    648
    -releaseCapability (Capability* cap)
    
    649
    -{
    
    650
    -    ACQUIRE_LOCK(&cap->lock);
    
    651
    -    releaseCapability_(cap, false);
    
    652
    -    RELEASE_LOCK(&cap->lock);
    
    653
    -}
    
    654
    -
    
    655
    -void
    
    656
    -releaseAndWakeupCapability (Capability* cap)
    
    657
    -{
    
    658
    -    ACQUIRE_LOCK(&cap->lock);
    
    659
    -    releaseCapability_(cap, true);
    
    660
    -    RELEASE_LOCK(&cap->lock);
    
    661
    -}
    
    662
    -
    
    663
    -static void
    
    664
    -enqueueWorker (Capability* cap)
    
    665
    -{
    
    666
    -    Task *task;
    
    667
    -
    
    668
    -    task = cap->running_task;
    
    669
    -
    
    670
    -    // If the Task is stopped, we shouldn't be yielding, we should
    
    671
    -    // be just exiting.
    
    672
    -    ASSERT(!task->stopped);
    
    673
    -    ASSERT(task->worker);
    
    674
    -
    
    675
    -    if (cap->n_spare_workers < MAX_SPARE_WORKERS)
    
    676
    -    {
    
    677
    -        task->next = cap->spare_workers;
    
    678
    -        cap->spare_workers = task;
    
    679
    -        cap->n_spare_workers++;
    
    680
    -    }
    
    681
    -    else
    
    682
    -    {
    
    683
    -        debugTrace(DEBUG_sched, "%d spare workers already, exiting",
    
    684
    -                   cap->n_spare_workers);
    
    685
    -        releaseCapability_(cap,false);
    
    686
    -        // hold the lock until after workerTaskStop; c.f. scheduleWorker()
    
    687
    -        workerTaskStop(task);
    
    688
    -        RELEASE_LOCK(&cap->lock);
    
    689
    -        shutdownThread();
    
    690
    -    }
    
    691
    -}
    
    692
    -
    
    693 777
     #endif
    
    694 778
     
    
    695 779
     /*
    
    ... ... @@ -822,7 +906,7 @@ static Capability * waitForReturnCapability (Task *task)
    822 906
                     continue;
    
    823 907
                 }
    
    824 908
                 RELAXED_STORE(&cap->running_task, task);
    
    825
    -            popReturningTask(cap);
    
    909
    +            popReturningTaskQueue(cap);
    
    826 910
                 RELEASE_LOCK(&cap->lock);
    
    827 911
                 break;
    
    828 912
             }
    
    ... ... @@ -893,38 +977,65 @@ static Capability * find_capability_for_task(const Task * task)
    893 977
     #endif /* THREADED_RTS */
    
    894 978
     
    
    895 979
     /* ----------------------------------------------------------------------------
    
    896
    - * waitForCapability (Capability **pCap, Task *task)
    
    980
    + * Capability *waitForCapability (Task *task)
    
    897 981
      *
    
    898
    - * Purpose:  when an OS thread returns from an external call,
    
    899
    - * it calls waitForCapability() (via Schedule.resumeThread())
    
    900
    - * to wait for permission to enter the RTS & communicate the
    
    901
    - * result of the external call back to the Haskell thread that
    
    902
    - * made it.
    
    982
    + * Purpose:  when an OS thread returns from an external call, it calls
    
    983
    + * waitForCapability() (via Schedule.resumeThread()) to wait for
    
    984
    + * permission to enter the RTS & communicate the result of the external
    
    985
    + * call back to the Haskell thread that made it. The task must already
    
    986
    + * be associated with a capability.
    
    903 987
      *
    
    904
    - * pCap is strictly an output.
    
    988
    + * Capability *waitForSomeCapability (Task *task)
    
    905 989
      *
    
    990
    + * Like waitForCapability but the task need not already be associated
    
    991
    + * with a capability. If it is not associated, an appropriate one will
    
    992
    + * be chosen. Used in rts_lock(), for calling into the RTS from outside.
    
    906 993
      * ------------------------------------------------------------------------- */
    
    907 994
     
    
    908
    -void waitForCapability (Capability **pCap, Task *task)
    
    909
    -{
    
    910 995
     #if !defined(THREADED_RTS)
    
    911
    -
    
    996
    +Capability *waitForSomeCapability (Task *task)
    
    997
    +{
    
    912 998
         MainCapability.running_task = task;
    
    913 999
         task->cap = &MainCapability;
    
    914
    -    *pCap = &MainCapability;
    
    1000
    +    return &MainCapability;
    
    1001
    +}
    
    1002
    +
    
    1003
    +void waitForCapability (Task *task)
    
    1004
    +{
    
    1005
    +    waitForSomeCapability(task);
    
    1006
    +}
    
    915 1007
     
    
    916 1008
     #else
    
    917
    -    Capability *cap = *pCap;
    
    918 1009
     
    
    1010
    +static void waitForCapability_ (Task *task,
    
    1011
    +                                bool high_priority);
    
    1012
    +
    
    1013
    +Capability *waitForSomeCapability (Task *task)
    
    1014
    +{
    
    1015
    +    Capability *cap = task->cap;
    
    919 1016
         if (cap == NULL) {
    
    920 1017
             cap = find_capability_for_task(task);
    
    921 1018
     
    
    922 1019
             // record the Capability as the one this Task is now associated with.
    
    923 1020
             task->cap = cap;
    
    924
    -    } else {
    
    925
    -        ASSERT(task->cap == cap);
    
    926 1021
         }
    
    927 1022
     
    
    1023
    +    waitForCapability_(task, false /*high_priority*/);
    
    1024
    +
    
    1025
    +    return task->cap;
    
    1026
    +}
    
    1027
    +
    
    1028
    +void waitForCapability (Task *task)
    
    1029
    +{
    
    1030
    +    waitForCapability_(task, false /*high_priority*/);
    
    1031
    +}
    
    1032
    +
    
    1033
    +static void waitForCapability_ (Task *task,
    
    1034
    +                                bool high_priority)
    
    1035
    +{
    
    1036
    +    Capability *cap = task->cap;
    
    1037
    +    ASSERT(task->cap);
    
    1038
    +
    
    928 1039
         debugTrace(DEBUG_sched, "returning; I want capability %d", cap->no);
    
    929 1040
     
    
    930 1041
         ACQUIRE_LOCK(&cap->lock);
    
    ... ... @@ -933,7 +1044,11 @@ void waitForCapability (Capability **pCap, Task *task)
    933 1044
             RELAXED_STORE(&cap->running_task, task);
    
    934 1045
             RELEASE_LOCK(&cap->lock);
    
    935 1046
         } else {
    
    936
    -        newReturningTask(cap,task);
    
    1047
    +        if (high_priority) {
    
    1048
    +            prependToReturningTaskQueue(cap,task);
    
    1049
    +        } else {
    
    1050
    +            appendToReturningTaskQueue(cap,task);
    
    1051
    +        }
    
    937 1052
             RELEASE_LOCK(&cap->lock);
    
    938 1053
             cap = waitForReturnCapability(task);
    
    939 1054
         }
    
    ... ... @@ -945,10 +1060,8 @@ void waitForCapability (Capability **pCap, Task *task)
    945 1060
         ASSERT_FULL_CAPABILITY_INVARIANTS(cap, task);
    
    946 1061
     
    
    947 1062
         debugTrace(DEBUG_sched, "resuming capability %d", cap->no);
    
    948
    -
    
    949
    -    *pCap = cap;
    
    950
    -#endif
    
    951 1063
     }
    
    1064
    +#endif
    
    952 1065
     
    
    953 1066
     /* ----------------------------------------------------------------------------
    
    954 1067
      * yieldCapability
    
    ... ... @@ -957,12 +1070,6 @@ void waitForCapability (Capability **pCap, Task *task)
    957 1070
      * when either we know that the Capability should be given to another Task, or
    
    958 1071
      * there is nothing to do right now.  One of the following is true:
    
    959 1072
      *
    
    960
    - *    - The current Task is a worker, and there's a bound thread at the head of
    
    961
    - *      the run queue (or vice versa)
    
    962
    - *
    
    963
    - *    - The run queue is empty.  We'll be woken up again when there's work to
    
    964
    - *      do.
    
    965
    - *
    
    966 1073
      *    - Another Task is trying to do parallel GC (pending_sync == SYNC_GC_PAR).
    
    967 1074
      *      We should become a GC worker for a while.
    
    968 1075
      *
    
    ... ... @@ -970,12 +1077,21 @@ void waitForCapability (Capability **pCap, Task *task)
    970 1077
      *      SYNC_GC_PAR), either to do a sequential GC, forkProcess, or
    
    971 1078
      *      setNumCapabilities.  We should give up the Capability temporarily.
    
    972 1079
      *
    
    1080
    + *    - There is a Task returning from a safe FFI call.
    
    1081
    + *
    
    1082
    + *    - The current Task is a worker, and there's a bound thread at the head of
    
    1083
    + *      the run queue (or vice versa)
    
    1084
    + *
    
    1085
    + *    - There is no work to do (empty run queue, inbox etc).  We'll be woken up
    
    1086
    + *      again when there's work to do.
    
    1087
    + *
    
    973 1088
      * When yieldCapability returns *pCap will have been updated to the new
    
    974 1089
      * capability held by the caller.
    
    975 1090
      *
    
    976 1091
      * ------------------------------------------------------------------------- */
    
    977 1092
     
    
    978 1093
     #if defined(THREADED_RTS)
    
    1094
    +static bool tryEnqueueWorker (Capability* cap);
    
    979 1095
     
    
    980 1096
     /* See Note [GC livelock] in Schedule.c for why we have gcAllowed
    
    981 1097
        and return the bool */
    
    ... ... @@ -1030,28 +1146,61 @@ yieldCapability
    1030 1146
         // We must now release the capability and wait to be woken up again.
    
    1031 1147
         task->wakeup = false;
    
    1032 1148
     
    
    1149
    +    // What happens next is a bit complicated. It has the following outline:
    
    1150
    +    //
    
    1151
    +    //  1. take the cap->lock
    
    1152
    +    //  2. "various stuff part A", pre-releaseCapability_ holding cap->lock
    
    1153
    +    //  3. release the capability
    
    1154
    +    //  4. "various stuff part B", post-releaseCapability_ holding cap->lock
    
    1155
    +    //  5. release the cap->lock
    
    1156
    +    //  6. "various stuff part C", post release cap->lock
    
    1157
    +    //
    
    1158
    +    // Much of the "various stuff" is also conditional, which complicates
    
    1159
    +    // matters further. To try and maintain clarity we use the following
    
    1160
    +    // variables in the conditions for the in-between steps.
    
    1161
    +    //
    
    1162
    +    bool terminate_worker = false;
    
    1163
    +    bool task_is_worker   = isWorker(task);
    
    1164
    +    bool task_is_bound    = isBoundTask(task);
    
    1165
    +
    
    1166
    +    // Step 1: take the cap->lock
    
    1033 1167
         ACQUIRE_LOCK(&cap->lock);
    
    1034 1168
     
    
    1035
    -    // If this is a worker thread, put it on the spare_workers queue
    
    1036
    -    if (isWorker(task)) {
    
    1037
    -        enqueueWorker(cap);
    
    1169
    +    // Step 2: "various stuff part A", pre-releaseCapability_ holding cap->lock
    
    1170
    +    if (task_is_worker) {
    
    1171
    +        // If this is a worker thread, try to put it on the spare_workers
    
    1172
    +        // queue or if it is surplus then we will terminate it.
    
    1173
    +        terminate_worker = !tryEnqueueWorker(cap);
    
    1038 1174
         }
    
    1039 1175
     
    
    1040
    -    releaseCapability_(cap, false);
    
    1176
    +    // Step 3: release the capability
    
    1177
    +    releaseCapability_(cap);
    
    1041 1178
     
    
    1042
    -    if (isWorker(task) || isBoundTask(task)) {
    
    1043
    -        RELEASE_LOCK(&cap->lock);
    
    1044
    -        cap = waitForWorkerCapability(task);
    
    1045
    -    } else {
    
    1179
    +    // Step 4: "various stuff part B", post-releaseCapability_ holding cap->lock
    
    1180
    +    if (terminate_worker) {
    
    1181
    +        // hold the lock until after workerTaskStop; c.f. scheduleWorker()
    
    1182
    +        workerTaskStop(task);
    
    1183
    +    } else if (!task_is_worker && !task_is_bound) {
    
    1046 1184
             // Not a worker Task, or a bound Task.  The only way we can be woken up
    
    1047 1185
             // again is to put ourselves on the returning_tasks queue, so that's
    
    1048
    -        // what we do.  We still hold cap->lock at this point
    
    1049
    -        // The Task waiting for this Capability does not have it
    
    1050
    -        // yet, so we can be sure to be woken up later. (see #10545)
    
    1051
    -        newReturningTask(cap,task);
    
    1052
    -        RELEASE_LOCK(&cap->lock);
    
    1186
    +        // what we do.  We still hold cap->lock at this point. The Task waiting
    
    1187
    +        // for this Capability does not have it yet, so we can be sure to be
    
    1188
    +        // woken up later. (see #10545)
    
    1189
    +        appendToReturningTaskQueue(cap,task);
    
    1190
    +    }
    
    1191
    +
    
    1192
    +    // Step 5: release the cap->lock
    
    1193
    +    RELEASE_LOCK(&cap->lock);
    
    1194
    +
    
    1195
    +    // Step 6. "various stuff part C", post release cap->lock
    
    1196
    +    if (terminate_worker) {
    
    1197
    +        shutdownThread();
    
    1198
    +    } else if (task_is_worker || task_is_bound) {
    
    1199
    +        cap = waitForWorkerCapability(task);
    
    1200
    +    } else {
    
    1053 1201
             cap = waitForReturnCapability(task);
    
    1054 1202
         }
    
    1203
    +    // End of step 6.
    
    1055 1204
     
    
    1056 1205
         debugTrace(DEBUG_sched, "resuming capability %d", cap->no);
    
    1057 1206
         ASSERT(cap->running_task == task);
    
    ... ... @@ -1067,8 +1216,226 @@ yieldCapability
    1067 1216
         return false;
    
    1068 1217
     }
    
    1069 1218
     
    
    1219
    +// Returns true if it could enqueue, and false if the worker is surplus to
    
    1220
    +// requirements and should be terminated.
    
    1221
    +static bool tryEnqueueWorker (Capability* cap)
    
    1222
    +{
    
    1223
    +    Task *task = cap->running_task;
    
    1224
    +
    
    1225
    +    // If the Task is stopped, we shouldn't be yielding, we should
    
    1226
    +    // be just exiting.
    
    1227
    +    ASSERT(!task->stopped);
    
    1228
    +    ASSERT(task->worker);
    
    1229
    +    ASSERT_LOCK_HELD(&cap->lock);
    
    1230
    +
    
    1231
    +    if (cap->n_spare_workers < MAX_SPARE_WORKERS)
    
    1232
    +    {
    
    1233
    +        task->next = cap->spare_workers;
    
    1234
    +        cap->spare_workers = task;
    
    1235
    +        cap->n_spare_workers++;
    
    1236
    +        return true;
    
    1237
    +    }
    
    1238
    +    else
    
    1239
    +    {
    
    1240
    +        debugTrace(DEBUG_sched, "%d spare workers already, exiting",
    
    1241
    +                   cap->n_spare_workers);
    
    1242
    +        return false;
    
    1243
    +    }
    
    1244
    +}
    
    1245
    +
    
    1070 1246
     #endif /* THREADED_RTS */
    
    1071 1247
     
    
    1248
    +
    
    1249
    +/* -----------------------------------------------------------------------------
    
    1250
    + * stopAllCapabilities()
    
    1251
    + *
    
    1252
    + * Stop all Haskell execution.  This is used when we need to make some global
    
    1253
    + * change to the system, such as altering the number of capabilities, or
    
    1254
    + * forking.
    
    1255
    + *
    
    1256
    + * pCap may be NULL in the event that the caller doesn't yet own a capability.
    
    1257
    + *
    
    1258
    + * To resume after stopAllCapabilities(), use releaseAllCapabilities().
    
    1259
    + * -------------------------------------------------------------------------- */
    
    1260
    +
    
    1261
    +#if defined(THREADED_RTS)
    
    1262
    +void stopAllCapabilities
    
    1263
    +    ( Capability **pCap     // [in/out] This thread's task's owned capability.
    
    1264
    +                            //          pCap may be NULL if no capability is owned.
    
    1265
    +                            //          Else *pCap != NULL
    
    1266
    +                            // On return, set to the task's newly owned
    
    1267
    +                            // capability (task->cap). Though, the Task will
    
    1268
    +                            // technically own all capabilities.
    
    1269
    +    , Task *task            // [in] This thread's task.
    
    1270
    +    )
    
    1271
    +{
    
    1272
    +    stopAllCapabilitiesWith(pCap, task, SYNC_OTHER);
    
    1273
    +}
    
    1274
    +
    
    1275
    +void stopAllCapabilitiesWith (Capability **pCap, Task *task, SyncType sync_type)
    
    1276
    +{
    
    1277
    +    bool was_syncing;
    
    1278
    +    SyncType prev_sync_type;
    
    1279
    +
    
    1280
    +    PendingSync sync = {
    
    1281
    +        .type = sync_type,
    
    1282
    +        .idle = NULL,
    
    1283
    +        .task = task
    
    1284
    +    };
    
    1285
    +
    
    1286
    +    do {
    
    1287
    +        was_syncing = requestSync(pCap, task, &sync, &prev_sync_type);
    
    1288
    +    } while (was_syncing);
    
    1289
    +
    
    1290
    +    acquireAllCapabilities(pCap ? *pCap : NULL, task);
    
    1291
    +
    
    1292
    +    resetSync();
    
    1293
    +}
    
    1294
    +#endif
    
    1295
    +
    
    1296
    +/* -----------------------------------------------------------------------------
    
    1297
    + * requestSync()
    
    1298
    + *
    
    1299
    + * Commence a synchronisation between all capabilities.  Normally not called
    
    1300
    + * directly, instead use stopAllCapabilities().  This is used by the GC, which
    
    1301
    + * has some special synchronisation requirements.
    
    1302
    + *
    
    1303
    + * Note that this can be called in two ways:
    
    1304
    + *
    
    1305
    + * - where *pcap points to a capability owned by the caller: in this case
    
    1306
    + *   *prev_sync_type will reflect the in-progress sync type on return, if one
    
    1307
    + *   *was found
    
    1308
    + *
    
    1309
    + *  - where pcap == NULL: in this case the caller doesn't hold a capability.
    
    1310
    + *    we only return whether or not a pending sync was found and prev_sync_type
    
    1311
    + *    is unchanged.
    
    1312
    + *
    
    1313
    + * Returns:
    
    1314
    + *    false if we successfully got a sync
    
    1315
    + *    true  if there was another sync request in progress,
    
    1316
    + *             and we yielded to it.  The value returned is the
    
    1317
    + *             type of the other sync request.
    
    1318
    + * -------------------------------------------------------------------------- */
    
    1319
    +
    
    1320
    +#if defined(THREADED_RTS)
    
    1321
    +bool requestSync
    
    1322
    +    ( Capability **pcap         // [in/out] This thread's task's owned capability.
    
    1323
    +                                // May change if there is an existing sync (true is returned).
    
    1324
    +                                // Precondition:
    
    1325
    +                                //      pcap may be NULL
    
    1326
    +                                //      *pcap != NULL
    
    1327
    +    , Task *task                // [in] This thread's task.
    
    1328
    +    , PendingSync *new_sync     // [in] The new requested sync.
    
    1329
    +    , SyncType *prev_sync_type  // [out] Only set if there is an existing sync (true is returned).
    
    1330
    +    )
    
    1331
    +{
    
    1332
    +    PendingSync *sync;
    
    1333
    +
    
    1334
    +    sync = (PendingSync*)cas((StgVolatilePtr)&pending_sync,
    
    1335
    +                             (StgWord)NULL,
    
    1336
    +                             (StgWord)new_sync);
    
    1337
    +
    
    1338
    +    if (sync != NULL)
    
    1339
    +    {
    
    1340
    +        // sync is valid until we have called yieldCapability().
    
    1341
    +        // After the sync is completed, we cannot read that struct any
    
    1342
    +        // more because it has been freed.
    
    1343
    +        *prev_sync_type = sync->type;
    
    1344
    +        if (pcap == NULL) {
    
    1345
    +            // The caller does not hold a capability (e.g. may be a concurrent
    
    1346
    +            // mark thread). Consequently we must wait until the pending sync is
    
    1347
    +            // finished before proceeding to ensure we don't loop.
    
    1348
    +            // TODO: Don't busy-wait
    
    1349
    +            ACQUIRE_LOCK(&sync_finished_mutex);
    
    1350
    +            while (pending_sync) {
    
    1351
    +                waitCondition(&sync_finished_cond, &sync_finished_mutex);
    
    1352
    +            }
    
    1353
    +            RELEASE_LOCK(&sync_finished_mutex);
    
    1354
    +        } else {
    
    1355
    +            do {
    
    1356
    +                debugTrace(DEBUG_sched, "someone else is trying to sync (%d)...",
    
    1357
    +                          sync->type);
    
    1358
    +                ASSERT(*pcap);
    
    1359
    +                yieldCapability(pcap,task,true);
    
    1360
    +                sync = SEQ_CST_LOAD(&pending_sync);
    
    1361
    +            } while (sync != NULL);
    
    1362
    +        }
    
    1363
    +
    
    1364
    +        // NOTE: task->cap might have changed now
    
    1365
    +        return true;
    
    1366
    +    }
    
    1367
    +    else
    
    1368
    +    {
    
    1369
    +        return false;
    
    1370
    +    }
    
    1371
    +}
    
    1372
    +
    
    1373
    +void resetSync (void)
    
    1374
    +{
    
    1375
    +    RELAXED_STORE(&pending_sync, 0);
    
    1376
    +    signalCondition(&sync_finished_cond);
    
    1377
    +}
    
    1378
    +#endif
    
    1379
    +
    
    1380
    +/* -----------------------------------------------------------------------------
    
    1381
    + * acquireAllCapabilities()
    
    1382
    + *
    
    1383
    + * Grab all the capabilities except the one we already hold (cap may be NULL if
    
    1384
    + * the caller does not currently hold a capability). Used when synchronising
    
    1385
    + * before a single-threaded GC (SYNC_SEQ_GC), and before a fork (SYNC_OTHER).
    
    1386
    + *
    
    1387
    + * Only call this after requestSync(), otherwise a deadlock might
    
    1388
    + * ensue if another thread is trying to synchronise.
    
    1389
    + * -------------------------------------------------------------------------- */
    
    1390
    +
    
    1391
    +#if defined(THREADED_RTS)
    
    1392
    +void acquireAllCapabilities(Capability *cap, Task *task)
    
    1393
    +{
    
    1394
    +    Capability *tmpcap = NULL;
    
    1395
    +    uint32_t i;
    
    1396
    +
    
    1397
    +    ASSERT(SEQ_CST_LOAD(&pending_sync) != NULL);
    
    1398
    +    for (i=0; i < getNumCapabilities(); i++) {
    
    1399
    +        debugTrace(DEBUG_sched, "grabbing all the capabilities (%d/%d)",
    
    1400
    +                   i, getNumCapabilities());
    
    1401
    +        tmpcap = getCapability(i);
    
    1402
    +        if (tmpcap != cap) {
    
    1403
    +            task->cap = tmpcap;
    
    1404
    +            waitForCapability_(task, true /*high_priority*/);
    
    1405
    +
    
    1406
    +            // Note that waitForCapability only waits for the capability
    
    1407
    +            // the task is associated with. There's no task migration here.
    
    1408
    +            ASSERT(task->cap == tmpcap);
    
    1409
    +        }
    
    1410
    +    }
    
    1411
    +    ASSERT(tmpcap != NULL);
    
    1412
    +    task->cap = cap == NULL ? tmpcap : cap;
    
    1413
    +}
    
    1414
    +#endif
    
    1415
    +
    
    1416
    +/* -----------------------------------------------------------------------------
    
    1417
    + * releaseAllCapabilities()
    
    1418
    + *
    
    1419
    + * Assuming this thread holds all the capabilities, release them all (except for
    
    1420
    + * the one passed in as keep_cap, if non-NULL).
    
    1421
    + * -------------------------------------------------------------------------- */
    
    1422
    +
    
    1423
    +#if defined(THREADED_RTS)
    
    1424
    +void releaseAllCapabilities(uint32_t n, Capability *keep_cap, Task *task)
    
    1425
    +{
    
    1426
    +    uint32_t i;
    
    1427
    +    ASSERT( task != NULL);
    
    1428
    +    for (i = 0; i < n; i++) {
    
    1429
    +        Capability *tmpcap = getCapability(i);
    
    1430
    +        if (keep_cap != tmpcap) {
    
    1431
    +            task->cap = tmpcap;
    
    1432
    +            releaseCapability(tmpcap);
    
    1433
    +        }
    
    1434
    +    }
    
    1435
    +    task->cap = keep_cap;
    
    1436
    +}
    
    1437
    +#endif
    
    1438
    +
    
    1072 1439
     /*
    
    1073 1440
      * Note [migrated bound threads]
    
    1074 1441
      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    ... ... @@ -1117,7 +1484,14 @@ prodCapability (Capability *cap)
    1117 1484
     {
    
    1118 1485
         ACQUIRE_LOCK(&cap->lock);
    
    1119 1486
         if (!cap->running_task) {
    
    1120
    -        releaseCapability_(cap,true);
    
    1487
    +        /* We have to use always_wakeup here because when prodCapability is
    
    1488
    +         * used for ctl-c, releaseCapability__ does not know about pending
    
    1489
    +         * signals (or the I/O managers generally) as one of the set of
    
    1490
    +         * conditions to look for when deciding if a Task should be woken up
    
    1491
    +         * to run the Capability. This is a bit of a design wart.
    
    1492
    +         */
    
    1493
    +        releaseCapability__(cap, true  /*always_wakeup*/,
    
    1494
    +                                 false /*wakeup_worker*/);
    
    1121 1495
         }
    
    1122 1496
         RELEASE_LOCK(&cap->lock);
    
    1123 1497
     }
    
    ... ... @@ -1238,7 +1612,7 @@ shutdownCapability (Capability *cap USED_IF_THREADS,
    1238 1612
             if (!emptyRunQueue(cap) || cap->spare_workers) {
    
    1239 1613
                 debugTrace(DEBUG_sched,
    
    1240 1614
                            "runnable threads or workers still alive, yielding");
    
    1241
    -            releaseCapability_(cap,false); // this will wake up a worker
    
    1615
    +            releaseCapability_(cap); // this will wake up a worker
    
    1242 1616
                 RELEASE_LOCK(&cap->lock);
    
    1243 1617
                 yieldThread();
    
    1244 1618
                 continue;
    

  • rts/Capability.h
    ... ... @@ -254,16 +254,13 @@ void moreCapabilities (uint32_t from, uint32_t to);
    254 254
     // ASSUMES: cap->running_task is the current Task.
    
    255 255
     //
    
    256 256
     #if defined(THREADED_RTS)
    
    257
    -void releaseCapability           (Capability* cap);
    
    258
    -void releaseAndWakeupCapability  (Capability* cap);
    
    259
    -void releaseCapability_ (Capability* cap, bool always_wakeup);
    
    257
    +void releaseCapability  (Capability* cap);
    
    258
    +void releaseCapability_ (Capability* cap);
    
    260 259
     // assumes cap->lock is held
    
    261 260
     #else
    
    262 261
     // releaseCapability() is empty in non-threaded RTS
    
    263 262
     INLINE_HEADER void releaseCapability  (Capability* cap STG_UNUSED) {};
    
    264
    -INLINE_HEADER void releaseAndWakeupCapability  (Capability* cap STG_UNUSED) {};
    
    265
    -INLINE_HEADER void releaseCapability_ (Capability* cap STG_UNUSED,
    
    266
    -                                       bool always_wakeup STG_UNUSED) {};
    
    263
    +INLINE_HEADER void releaseCapability_ (Capability* cap STG_UNUSED) {};
    
    267 264
     #endif
    
    268 265
     
    
    269 266
     // declared in rts/include/rts/Threads.h:
    
    ... ... @@ -283,46 +280,22 @@ INLINE_HEADER Capability *getCapability(uint32_t i)
    283 280
         return RELAXED_LOAD(&capabilities[i]);
    
    284 281
     }
    
    285 282
     
    
    283
    +// Acquire the task's associated capability, waiting as necessary.
    
    286 284
     //
    
    287
    -// Types of global synchronisation
    
    288
    -//
    
    289
    -typedef enum {
    
    290
    -    SYNC_OTHER,
    
    291
    -    SYNC_GC_SEQ,
    
    292
    -    SYNC_GC_PAR,
    
    293
    -    SYNC_FLUSH_UPD_REM_SET,
    
    294
    -    SYNC_FLUSH_EVENT_LOG
    
    295
    -} SyncType;
    
    296
    -
    
    297
    -//
    
    298
    -// Details about a global synchronisation
    
    299
    -//
    
    300
    -typedef struct {
    
    301
    -    SyncType type;              // The kind of synchronisation
    
    302
    -    bool *idle;                 // Array of size n_capabilities. idle[i] is true
    
    303
    -                                // if capability i will be idle during this GC
    
    304
    -                                // cycle. Only available when doing GC (when
    
    305
    -                                // type is SYNC_GC_*).
    
    306
    -    Task *task;                 // The Task performing the sync
    
    307
    -} PendingSync;
    
    308
    -
    
    309
    -//
    
    310
    -// Indicates that the RTS wants to synchronise all the Capabilities
    
    311
    -// for some reason.  All Capabilities should stop and return to the
    
    312
    -// scheduler.
    
    285
    +// Note this acquires the specific capability the task is already associated
    
    286
    +// with (i.e. task->cap must be set). Alternatively, use waitForSomeCapability
    
    287
    +// to acquire some appropriate choice of capability.
    
    313 288
     //
    
    314
    -extern PendingSync * volatile pending_sync;
    
    289
    +void waitForCapability (Task *task);
    
    315 290
     
    
    316
    -// Acquires a capability at a return point.  If *cap is non-NULL, then
    
    317
    -// this is taken as a preference for the Capability we wish to
    
    318
    -// acquire.
    
    291
    +// If the task is not already associated with a capability, select some
    
    292
    +// reasonable choice of capability and associate the task with the chosen
    
    293
    +// capability (i.e. the task->cap is set to the new choice). Then acquire the
    
    294
    +// chosen capability, waiting as necessary.
    
    319 295
     //
    
    320
    -// OS threads waiting in this function get priority over those waiting
    
    321
    -// in waitForCapability().
    
    296
    +// Returns the now-acquired cap that was newly associated with the task.
    
    322 297
     //
    
    323
    -// On return, *cap is non-NULL, and points to the Capability acquired.
    
    324
    -//
    
    325
    -void waitForCapability (Capability **cap/*in/out*/, Task *task);
    
    298
    +Capability *waitForSomeCapability (Task *task);
    
    326 299
     
    
    327 300
     EXTERN_INLINE void recordMutableCap (const StgClosure *p, Capability *cap,
    
    328 301
                                             uint32_t gen);
    
    ... ... @@ -391,6 +364,50 @@ INLINE_HEADER void contextSwitchCapability(Capability *cap, bool immediately);
    391 364
     void interruptAllCapabilities(void);
    
    392 365
     INLINE_HEADER void interruptCapability(Capability *cap);
    
    393 366
     
    
    367
    +#if defined(THREADED_RTS)
    
    368
    +//
    
    369
    +// Types of global synchronisation
    
    370
    +//
    
    371
    +typedef enum {
    
    372
    +    SYNC_OTHER,
    
    373
    +    SYNC_GC_SEQ,
    
    374
    +    SYNC_GC_PAR,
    
    375
    +    SYNC_FLUSH_UPD_REM_SET,
    
    376
    +    SYNC_FLUSH_EVENT_LOG
    
    377
    +} SyncType;
    
    378
    +
    
    379
    +void stopAllCapabilities (Capability **pCap, Task *task);
    
    380
    +void stopAllCapabilitiesWith (Capability **pCap, Task *task, SyncType sync_type);
    
    381
    +void acquireAllCapabilities(Capability *cap, Task *task);
    
    382
    +void releaseAllCapabilities(uint32_t n, Capability *keep_cap, Task *task);
    
    383
    +
    
    384
    +//
    
    385
    +// Details about a global synchronisation
    
    386
    +//
    
    387
    +typedef struct {
    
    388
    +    SyncType type;              // The kind of synchronisation
    
    389
    +    bool *idle;                 // Array of size n_capabilities. idle[i] is true
    
    390
    +                                // if capability i will be idle during this GC
    
    391
    +                                // cycle. Only available when doing GC (when
    
    392
    +                                // type is SYNC_GC_*).
    
    393
    +    Task *task;                 // The Task performing the sync
    
    394
    +} PendingSync;
    
    395
    +
    
    396
    +// Used by scheduler for GC. Other use cases should prefer stopAllCapabilities.
    
    397
    +bool requestSync (Capability **pcap,
    
    398
    +                  Task *task,
    
    399
    +                  PendingSync *new_sync,
    
    400
    +                  SyncType *prev_sync_type);
    
    401
    +void resetSync (void);
    
    402
    +
    
    403
    +// Is there a current pending sync? This is true after a successful requestSync
    
    404
    +// and before the sync completes.
    
    405
    +INLINE_HEADER bool anyPendingSync(void);
    
    406
    +
    
    407
    +// Private, use anyPendingSync() accessor.
    
    408
    +extern PendingSync *pending_sync;
    
    409
    +#endif
    
    410
    +
    
    394 411
     // Free all capabilities
    
    395 412
     void freeCapabilities (void);
    
    396 413
     
    
    ... ... @@ -516,6 +533,10 @@ INLINE_HEADER bool emptyInbox(Capability *cap)
    516 533
                 RELAXED_LOAD(&cap->putMVars) == NULL);
    
    517 534
     }
    
    518 535
     
    
    536
    +INLINE_HEADER bool anyPendingSync(void)
    
    537
    +{
    
    538
    +    return RELAXED_LOAD(&pending_sync);
    
    539
    +}
    
    519 540
     #endif
    
    520 541
     
    
    521 542
     #include "EndPrivate.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
    ... ... @@ -654,8 +654,7 @@ rts_lock (void)
    654 654
         }
    
    655 655
     #endif
    
    656 656
     
    
    657
    -    cap = NULL;
    
    658
    -    waitForCapability(&cap, task);
    
    657
    +    cap = waitForSomeCapability(task);
    
    659 658
     
    
    660 659
         if (task->incall->prev_stack == NULL) {
    
    661 660
           // This is a new outermost call from C into Haskell land.
    
    ... ... @@ -692,7 +691,7 @@ rts_unlock (Capability *cap)
    692 691
         // random point in the future, which causes problems for
    
    693 692
         // freeTaskManager().
    
    694 693
         ACQUIRE_LOCK(&cap->lock);
    
    695
    -    releaseCapability_(cap,false);
    
    694
    +    releaseCapability_(cap);
    
    696 695
     
    
    697 696
         // Finally, we can release the Task to the free list.
    
    698 697
         exitMyTask();
    

  • rts/Schedule.c
    ... ... @@ -112,18 +112,6 @@ Mutex sched_mutex;
    112 112
     #define FORKPROCESS_PRIMOP_SUPPORTED
    
    113 113
     #endif
    
    114 114
     
    
    115
    -/*
    
    116
    - * sync_finished_cond allows threads which do not own any capability (e.g. the
    
    117
    - * concurrent mark thread) to participate in the sync protocol. In particular,
    
    118
    - * if such a thread requests a sync while sync is already in progress it will
    
    119
    - * block on sync_finished_cond, which will be signalled when the sync is
    
    120
    - * finished (by releaseAllCapabilities).
    
    121
    - */
    
    122
    -#if defined(THREADED_RTS)
    
    123
    -static Condition sync_finished_cond;
    
    124
    -static Mutex sync_finished_mutex;
    
    125
    -#endif
    
    126
    -
    
    127 115
     
    
    128 116
     /* -----------------------------------------------------------------------------
    
    129 117
      * static function prototypes
    
    ... ... @@ -141,9 +129,6 @@ static void scheduleFindWork (Capability **pcap);
    141 129
     static void scheduleYield (Capability **pcap, Task *task);
    
    142 130
     #endif
    
    143 131
     #if defined(THREADED_RTS)
    
    144
    -static bool requestSync (Capability **pcap, Task *task,
    
    145
    -                         PendingSync *sync_type, SyncType *prev_sync_type);
    
    146
    -static void acquireAllCapabilities(Capability *cap, Task *task);
    
    147 132
     static void startWorkerTasks (uint32_t from USED_IF_THREADS,
    
    148 133
                                   uint32_t to USED_IF_THREADS);
    
    149 134
     #endif
    
    ... ... @@ -676,7 +661,7 @@ shouldYieldCapability (Capability *cap, Task *task, bool didGcLast)
    676 661
         // n_returning_tasks. However, since this is an approximate predicate we can
    
    677 662
         // use a RELAXED ordering.
    
    678 663
     
    
    679
    -    return ((RELAXED_LOAD(&pending_sync) && !didGcLast) ||
    
    664
    +    return ((anyPendingSync() && !didGcLast) ||
    
    680 665
                 RELAXED_LOAD(&cap->n_returning_tasks) != 0 ||
    
    681 666
                 (!emptyRunQueue(cap) && (task->incall->tso == NULL
    
    682 667
                                          ? peekRunQueue(cap)->bound != NULL
    
    ... ... @@ -875,13 +860,9 @@ schedulePushWork(Capability *cap USED_IF_THREADS,
    875 860
             // release the capabilities
    
    876 861
             for (i = 0; i < n_free_caps; i++) {
    
    877 862
                 task->cap = free_caps[i];
    
    878
    -            if (sparkPoolSizeCap(cap) > 0) {
    
    879
    -                // If we have sparks to steal, wake up a worker on the
    
    880
    -                // capability, even if it has no threads to run.
    
    881
    -                releaseAndWakeupCapability(free_caps[i]);
    
    882
    -            } else {
    
    883
    -                releaseCapability(free_caps[i]);
    
    884
    -            }
    
    863
    +            // If there are sparks available, this will wake up a Task to run
    
    864
    +            // the Capability, even if it has no threads to run.
    
    865
    +            releaseCapability(free_caps[i]);
    
    885 866
             }
    
    886 867
         }
    
    887 868
         task->cap = cap; // reset to point to our Capability.
    
    ... ... @@ -1412,194 +1393,6 @@ scheduleNeedHeapProfile( bool ready_to_gc )
    1412 1393
         }
    
    1413 1394
     }
    
    1414 1395
     
    
    1415
    -/* -----------------------------------------------------------------------------
    
    1416
    - * stopAllCapabilities()
    
    1417
    - *
    
    1418
    - * Stop all Haskell execution.  This is used when we need to make some global
    
    1419
    - * change to the system, such as altering the number of capabilities, or
    
    1420
    - * forking.
    
    1421
    - *
    
    1422
    - * pCap may be NULL in the event that the caller doesn't yet own a capability.
    
    1423
    - *
    
    1424
    - * To resume after stopAllCapabilities(), use releaseAllCapabilities().
    
    1425
    - * -------------------------------------------------------------------------- */
    
    1426
    -
    
    1427
    -#if defined(THREADED_RTS)
    
    1428
    -void stopAllCapabilities
    
    1429
    -    ( Capability **pCap     // [in/out] This thread's task's owned capability.
    
    1430
    -                            //          pCap may be NULL if no capability is owned.
    
    1431
    -                            //          Else *pCap != NULL
    
    1432
    -                            // On return, set to the task's newly owned
    
    1433
    -                            // capability (task->cap). Though, the Task will
    
    1434
    -                            // technically own all capabilities.
    
    1435
    -    , Task *task            // [in] This thread's task.
    
    1436
    -    )
    
    1437
    -{
    
    1438
    -    stopAllCapabilitiesWith(pCap, task, SYNC_OTHER);
    
    1439
    -}
    
    1440
    -
    
    1441
    -void stopAllCapabilitiesWith (Capability **pCap, Task *task, SyncType sync_type)
    
    1442
    -{
    
    1443
    -    bool was_syncing;
    
    1444
    -    SyncType prev_sync_type;
    
    1445
    -
    
    1446
    -    PendingSync sync = {
    
    1447
    -        .type = sync_type,
    
    1448
    -        .idle = NULL,
    
    1449
    -        .task = task
    
    1450
    -    };
    
    1451
    -
    
    1452
    -    do {
    
    1453
    -        was_syncing = requestSync(pCap, task, &sync, &prev_sync_type);
    
    1454
    -    } while (was_syncing);
    
    1455
    -
    
    1456
    -    acquireAllCapabilities(pCap ? *pCap : NULL, task);
    
    1457
    -
    
    1458
    -    RELAXED_STORE(&pending_sync, 0);
    
    1459
    -    signalCondition(&sync_finished_cond);
    
    1460
    -}
    
    1461
    -#endif
    
    1462
    -
    
    1463
    -/* -----------------------------------------------------------------------------
    
    1464
    - * requestSync()
    
    1465
    - *
    
    1466
    - * Commence a synchronisation between all capabilities.  Normally not called
    
    1467
    - * directly, instead use stopAllCapabilities().  This is used by the GC, which
    
    1468
    - * has some special synchronisation requirements.
    
    1469
    - *
    
    1470
    - * Note that this can be called in two ways:
    
    1471
    - *
    
    1472
    - * - where *pcap points to a capability owned by the caller: in this case
    
    1473
    - *   *prev_sync_type will reflect the in-progress sync type on return, if one
    
    1474
    - *   *was found
    
    1475
    - *
    
    1476
    - *  - where pcap == NULL: in this case the caller doesn't hold a capability.
    
    1477
    - *    we only return whether or not a pending sync was found and prev_sync_type
    
    1478
    - *    is unchanged.
    
    1479
    - *
    
    1480
    - * Returns:
    
    1481
    - *    false if we successfully got a sync
    
    1482
    - *    true  if there was another sync request in progress,
    
    1483
    - *             and we yielded to it.  The value returned is the
    
    1484
    - *             type of the other sync request.
    
    1485
    - * -------------------------------------------------------------------------- */
    
    1486
    -
    
    1487
    -#if defined(THREADED_RTS)
    
    1488
    -static bool requestSync
    
    1489
    -    ( Capability **pcap         // [in/out] This thread's task's owned capability.
    
    1490
    -                                // May change if there is an existing sync (true is returned).
    
    1491
    -                                // Precondition:
    
    1492
    -                                //      pcap may be NULL
    
    1493
    -                                //      *pcap != NULL
    
    1494
    -    , Task *task                // [in] This thread's task.
    
    1495
    -    , PendingSync *new_sync     // [in] The new requested sync.
    
    1496
    -    , SyncType *prev_sync_type  // [out] Only set if there is an existing sync (true is returned).
    
    1497
    -    )
    
    1498
    -{
    
    1499
    -    PendingSync *sync;
    
    1500
    -
    
    1501
    -    sync = (PendingSync*)cas((StgVolatilePtr)&pending_sync,
    
    1502
    -                             (StgWord)NULL,
    
    1503
    -                             (StgWord)new_sync);
    
    1504
    -
    
    1505
    -    if (sync != NULL)
    
    1506
    -    {
    
    1507
    -        // sync is valid until we have called yieldCapability().
    
    1508
    -        // After the sync is completed, we cannot read that struct any
    
    1509
    -        // more because it has been freed.
    
    1510
    -        *prev_sync_type = sync->type;
    
    1511
    -        if (pcap == NULL) {
    
    1512
    -            // The caller does not hold a capability (e.g. may be a concurrent
    
    1513
    -            // mark thread). Consequently we must wait until the pending sync is
    
    1514
    -            // finished before proceeding to ensure we don't loop.
    
    1515
    -            // TODO: Don't busy-wait
    
    1516
    -            ACQUIRE_LOCK(&sync_finished_mutex);
    
    1517
    -            while (pending_sync) {
    
    1518
    -                waitCondition(&sync_finished_cond, &sync_finished_mutex);
    
    1519
    -            }
    
    1520
    -            RELEASE_LOCK(&sync_finished_mutex);
    
    1521
    -        } else {
    
    1522
    -            do {
    
    1523
    -                debugTrace(DEBUG_sched, "someone else is trying to sync (%d)...",
    
    1524
    -                          sync->type);
    
    1525
    -                ASSERT(*pcap);
    
    1526
    -                yieldCapability(pcap,task,true);
    
    1527
    -                sync = SEQ_CST_LOAD(&pending_sync);
    
    1528
    -            } while (sync != NULL);
    
    1529
    -        }
    
    1530
    -
    
    1531
    -        // NOTE: task->cap might have changed now
    
    1532
    -        return true;
    
    1533
    -    }
    
    1534
    -    else
    
    1535
    -    {
    
    1536
    -        return false;
    
    1537
    -    }
    
    1538
    -}
    
    1539
    -#endif
    
    1540
    -
    
    1541
    -/* -----------------------------------------------------------------------------
    
    1542
    - * acquireAllCapabilities()
    
    1543
    - *
    
    1544
    - * Grab all the capabilities except the one we already hold (cap may be NULL is
    
    1545
    - * the caller does not currently hold a capability). Used when synchronising
    
    1546
    - * before a single-threaded GC (SYNC_SEQ_GC), and before a fork (SYNC_OTHER).
    
    1547
    - *
    
    1548
    - * Only call this after requestSync(), otherwise a deadlock might
    
    1549
    - * ensue if another thread is trying to synchronise.
    
    1550
    - * -------------------------------------------------------------------------- */
    
    1551
    -
    
    1552
    -#if defined(THREADED_RTS)
    
    1553
    -static void acquireAllCapabilities(Capability *cap, Task *task)
    
    1554
    -{
    
    1555
    -    Capability *tmpcap;
    
    1556
    -    uint32_t i;
    
    1557
    -
    
    1558
    -    ASSERT(SEQ_CST_LOAD(&pending_sync) != NULL);
    
    1559
    -    for (i=0; i < getNumCapabilities(); i++) {
    
    1560
    -        debugTrace(DEBUG_sched, "grabbing all the capabilities (%d/%d)",
    
    1561
    -                   i, getNumCapabilities());
    
    1562
    -        tmpcap = getCapability(i);
    
    1563
    -        if (tmpcap != cap) {
    
    1564
    -            // we better hope this task doesn't get migrated to
    
    1565
    -            // another Capability while we're waiting for this one.
    
    1566
    -            // It won't, because load balancing happens while we have
    
    1567
    -            // all the Capabilities, but even so it's a slightly
    
    1568
    -            // unsavoury invariant.
    
    1569
    -            task->cap = tmpcap;
    
    1570
    -            waitForCapability(&tmpcap, task);
    
    1571
    -            if (tmpcap->no != i) {
    
    1572
    -                barf("acquireAllCapabilities: got the wrong capability");
    
    1573
    -            }
    
    1574
    -        }
    
    1575
    -    }
    
    1576
    -    task->cap = cap == NULL ? tmpcap : cap;
    
    1577
    -}
    
    1578
    -#endif
    
    1579
    -
    
    1580
    -/* -----------------------------------------------------------------------------
    
    1581
    - * releaseAllCapabilities()
    
    1582
    - *
    
    1583
    - * Assuming this thread holds all the capabilities, release them all (except for
    
    1584
    - * the one passed in as keep_cap, if non-NULL).
    
    1585
    - * -------------------------------------------------------------------------- */
    
    1586
    -
    
    1587
    -#if defined(THREADED_RTS)
    
    1588
    -void releaseAllCapabilities(uint32_t n, Capability *keep_cap, Task *task)
    
    1589
    -{
    
    1590
    -    uint32_t i;
    
    1591
    -    ASSERT( task != NULL);
    
    1592
    -    for (i = 0; i < n; i++) {
    
    1593
    -        Capability *tmpcap = getCapability(i);
    
    1594
    -        if (keep_cap != tmpcap) {
    
    1595
    -            task->cap = tmpcap;
    
    1596
    -            releaseCapability(tmpcap);
    
    1597
    -        }
    
    1598
    -    }
    
    1599
    -    task->cap = keep_cap;
    
    1600
    -}
    
    1601
    -#endif
    
    1602
    -
    
    1603 1396
     /* -----------------------------------------------------------------------------
    
    1604 1397
      * Perform a garbage collection if necessary
    
    1605 1398
      * -------------------------------------------------------------------------- */
    
    ... ... @@ -1665,7 +1458,7 @@ scheduleDoGC (Capability **pcap, Task *task USED_IF_THREADS,
    1665 1458
         // GC, we synchronise all the running threads using requestSync().
    
    1666 1459
         //
    
    1667 1460
         // Other capabilities are prevented from running yet more Haskell threads if
    
    1668
    -    // pending_sync is set. Tested inside yieldCapability() and
    
    1461
    +    // anyPendingSync() holds. Tested inside yieldCapability() and
    
    1669 1462
         // releaseCapability() in Capability.c
    
    1670 1463
     
    
    1671 1464
         PendingSync sync = {
    
    ... ... @@ -1802,7 +1595,7 @@ scheduleDoGC (Capability **pcap, Task *task USED_IF_THREADS,
    1802 1595
                         if (i != cap->no && idle_cap[i]) {
    
    1803 1596
                             Capability *tmpcap = getCapability(i);
    
    1804 1597
                             task->cap = tmpcap;
    
    1805
    -                        waitForCapability(&tmpcap, task);
    
    1598
    +                        waitForCapability(task);
    
    1806 1599
                             n_idle_caps++;
    
    1807 1600
                         }
    
    1808 1601
                     }
    
    ... ... @@ -1912,8 +1705,8 @@ delete_threads_and_gc:
    1912 1705
     #if defined(THREADED_RTS)
    
    1913 1706
         // reset pending_sync *before* GC, so that when the GC threads
    
    1914 1707
         // emerge they don't immediately re-enter the GC.
    
    1915
    -    RELAXED_STORE(&pending_sync, 0);
    
    1916
    -    signalCondition(&sync_finished_cond);
    
    1708
    +    resetSync();
    
    1709
    +
    
    1917 1710
         config.parallel = gc_type == SYNC_GC_PAR;
    
    1918 1711
         GarbageCollect(config, cap, idle_cap);
    
    1919 1712
     #else
    
    ... ... @@ -2074,8 +1867,7 @@ forkProcess(HsStablePtr *entry
    2074 1867
     
    
    2075 1868
         task = newBoundTask();
    
    2076 1869
     
    
    2077
    -    cap = NULL;
    
    2078
    -    waitForCapability(&cap, task);
    
    1870
    +    cap = waitForSomeCapability(task);
    
    2079 1871
     
    
    2080 1872
     #if defined(THREADED_RTS)
    
    2081 1873
         stopAllCapabilities(&cap, task);
    
    ... ... @@ -2132,7 +1924,7 @@ forkProcess(HsStablePtr *entry
    2132 1924
     #endif
    
    2133 1925
     
    
    2134 1926
             for (i=0; i < n_capabilities; i++) {
    
    2135
    -            releaseCapability_(getCapability(i),false);
    
    1927
    +            releaseCapability_(getCapability(i));
    
    2136 1928
                 RELEASE_LOCK(&getCapability(i)->lock);
    
    2137 1929
             }
    
    2138 1930
     
    
    ... ... @@ -2534,7 +2326,7 @@ suspendThread (StgRegTable *reg, bool interruptible)
    2534 2326
     
    
    2535 2327
       suspendTask(cap,task);
    
    2536 2328
       cap->in_haskell = false;
    
    2537
    -  releaseCapability_(cap,false);
    
    2329
    +  releaseCapability_(cap);
    
    2538 2330
     
    
    2539 2331
       RELEASE_LOCK(&cap->lock);
    
    2540 2332
     
    
    ... ... @@ -2567,10 +2359,7 @@ resumeThread (void *task_)
    2567 2359
         task->cap = cap;
    
    2568 2360
     
    
    2569 2361
         // Wait for permission to re-enter the RTS with the result.
    
    2570
    -    waitForCapability(&cap,task);
    
    2571
    -    // we might be on a different capability now... but if so, our
    
    2572
    -    // entry on the suspended_ccalls list will also have been
    
    2573
    -    // migrated.
    
    2362
    +    waitForCapability(task);
    
    2574 2363
     
    
    2575 2364
         // Remove the thread from the suspended list
    
    2576 2365
         recoverSuspendedTask(cap,task);
    
    ... ... @@ -2721,7 +2510,7 @@ void scheduleWorker (Capability *cap, Task *task)
    2721 2510
         // Capability has been shut down.
    
    2722 2511
         //
    
    2723 2512
         ACQUIRE_LOCK(&cap->lock);
    
    2724
    -    releaseCapability_(cap,false);
    
    2513
    +    releaseCapability_(cap);
    
    2725 2514
         workerTaskStop(task);
    
    2726 2515
         RELEASE_LOCK(&cap->lock);
    
    2727 2516
     }
    
    ... ... @@ -2767,8 +2556,6 @@ initScheduler(void)
    2767 2556
        * the scheduler. */
    
    2768 2557
     #if defined(THREADED_RTS)
    
    2769 2558
       initMutex(&sched_mutex);
    
    2770
    -  initMutex(&sync_finished_mutex);
    
    2771
    -  initCondition(&sync_finished_cond);
    
    2772 2559
     #endif
    
    2773 2560
     
    
    2774 2561
       ACQUIRE_LOCK(&sched_mutex);
    
    ... ... @@ -2804,8 +2591,7 @@ exitScheduler (bool wait_foreign USED_IF_THREADS)
    2804 2591
         // If we haven't killed all the threads yet, do it now.
    
    2805 2592
         if (getSchedState() < SCHED_SHUTTING_DOWN) {
    
    2806 2593
             setSchedState(SCHED_INTERRUPTING);
    
    2807
    -        Capability *cap = task->cap;
    
    2808
    -        waitForCapability(&cap,task);
    
    2594
    +        Capability *cap = waitForSomeCapability(task);
    
    2809 2595
             scheduleDoGC(&cap,task,true,false,false,true);
    
    2810 2596
             ASSERT(task->incall->tso == NULL);
    
    2811 2597
             releaseCapability(cap);
    
    ... ... @@ -2850,17 +2636,19 @@ freeScheduler( void )
    2850 2636
     static void
    
    2851 2637
     performGC_(bool force_major, bool nonconcurrent)
    
    2852 2638
     {
    
    2853
    -    Task *task;
    
    2854
    -    Capability *cap = NULL;
    
    2855
    -
    
    2856
    -    // We must grab a new Task here, because the existing Task may be
    
    2857
    -    // associated with a particular Capability, and chained onto the
    
    2858
    -    // suspended_ccalls queue.
    
    2859
    -    task = newBoundTask();
    
    2639
    +    // We must use a new InCall for our Task here, because the existing Task's
    
    2640
    +    // InCall may be chained onto the suspended_ccalls queue.
    
    2641
    +    //
    
    2642
    +    // If we could guarante there's an existing Task then we could just use
    
    2643
    +    // newInCall/endInCall. But it's not clear the calling thread necessarily
    
    2644
    +    // has an existing Task, so we play it safe and use newBoundTask/exitMyTask.
    
    2645
    +    // This does the same newInCall/endInCall when there is an initialised Task,
    
    2646
    +    // but will also do the right thing if there's no existing Task.
    
    2647
    +    Task *task = newBoundTask();
    
    2860 2648
     
    
    2861 2649
         // TODO: do we need to traceTask*() here?
    
    2862 2650
     
    
    2863
    -    waitForCapability(&cap,task);
    
    2651
    +    Capability *cap = waitForSomeCapability(task);
    
    2864 2652
         scheduleDoGC(&cap,task,force_major,false,false,nonconcurrent);
    
    2865 2653
         releaseCapability(cap);
    
    2866 2654
         exitMyTask();
    

  • rts/Schedule.h
    ... ... @@ -55,12 +55,6 @@ StgWord findAtomicallyFrameHelper (Capability *cap, StgTSO *tso);
    55 55
     /* Entry point for a new worker */
    
    56 56
     void scheduleWorker (Capability *cap, Task *task);
    
    57 57
     
    
    58
    -#if defined(THREADED_RTS)
    
    59
    -void stopAllCapabilitiesWith (Capability **pCap, Task *task, SyncType sync_type);
    
    60
    -void stopAllCapabilities (Capability **pCap, Task *task);
    
    61
    -void releaseAllCapabilities(uint32_t n, Capability *keep_cap, Task *task);
    
    62
    -#endif
    
    63
    -
    
    64 58
     /* The state of the scheduler.  This is used to control the sequence
    
    65 59
      * of events during shutdown.  See Note [shutdown] in Schedule.c.
    
    66 60
      */