Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC

Commits:

7 changed files:

Changes:

  • rts/Capability.c
    ... ... @@ -525,13 +525,20 @@ giveCapabilityToTask (Capability *cap USED_IF_DEBUG, Task *task)
    525 525
     /* ----------------------------------------------------------------------------
    
    526 526
      * releaseCapability
    
    527 527
      *
    
    528
    - * The current Task (cap->task) releases the Capability.  The Capability is
    
    529
    - * marked free, and if there is any work to do, an appropriate Task is woken up.
    
    528
    + * This serves two purposes:
    
    529
    + *
    
    530
    + * 1. The current Task (cap->running_task) releases the Capability.
    
    531
    + *    The Capability is marked free, and if there is any work to do, an
    
    532
    + *    appropriate Task is woken up.
    
    533
    + *
    
    534
    + * 2. There is no current task (cap->task == NULL), and thus the Capability
    
    535
    + *    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.
    
    530 537
      *
    
    531 538
      * The caller must hold cap->lock and will still hold it after
    
    532 539
      * releaseCapability returns.
    
    533 540
      *
    
    534
    - * N.B. May need to take all_tasks_mutex.
    
    541
    + * N.B. May need to take all_tasks_mutex, if it needs to start a new task.
    
    535 542
      *
    
    536 543
      * ------------------------------------------------------------------------- */
    
    537 544
     
    
    ... ... @@ -540,12 +547,18 @@ void
    540 547
     releaseCapability_ (Capability* cap,
    
    541 548
                         bool always_wakeup)
    
    542 549
     {
    
    543
    -    Task *task;
    
    544
    -
    
    545
    -    task = cap->running_task;
    
    546
    -
    
    547
    -    ASSERT_PARTIAL_CAPABILITY_INVARIANTS(cap,task);
    
    548
    -    ASSERT_RETURNING_TASKS(cap,task);
    
    550
    +    {
    
    551
    +        Task *task = cap->running_task;
    
    552
    +
    
    553
    +        ASSERT(task || always_wakeup);
    
    554
    +        // To cover purpose 2 above, we allow the cap->running_task to be
    
    555
    +        // NULL, to handle cases where a thread (that is not itself a Task)
    
    556
    +        // needs to wake up an idle task for the capability.
    
    557
    +        if (task) {
    
    558
    +            ASSERT_PARTIAL_CAPABILITY_INVARIANTS(cap,task);
    
    559
    +            ASSERT_RETURNING_TASKS(cap,task);
    
    560
    +        }
    
    561
    +    }
    
    549 562
         ASSERT_LOCK_HELD(&cap->lock);
    
    550 563
     
    
    551 564
         RELAXED_STORE(&cap->running_task, NULL);
    
    ... ... @@ -581,8 +594,8 @@ releaseCapability_ (Capability* cap,
    581 594
             // assertion is false: in schedule() we force a yield after
    
    582 595
             // ThreadBlocked, but the thread may be back on the run queue
    
    583 596
             // by now.
    
    584
    -        task = peekRunQueue(cap)->bound->task;
    
    585
    -        giveCapabilityToTask(cap, task);
    
    597
    +        Task *btask = peekRunQueue(cap)->bound->task;
    
    598
    +        giveCapabilityToTask(cap, btask);
    
    586 599
             return;
    
    587 600
         }
    
    588 601
     
    
    ... ... @@ -1087,16 +1100,25 @@ yieldCapability
    1087 1100
     #if defined(THREADED_RTS)
    
    1088 1101
     
    
    1089 1102
     void
    
    1090
    -prodCapability (Capability *cap, Task *task)
    
    1103
    +prodCapability (Capability *cap)
    
    1091 1104
     {
    
    1092 1105
         ACQUIRE_LOCK(&cap->lock);
    
    1093 1106
         if (!cap->running_task) {
    
    1094
    -        cap->running_task = task;
    
    1095 1107
             releaseCapability_(cap,true);
    
    1096 1108
         }
    
    1097 1109
         RELEASE_LOCK(&cap->lock);
    
    1098 1110
     }
    
    1099 1111
     
    
    1112
    +/* Ensure at least one capability is not idle. Used to wake up the RTS
    
    1113
    + * in cases where we anticipate that all capabilities may be idle. In
    
    1114
    + * particular it is used for the ctl-c handler, and after the idle GC
    
    1115
    + * timeout to initiate idle GC. */
    
    1116
    +void
    
    1117
    +prodOneCapability (void)
    
    1118
    +{
    
    1119
    +    prodCapability(getCapability(0));
    
    1120
    +}
    
    1121
    +
    
    1100 1122
     #endif /* THREADED_RTS */
    
    1101 1123
     
    
    1102 1124
     /* ----------------------------------------------------------------------------
    

  • rts/Capability.h
    ... ... @@ -348,11 +348,7 @@ bool yieldCapability (Capability** pCap, Task *task, bool gcAllowed);
    348 348
     // need to service some global event.
    
    349 349
     //
    
    350 350
     void prodOneCapability (void);
    
    351
    -void prodCapability (Capability *cap, Task *task);
    
    352
    -
    
    353
    -// Similar to prodOneCapability(), but prods all of them.
    
    354
    -//
    
    355
    -void prodAllCapabilities (void);
    
    351
    +void prodCapability (Capability *cap);
    
    356 352
     
    
    357 353
     // Attempt to gain control of a Capability if it is free.
    
    358 354
     //
    

  • rts/Messages.c
    ... ... @@ -49,9 +49,11 @@ 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
    -        to_cap->running_task = myTask();
    
    53
    -            // precond for releaseCapability_()
    
    54
    -        releaseCapability_(to_cap,false);
    
    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*/);
    
    55 57
         } else {
    
    56 58
             interruptCapability(to_cap);
    
    57 59
         }
    

  • rts/Schedule.c
    ... ... @@ -2885,8 +2885,8 @@ performBlockingMajorGC(void)
    2885 2885
     }
    
    2886 2886
     
    
    2887 2887
     /* ---------------------------------------------------------------------------
    
    2888
    -   Interrupt execution.
    
    2889
    -   Might be called inside a signal handler so it mustn't do anything fancy.
    
    2888
    +   Interrupt execution in response to ctl-c.
    
    2889
    +   On posix, ctl-c is a signal, while on Win32 it is a console event.
    
    2890 2890
        ------------------------------------------------------------------------ */
    
    2891 2891
     
    
    2892 2892
     void
    
    ... ... @@ -2896,17 +2896,37 @@ interruptStgRts(void)
    2896 2896
         setSchedState(SCHED_INTERRUPTING);
    
    2897 2897
         interruptAllCapabilities();
    
    2898 2898
     #if defined(THREADED_RTS)
    
    2899
    +    /* It may be that all capabilities are idle. If so, we must wake one up. */
    
    2900
    +#if defined(mingw32_HOST_OS)
    
    2901
    +    /* On win32, console handlers are invoked in a proper thread, so we can
    
    2902
    +     * directly call wakeUpRts. Although it is an OS thread, it is not one
    
    2903
    +     * we created or control necessarily, so it may have no associated Task.
    
    2904
    +     */
    
    2899 2905
         wakeUpRts();
    
    2906
    +#else
    
    2907
    +    /* On posix on the other hand, signal handlers are very limited in what
    
    2908
    +     * they can do. We cannot directly call wakeUpRts below because it is not
    
    2909
    +     * signal safe (it uses cond vars to wake up a task). So instead we proxy
    
    2910
    +     * it: we interrupt the ticker thread and ask the ticker thread to call
    
    2911
    +     * wakeUpRts below. The ticker thread is a proper thread and so can call
    
    2912
    +     * wakeUpRts. We can interrupt the ticker thread from signal handler
    
    2913
    +     * context safely because it only involves writing to a pipe/eventfd.
    
    2914
    +     */
    
    2915
    +    wakeUpRtsViaTicker();
    
    2916
    +#endif
    
    2900 2917
     #endif
    
    2901 2918
     }
    
    2902 2919
     
    
    2903 2920
     /* -----------------------------------------------------------------------------
    
    2904 2921
        Wake up the RTS
    
    2905 2922
     
    
    2906
    -   This function causes at least one OS thread to wake up and run the
    
    2907
    -   scheduler loop.  It is invoked when the RTS might be deadlocked, or
    
    2908
    -   an external event has arrived that may need servicing (eg. a
    
    2909
    -   keyboard interrupt).
    
    2923
    +   This function causes at least one task to wake up and run the scheduler
    
    2924
    +   loop on at least one capability.
    
    2925
    +
    
    2926
    +   It is invoked:
    
    2927
    +   1. as part of the idle GC scheme: when the RTS has been idle for long enough
    
    2928
    +      and it is time to go back to the scheduler which will invoke idle GC; or
    
    2929
    +   2. when a ctl-c occurs (posix sigint signal or win32 console event)
    
    2910 2930
     
    
    2911 2931
        In the single-threaded RTS we don't do anything here; we only have
    
    2912 2932
        one thread anyway, and the event that caused us to want to wake up
    
    ... ... @@ -2916,10 +2936,11 @@ interruptStgRts(void)
    2916 2936
     #if defined(THREADED_RTS)
    
    2917 2937
     void wakeUpRts(void)
    
    2918 2938
     {
    
    2919
    -    // This forces the IO Manager thread to wakeup, which will
    
    2920
    -    // in turn ensure that some OS thread wakes up and runs the
    
    2921
    -    // scheduler loop, which will cause a GC and deadlock check.
    
    2922
    -    wakeupIOManager();
    
    2939
    +    /* Our current thread may not have a Task, in particular it will not when
    
    2940
    +     * called from interruptStgRts or via wakeUpRtsViaTicker. This is ok,
    
    2941
    +     * prodOneCapability does not require one.
    
    2942
    +     */
    
    2943
    +    prodOneCapability();
    
    2923 2944
     }
    
    2924 2945
     #endif
    
    2925 2946
     
    

  • rts/Ticker.h
    ... ... @@ -52,4 +52,8 @@ void exitTicker(void);
    52 52
     void pauseTicker(void);
    
    53 53
     void unpauseTicker(void);
    
    54 54
     
    
    55
    +#if defined(THREADED_RTS)
    
    56
    +void wakeUpRtsViaTicker(void);
    
    57
    +#endif
    
    58
    +
    
    55 59
     #include "EndPrivate.h"

  • rts/posix/Ticker.c
    ... ... @@ -127,6 +127,13 @@ static Time ticker_interval = DEFAULT_TICK_INTERVAL;
    127 127
     // acknowledgement.
    
    128 128
     static bool pause_request;
    
    129 129
     
    
    130
    +#if defined(THREADED_RTS)
    
    131
    +// Atomic variable used by the ctl-c handler (posix signal handler) to
    
    132
    +// communicate that the ticker thread should wake up the rts. This
    
    133
    +// communication is one-way, with no acknowledgement.
    
    134
    +static bool interrupt_request;
    
    135
    +#endif
    
    136
    +
    
    130 137
     // Atomic variable used by other threads to communicate that they want the
    
    131 138
     // ticker thread to exit.
    
    132 139
     static bool exit_request;
    
    ... ... @@ -177,6 +184,13 @@ static void *ticker_thread_func(void *_handle_tick)
    177 184
     
    
    178 185
                 paused = ACQUIRE_LOAD_ALWAYS(&pause_request);
    
    179 186
                 exit   = RELAXED_LOAD_ALWAYS(&exit_request);
    
    187
    +
    
    188
    +#if defined(THREADED_RTS)
    
    189
    +            if (RELAXED_LOAD_ALWAYS(&interrupt_request)) {
    
    190
    +                RELEASE_STORE_ALWAYS(&interrupt_request, false);
    
    191
    +                wakeUpRts();
    
    192
    +            }
    
    193
    +#endif
    
    180 194
             } else if (errno != EINTR) {
    
    181 195
                 // While the RTS attempts to mask signals, some foreign libraries
    
    182 196
                 // that rely on signal delivery may unmask them. Consequently we
    
    ... ... @@ -262,6 +276,14 @@ void pauseTicker(void)
    262 276
         sendFdWakeup(notifyfd_w);
    
    263 277
     }
    
    264 278
     
    
    279
    +#if defined(THREADED_RTS)
    
    280
    +void wakeUpRtsViaTicker(void)
    
    281
    +{
    
    282
    +    RELAXED_STORE_ALWAYS(&interrupt_request, true);
    
    283
    +    sendFdWakeup(notifyfd_w);
    
    284
    +}
    
    285
    +#endif
    
    286
    +
    
    265 287
     /* Synchronous. Not idempotent.
    
    266 288
      * The ticker is guaranteed stopped after this.
    
    267 289
      */
    

  • rts/sm/GC.c
    ... ... @@ -1522,7 +1522,7 @@ waitForGcThreads (Capability *cap, bool idle_cap[])
    1522 1522
             for(i = 0; i < getNumCapabilities(); ++i) {
    
    1523 1523
                 if (i == me || idle_cap[i]) { continue; }
    
    1524 1524
                 if (SEQ_CST_LOAD(&gc_threads[i]->wakeup) != GC_THREAD_STANDING_BY) {
    
    1525
    -                prodCapability(getCapability(i), cap->running_task);
    
    1525
    +                prodCapability(getCapability(i));
    
    1526 1526
                     interruptCapability(getCapability(i));
    
    1527 1527
                 }
    
    1528 1528
             }