Duncan Coutts pushed to branch wip/io-manager-deadlock-detection at Glasgow Haskell Compiler / GHC

Commits:

29 changed files:

Changes:

  • docs/users_guide/runtime_control.rst
    ... ... @@ -739,18 +739,18 @@ performance.
    739 739
     
    
    740 740
     .. rts-flag:: -I ⟨seconds⟩
    
    741 741
     
    
    742
    -    :default: 0.3 seconds in the threaded runtime, 0 in the non-threaded runtime
    
    742
    +    :default: 0.3 seconds
    
    743 743
     
    
    744 744
         .. index::
    
    745 745
            single: idle GC
    
    746 746
     
    
    747
    -    Set the amount of idle time which must pass before a idle GC is
    
    748
    -    performed. Setting ``-I0`` disables the idle GC.
    
    747
    +    A major GC is automatically performed if the runtime has been idle (no
    
    748
    +    Haskell computation has been running) for a period of time. Set the amount
    
    749
    +    of idle time which must pass before a idle GC is performed.
    
    749 750
     
    
    750
    -    In the threaded and SMP versions of the RTS (see :ghc-flag:`-threaded`,
    
    751
    -    :ref:`options-linker`), a major GC is automatically performed if the
    
    752
    -    runtime has been idle (no Haskell computation has been running) for a
    
    753
    -    period of time.
    
    751
    +    Setting ``-I0`` disables the idle GC. This also has the unfortunate side
    
    752
    +    effect of disabling thread deadlock detection (the implementation of which
    
    753
    +    uses the idle GC).
    
    754 754
     
    
    755 755
         For an interactive application, it is probably a good idea to use
    
    756 756
         the idle GC, because this will allow finalizers to run and
    
    ... ... @@ -767,8 +767,8 @@ performance.
    767 767
         after the first idle collection is triggered then no more future collections
    
    768 768
         will be scheduled until more work is performed.
    
    769 769
     
    
    770
    -    This is an experimental feature, please let us know if it causes
    
    771
    -    problems and/or could benefit from further tuning.
    
    770
    +    Please let us know if it causes problems and/or could benefit from further
    
    771
    +    tuning.
    
    772 772
     
    
    773 773
     .. rts-flag:: -Iw ⟨seconds⟩
    
    774 774
     
    
    ... ... @@ -779,7 +779,7 @@ performance.
    779 779
     
    
    780 780
         Set the minimum wait time between runs of the idle GC.
    
    781 781
     
    
    782
    -    By default, if idle GC is enabled in the threaded runtime, a major
    
    782
    +    By default (and if idle GC is not disabled) a major
    
    783 783
         GC will be performed every time the process goes idle for a
    
    784 784
         sufficiently long duration (see :rts-flag:`-I ⟨seconds⟩`).  For
    
    785 785
         large server processes accepting regular but infrequent requests
    

  • rts/Capability.c
    ... ... @@ -1280,6 +1280,7 @@ shutdownCapabilities(Task *task, bool safe)
    1280 1280
     static void
    
    1281 1281
     freeCapability (Capability *cap)
    
    1282 1282
     {
    
    1283
    +    freeCapabilityIOManager(cap->iomgr);
    
    1283 1284
         stgFree(cap->mut_lists);
    
    1284 1285
         stgFree(cap->saved_mut_lists);
    
    1285 1286
         if (cap->current_segments) {
    

  • rts/IOManager.c
    ... ... @@ -39,7 +39,7 @@
    39 39
     #endif
    
    40 40
     
    
    41 41
     #if defined(IOMGR_ENABLED_MIO_POSIX)
    
    42
    -#include "posix/Signals.h"
    
    42
    +#include "posix/MIO.h"
    
    43 43
     #include "Prelude.h"
    
    44 44
     #endif
    
    45 45
     
    
    ... ... @@ -343,9 +343,7 @@ void initCapabilityIOManager(CapIOManager *iomgr)
    343 343
         switch (iomgr_type) {
    
    344 344
     #if defined(IOMGR_ENABLED_SELECT)
    
    345 345
             case IO_MANAGER_SELECT:
    
    346
    -            iomgr->blocked_queue_hd = END_TSO_QUEUE;
    
    347
    -            iomgr->blocked_queue_tl = END_TSO_QUEUE;
    
    348
    -            iomgr->sleeping_queue   = END_TSO_QUEUE;
    
    346
    +            initCapabilityIOManagerSelect(iomgr);
    
    349 347
                 break;
    
    350 348
     #endif
    
    351 349
     
    
    ... ... @@ -373,11 +371,31 @@ void initCapabilityIOManager(CapIOManager *iomgr)
    373 371
     }
    
    374 372
     
    
    375 373
     
    
    374
    +void freeCapabilityIOManager(CapIOManager *iomgr)
    
    375
    +{
    
    376
    +    switch (iomgr_type) {
    
    377
    +#if defined(IOMGR_ENABLED_SELECT)
    
    378
    +        case IO_MANAGER_SELECT:
    
    379
    +            freeCapabilityIOManagerSelect(iomgr);
    
    380
    +            break;
    
    381
    +#endif
    
    382
    +
    
    383
    +#if defined(IOMGR_ENABLED_POLL)
    
    384
    +        case IO_MANAGER_POLL:
    
    385
    +            freeCapabilityIOManagerPoll(iomgr);
    
    386
    +            break;
    
    387
    +#endif
    
    388
    +        default:
    
    389
    +            break;
    
    390
    +    }
    
    391
    +}
    
    392
    +
    
    393
    +
    
    376 394
     /* Called late in the RTS initialisation
    
    377 395
      */
    
    378
    -void initIOManager(void)
    
    396
    +void startIOManager(void)
    
    379 397
     {
    
    380
    -    debugTrace(DEBUG_iomanager, "initialising %s I/O manager", showIOManager());
    
    398
    +    debugTrace(DEBUG_iomanager, "starting %s I/O manager", showIOManager());
    
    381 399
     
    
    382 400
         switch (iomgr_type) {
    
    383 401
     
    
    ... ... @@ -441,7 +459,7 @@ void initIOManager(void)
    441 459
     /* Called from forkProcess in the child process on the surviving capability.
    
    442 460
      */
    
    443 461
     void
    
    444
    -initIOManagerAfterFork(CapIOManager *iomgr, Capability **pcap)
    
    462
    +restartIOManager(CapIOManager *iomgr, Capability **pcap)
    
    445 463
     {
    
    446 464
     
    
    447 465
         switch (iomgr_type) {
    
    ... ... @@ -541,13 +559,25 @@ exitIOManager(bool wait_threads)
    541 559
         }
    
    542 560
     }
    
    543 561
     
    
    544
    -/* Wakeup hook: called from the scheduler's wakeUpRts (currently only in
    
    545
    - * threaded mode).
    
    562
    +/* Wakeup hook: called from the scheduler's wakeUpRts
    
    546 563
      */
    
    547 564
     void wakeupIOManager(void)
    
    548 565
     {
    
    566
    +    debugTrace(DEBUG_iomanager, "Sending wakeup to I/O manager...");
    
    549 567
         switch (iomgr_type) {
    
    550 568
     
    
    569
    +#if defined(IOMGR_ENABLED_SELECT)
    
    570
    +        case IO_MANAGER_SELECT:
    
    571
    +            wakeupIOManagerSelect(MainCapability.iomgr);
    
    572
    +            break;
    
    573
    +#endif
    
    574
    +
    
    575
    +#if defined(IOMGR_ENABLED_POLL)
    
    576
    +        case IO_MANAGER_POLL:
    
    577
    +            wakeupIOManagerPoll(MainCapability.iomgr);
    
    578
    +            break;
    
    579
    +#endif
    
    580
    +
    
    551 581
     #if defined(IOMGR_ENABLED_MIO_POSIX)
    
    552 582
             case IO_MANAGER_MIO_POSIX:
    
    553 583
                 /* MIO Posix implementation in posix/Signals.c */
    
    ... ... @@ -572,8 +602,13 @@ void wakeupIOManager(void)
    572 602
     #endif
    
    573 603
                 break;
    
    574 604
     #endif
    
    575
    -        default:
    
    605
    +#if defined(IOMGR_ENABLED_WIN32_LEGACY)
    
    606
    +        case IO_MANAGER_WIN32_LEGACY:
    
    607
    +            abandonRequestWait();
    
    576 608
                 break;
    
    609
    +#endif
    
    610
    +        default:
    
    611
    +            barf("wakeupIOManager not implemented");
    
    577 612
         }
    
    578 613
     }
    
    579 614
     
    
    ... ... @@ -661,64 +696,6 @@ setIOManagerControlFd(uint32_t cap_no, int fd) {
    661 696
     #endif
    
    662 697
     
    
    663 698
     
    
    664
    -bool anyPendingTimeoutsOrIO(CapIOManager *iomgr)
    
    665
    -{
    
    666
    -    switch (iomgr_type) {
    
    667
    -#if defined(IOMGR_ENABLED_SELECT)
    
    668
    -        case IO_MANAGER_SELECT:
    
    669
    -            return (iomgr->blocked_queue_hd != END_TSO_QUEUE)
    
    670
    -                || (iomgr->sleeping_queue   != END_TSO_QUEUE);
    
    671
    -#endif
    
    672
    -
    
    673
    -#if defined(IOMGR_ENABLED_POLL)
    
    674
    -        case IO_MANAGER_POLL:
    
    675
    -            return anyPendingTimeoutsOrIOPoll(iomgr);
    
    676
    -#endif
    
    677
    -
    
    678
    -#if defined(IOMGR_ENABLED_WIN32_LEGACY)
    
    679
    -        case IO_MANAGER_WIN32_LEGACY:
    
    680
    -            return (iomgr->blocked_queue_hd != END_TSO_QUEUE);
    
    681
    -#endif
    
    682
    -
    
    683
    -    /* For the purpose of the scheduler, the threaded I/O managers never have
    
    684
    -       pending I/O or timers. Of course in reality they do, but they're
    
    685
    -       managed via other primitives that the scheduler can see into (threads,
    
    686
    -       MVars and foreign blocking calls).
    
    687
    -     */
    
    688
    -#if defined(IOMGR_ENABLED_MIO_POSIX)
    
    689
    -        case IO_MANAGER_MIO_POSIX:
    
    690
    -          return false;
    
    691
    -#endif
    
    692
    -
    
    693
    -#if defined(IOMGR_ENABLED_MIO_WIN32)
    
    694
    -        case IO_MANAGER_MIO_WIN32:
    
    695
    -          return false;
    
    696
    -#endif
    
    697
    -
    
    698
    -#if defined(IOMGR_ENABLED_WINIO)
    
    699
    -#if defined(THREADED_RTS)
    
    700
    -        /* As above, the threaded variants never have pending I/O or timers */
    
    701
    -        case IO_MANAGER_WINIO:
    
    702
    -          return false;
    
    703
    -#else
    
    704
    -        case IO_MANAGER_WINIO:
    
    705
    -          return false;
    
    706
    -        /* FIXME: But what is this? The WinIO I/O manager *also* returns false
    
    707
    -           in the non-threaded case! This is *totally bogus*! In the
    
    708
    -           non-threaded RTS the scheduler expects to be able to poll for IO.
    
    709
    -           The fact that this gives a wrong and useless answer for WinIO is
    
    710
    -           probably the cause of the complication in the scheduler with having
    
    711
    -           to call awaitCompletedTimeoutsOrIO() in multiple places (on Windows,
    
    712
    -           non-threaded).
    
    713
    -         */
    
    714
    -#endif
    
    715
    -#endif
    
    716
    -        default:
    
    717
    -            barf("anyPendingTimeoutsOrIO not implemented");
    
    718
    -    }
    
    719
    -}
    
    720
    -
    
    721
    -
    
    722 699
     void pollCompletedTimeoutsOrIO(CapIOManager *iomgr)
    
    723 700
     {
    
    724 701
         debugTrace(DEBUG_iomanager, "polling for completed IO or timeouts");
    
    ... ... @@ -782,7 +759,9 @@ void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr)
    782 759
             default:
    
    783 760
                 barf("pollCompletedTimeoutsOrIO not implemented");
    
    784 761
         }
    
    785
    -    ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING);
    
    762
    +    // FIXME: the post condition is now more complicated. Await can now simply
    
    763
    +    // be interrupted by wakeupIOManager.
    
    764
    +    // ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING);
    
    786 765
     }
    
    787 766
     
    
    788 767
     
    

  • rts/IOManager.h
    ... ... @@ -15,6 +15,11 @@
    15 15
      * subsystem implementations are centralised here. Not all implementations use
    
    16 16
      * all hooks.
    
    17 17
      *
    
    18
    + * I/O manager are responsible for:
    
    19
    + * - threads waiting on I/O
    
    20
    + * - threads waiting on timeouts
    
    21
    + * - signals (unix, and win32 console) starting handlers
    
    22
    + *
    
    18 23
      * -------------------------------------------------------------------------*/
    
    19 24
     
    
    20 25
     #pragma once
    
    ... ... @@ -242,11 +247,33 @@ CapIOManager *allocCapabilityIOManager(Capability *cap);
    242 247
      */
    
    243 248
     void initCapabilityIOManager(CapIOManager *iomgr);
    
    244 249
     
    
    250
    +/* When shutting down a capability, or after forkProcess, free the resources
    
    251
    + * held by a CapIOManager to put it back into a state in which either it can be
    
    252
    + * re-initialised using initCapabilityIOManager, or the whole structure freed.
    
    253
    + *
    
    254
    + * Note that this does not free the CapIOManager structure itself, just the
    
    255
    + * contents.
    
    256
    + *
    
    257
    + * This is used during capability shutdown, during RTS shutdown. It is not used
    
    258
    + * when reducing the number of capabilities. Capabilities are disabled rather
    
    259
    + * than freed entirely: the I/O manager keeps running but threads that become
    
    260
    + * runnable are migrated away.
    
    261
    + *
    
    262
    + * It is also used after forkProcess.
    
    263
    + */
    
    264
    +void freeCapabilityIOManager(CapIOManager *iomgr);
    
    265
    +
    
    266
    +/* CapIOManager life cycle:
    
    267
    + *
    
    268
    + * alloc -> init -> free -> free struct
    
    269
    + *           ^        |
    
    270
    + *           +--------+
    
    271
    + */
    
    245 272
     
    
    246 273
     /* Init hook: called from hs_init_ghc, very late in the startup after almost
    
    247 274
      * everything else is done.
    
    248 275
      */
    
    249
    -void initIOManager(void);
    
    276
    +void startIOManager(void);
    
    250 277
     
    
    251 278
     
    
    252 279
     /* Init hook: called from forkProcess in the child process on the surviving
    
    ... ... @@ -255,8 +282,8 @@ void initIOManager(void);
    255 282
      * This is synchronous and can run Haskell code, so can change the given cap.
    
    256 283
      * TODO: it would make for a cleaner API here if this were made asynchronous.
    
    257 284
      */
    
    258
    -void initIOManagerAfterFork(CapIOManager *iomgr,
    
    259
    -                /* inout */ Capability  **pcap);
    
    285
    +void restartIOManager(CapIOManager *iomgr,
    
    286
    +          /* inout */ Capability  **pcap);
    
    260 287
     
    
    261 288
     /* TODO: rationalise initIOManager and initIOManagerAfterFork into a single
    
    262 289
              per-capability init function.
    
    ... ... @@ -283,19 +310,13 @@ void stopIOManager(void);
    283 310
     void exitIOManager(bool wait_threads);
    
    284 311
     
    
    285 312
     
    
    286
    -/* Wakeup hook: called from the scheduler's wakeUpRts (currently only in
    
    287
    - * threaded mode).
    
    313
    +/* Wakeup hook: called from the scheduler's wakeUpRts().
    
    288 314
      *
    
    289 315
      * The I/O manager can be blocked waiting on I/O or timers. Sometimes there are
    
    290 316
      * other external events where we need to wake up the I/O manager and return
    
    291
    - * to the schedulr.
    
    292
    - *
    
    293
    - * At the moment, all the non-threaded I/O managers will do this automagically
    
    294
    - * since a signal will interrupt any waiting system calls, so at the moment
    
    295
    - * the implementation for the non-threaded I/O managers does nothing.
    
    317
    + * to the scheduler.
    
    296 318
      *
    
    297
    - * For the I/O managers in threaded mode, this arranges to unblock the I/O
    
    298
    - * manager if it waa blocked waiting.
    
    319
    + * This arranges to unblock the I/O manager if it was blocked waiting.
    
    299 320
      */
    
    300 321
     void wakeupIOManager(void);
    
    301 322
     
    
    ... ... @@ -343,35 +364,27 @@ void syncDelayCancel(CapIOManager *iomgr, StgTSO *tso);
    343 364
     void appendToIOBlockedQueue(CapIOManager *iomgr, StgTSO *tso);
    
    344 365
     #endif
    
    345 366
     
    
    346
    -/* Check to see if there are any pending timeouts or I/O operations
    
    347
    - * in progress with the I/O manager.
    
    367
    +/* Poll for any completed I/O operations, expired timers or pending signals
    
    368
    + * with handlers. If there are any, process the completions as appropriate
    
    369
    + * (which will typically unblock some waiting threads).
    
    348 370
      *
    
    349
    - * This is used by the scheduler as part of deadlock-detection, and the
    
    350
    - * "context switch as often as possible" test.
    
    351
    - */
    
    352
    -bool anyPendingTimeoutsOrIO(CapIOManager *iomgr);
    
    353
    -
    
    354
    -/* If there are any completed I/O operations or expired timers, process the
    
    355
    - * completions as appropriate (which will typically unblock some waiting
    
    356
    - * threads, but no guarantee). If there are none, return without waiting.
    
    371
    + * This polls, but does not block.
    
    372
    + *
    
    373
    + * No post-condition. It does not guarantee anything such as there being
    
    374
    + * runnable threads, since this does not wait.
    
    357 375
      *
    
    358
    - * Called from schedule() both *before* and *after* scheduleDetectDeadlock().
    
    376
    + * Called from schedule() before scheduleDetectDeadlock().
    
    359 377
      */
    
    360 378
     void pollCompletedTimeoutsOrIO(CapIOManager *iomgr);
    
    361 379
     
    
    362
    - /* If there are any completed I/O operations or expired timers, process the
    
    363
    - * completions as appropriate. If there are none, wait until I/O or a timer
    
    364
    - * does complete (or we get a signal with a handler) and process the
    
    365
    - * completions as appropriate.
    
    380
    +/* Wait for completed I/O operations, expired timers or signals and process
    
    381
    + * the completions as appropriate.
    
    366 382
      *
    
    367 383
      * Upon return this guarantees that the scheduler run queue is non-empty or
    
    368 384
      * that the scheduler is no longer in the running state. Succinctly, the
    
    369 385
      * post-condition is (!emptyRunQueue(cap) || getSchedState() != SCHED_RUNNING).
    
    370 386
      *
    
    371
    - * This is only expected to be called if anyPendingTimeoutsOrIO() returns true,
    
    372
    - * i.e. there actually is something to wait for.
    
    373
    - *
    
    374
    - * Called from schedule() both *before* and *after* scheduleDetectDeadlock().
    
    387
    + * Called from schedule() after scheduleDetectDeadlock().
    
    375 388
      */
    
    376 389
     void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr);
    
    377 390
     
    

  • rts/IOManagerInternals.h
    ... ... @@ -46,6 +46,11 @@ struct _CapIOManager {
    46 46
         StgTSO *sleeping_queue;
    
    47 47
     #endif
    
    48 48
     
    
    49
    +#if defined(IOMGR_ENABLED_SELECT) || defined(IOMGR_ENABLED_POLL)
    
    50
    +    /* FDs for waking up the I/O manager when it is blocked waiting */
    
    51
    +    int wakeup_fd_r, wakeup_fd_w;
    
    52
    +#endif
    
    53
    +
    
    49 54
     #if defined(IOMGR_ENABLED_POLL)
    
    50 55
         /* AIOP and timeout collections shared by several I/O manager impls */
    
    51 56
         ClosureTable     aiop_table;
    
    ... ... @@ -53,8 +58,11 @@ struct _CapIOManager {
    53 58
     #endif
    
    54 59
     
    
    55 60
     #if defined(IOMGR_ENABLED_POLL)
    
    56
    -    /* Auxiliary table with size and indexes matching the aiop_table */
    
    57
    -    struct pollfd *aiop_poll_table;
    
    61
    +    /* Auxiliary table with size and indexes matching the aiop_table. This is
    
    62
    +     * aliased to the tail of the full poll table, which has a head entry for
    
    63
    +     * the wakeup_fd_r above, so we can also poll that fd.
    
    64
    +     */
    
    65
    +    struct pollfd *aiop_poll_table, *full_poll_table;
    
    58 66
     #endif
    
    59 67
     
    
    60 68
     #if defined(IOMGR_ENABLED_WIN32_LEGACY)
    

  • rts/RtsFlags.c
    ... ... @@ -173,11 +173,7 @@ void initRtsFlagsDefaults(void)
    173 173
         RtsFlags.GcFlags.sweep              = false;
    
    174 174
         RtsFlags.GcFlags.idleGCDelayTime    = USToTime(300000); // 300ms
    
    175 175
         RtsFlags.GcFlags.interIdleGCWait    = 0;
    
    176
    -#if defined(THREADED_RTS)
    
    177 176
         RtsFlags.GcFlags.doIdleGC           = true;
    
    178
    -#else
    
    179
    -    RtsFlags.GcFlags.doIdleGC           = false;
    
    180
    -#endif
    
    181 177
         RtsFlags.GcFlags.heapBase           = 0;   /* means don't care */
    
    182 178
         RtsFlags.GcFlags.allocLimitGrace    = (100*1024) / BLOCK_SIZE;
    
    183 179
         RtsFlags.GcFlags.numa               = false;
    

  • rts/RtsSignals.h
    ... ... @@ -2,26 +2,15 @@
    2 2
      *
    
    3 3
      * (c) The GHC Team, 1998-2005
    
    4 4
      *
    
    5
    - * Signal processing / handling.
    
    5
    + * Signal processing / handling. This is the shared API to the subsystems for
    
    6
    + * POSIX signals and Win32 console events.
    
    7
    + *
    
    8
    + * Platform specific APIs live in posix/Signals.h and win32/ConsoleHandler.h
    
    6 9
      *
    
    7 10
      * ---------------------------------------------------------------------------*/
    
    8 11
     
    
    9 12
     #pragma once
    
    10 13
     
    
    11
    -#if !defined(mingw32_HOST_OS) && defined(HAVE_SIGNAL_H)
    
    12
    -
    
    13
    -#include "posix/Signals.h"
    
    14
    -
    
    15
    -#elif defined(mingw32_HOST_OS)
    
    16
    -
    
    17
    -#include "win32/ConsoleHandler.h"
    
    18
    -
    
    19
    -#else
    
    20
    -
    
    21
    -#define signals_pending() (false)
    
    22
    -
    
    23
    -#endif
    
    24
    -
    
    25 14
     #if defined(RTS_USER_SIGNALS)
    
    26 15
     
    
    27 16
     #include "BeginPrivate.h"
    
    ... ... @@ -44,39 +33,20 @@ void resetDefaultHandlers(void);
    44 33
     
    
    45 34
     void freeSignalHandlers(void);
    
    46 35
     
    
    47
    -/*
    
    48
    - * Function: awaitUserSignals()
    
    49
    - *
    
    50
    - * Wait for the next console event. Currently a NOP (returns immediately.)
    
    36
    +/* Tear down and shut down user signal processing.
    
    37
    + * This is called *after* freeSignalHandlers, but unconditionally!
    
    38
    + * TODO: unify this and freeSignalHandlers together, and make them make sense!
    
    51 39
      */
    
    52
    -void awaitUserSignals(void);
    
    40
    +void finiUserSignals(void);
    
    53 41
     
    
    54 42
     /*
    
    55 43
      * Function: startPendingSignalHandlers()
    
    56 44
      *
    
    57
    - * Start any pending signal handlers. This is used by the scheduler and some
    
    58
    - * in-RTS I/O managers. It does nothing (returns false) in the threaded RTS.
    
    59
    - *
    
    60
    - * Returns true if any signal handlers were pending and thus started.
    
    45
    + * If there are any queued up posix signals or win32 console events, run the
    
    46
    + * handlers associated with them. This is used by some in-RTS I/O managers.
    
    61 47
      */
    
    62
    -INLINE_HEADER bool startPendingSignalHandlers(Capability *cap);
    
    63
    -
    
    64 48
     #if !defined(THREADED_RTS)
    
    65
    -INLINE_HEADER bool startPendingSignalHandlers(Capability *cap)
    
    66
    -{
    
    67
    -    if (RtsFlags.MiscFlags.install_signal_handlers && signals_pending()) {
    
    68
    -        // safe outside the lock
    
    69
    -        startSignalHandlers(cap);
    
    70
    -        return true;
    
    71
    -    } else {
    
    72
    -        return false;
    
    73
    -    }
    
    74
    -}
    
    75
    -#else
    
    76
    -INLINE_HEADER bool startPendingSignalHandlers(Capability *cap STG_UNUSED)
    
    77
    -{
    
    78
    -    return false;
    
    79
    -}
    
    49
    +void startPendingSignalHandlers(Capability *cap);
    
    80 50
     #endif
    
    81 51
     
    
    82 52
     #include "EndPrivate.h"
    

  • rts/RtsStartup.c
    ... ... @@ -70,6 +70,10 @@
    70 70
     #include <locale.h>
    
    71 71
     #endif
    
    72 72
     
    
    73
    +#if !defined(mingw32_HOST_OS) && defined(HAVE_SIGNAL_H)
    
    74
    +#include <signal.h>
    
    75
    +#endif
    
    76
    +
    
    73 77
     // Count of how many outstanding hs_init()s there have been.
    
    74 78
     static StgWord hs_init_count = 0;
    
    75 79
     static bool rts_shutdown = false;
    
    ... ... @@ -427,7 +431,7 @@ hs_init_ghc(int *argc, char **argv[], RtsConfig rts_config)
    427 431
         }
    
    428 432
     #endif
    
    429 433
     
    
    430
    -    initIOManager();
    
    434
    +    startIOManager();
    
    431 435
     
    
    432 436
         x86_init_fpu();
    
    433 437
     
    
    ... ... @@ -619,9 +623,10 @@ hs_exit_(bool wait_foreign)
    619 623
     #if defined(mingw32_HOST_OS)
    
    620 624
        if (is_io_mng_native_p())
    
    621 625
           hs_restoreConsoleCP();
    
    626
    +#endif
    
    622 627
     
    
    623
    -   /* Disable console signal handlers, we're going down!.  */
    
    624
    -   finiUserSignals ();
    
    628
    +#if defined(RTS_USER_SIGNALS)
    
    629
    +    finiUserSignals();
    
    625 630
     #endif
    
    626 631
     
    
    627 632
         /* tear down statistics subsystem */
    

  • rts/RtsSymbols.c
    ... ... @@ -71,7 +71,6 @@ extern char **environ;
    71 71
           SymI_HasProto(__hscore_get_saved_termios) \
    
    72 72
           SymI_HasProto(__hscore_set_saved_termios) \
    
    73 73
           SymI_HasProto(shutdownHaskellAndSignal)   \
    
    74
    -      SymI_HasProto(signal_handlers)            \
    
    75 74
           SymI_HasProto(stg_sig_install)            \
    
    76 75
           SymI_HasProto(rtsTimerSignal)             \
    
    77 76
           SymI_NeedsDataProto(nocldstop)
    

  • rts/Schedule.c
    ... ... @@ -146,7 +146,6 @@ static void acquireAllCapabilities(Capability *cap, Task *task);
    146 146
     static void startWorkerTasks (uint32_t from USED_IF_THREADS,
    
    147 147
                                   uint32_t to USED_IF_THREADS);
    
    148 148
     #endif
    
    149
    -static void scheduleCheckBlockedThreads (Capability *cap);
    
    150 149
     static void scheduleProcessInbox(Capability **cap);
    
    151 150
     static void scheduleDetectDeadlock (Capability **pcap, Task *task);
    
    152 151
     static void schedulePushWork(Capability *cap, Task *task);
    
    ... ... @@ -174,6 +173,11 @@ static void deleteAllThreads (void);
    174 173
     static void deleteThread_(StgTSO *tso);
    
    175 174
     #endif
    
    176 175
     
    
    176
    +#if defined(FORKPROCESS_PRIMOP_SUPPORTED)
    
    177
    +static void truncateRunQueue(Capability *cap);
    
    178
    +#endif
    
    179
    +static StgTSO *popRunQueue (Capability *cap);
    
    180
    +
    
    177 181
     /* ---------------------------------------------------------------------------
    
    178 182
        Main scheduling loop.
    
    179 183
     
    
    ... ... @@ -295,21 +299,21 @@ schedule (Capability *initialCapability, Task *task)
    295 299
            (pushes threads, wakes up idle capabilities for stealing) */
    
    296 300
         schedulePushWork(cap,task);
    
    297 301
     
    
    298
    -    scheduleDetectDeadlock(&cap,task);
    
    302
    +    if (emptyRunQueue(cap)) {
    
    303
    +        /* When we have no threads to run, we *might* have a deadlock. */
    
    304
    +        scheduleDetectDeadlock(&cap,task);
    
    305
    +    }
    
    299 306
     
    
    300
    -    // Normally, the only way we can get here with no threads to
    
    301
    -    // run is if a keyboard interrupt received during
    
    302
    -    // scheduleCheckBlockedThreads() or scheduleDetectDeadlock().
    
    303
    -    // Additionally, it is not fatal for the
    
    304
    -    // threaded RTS to reach here with no threads to run.
    
    305
    -    //
    
    306
    -    // Since IOPorts have no deadlock avoidance guarantees you may also reach
    
    307
    -    // this point when blocked on an IO Port.  If this is the case the only
    
    308
    -    // thing that could unblock it is an I/O event.
    
    309
    -    //
    
    310
    -    // win32: might be here due to awaitCompletedTimeoutsOrIO() being abandoned
    
    311
    -    // as a result of a console event having been delivered or as a result of
    
    312
    -    // waiting on an async I/O to complete with WinIO.
    
    307
    +#if !defined(THREADED_RTS)
    
    308
    +    /* scheduleFindWork checks for completed I/O but does not block. If there
    
    309
    +     * is nothing to do now, we block and wait for I/O, timeouts or signals.
    
    310
    +     * Importantly, we only block /after/ checking for deadlocks. See #26408.
    
    311
    +     */
    
    312
    +    if (emptyRunQueue(cap)) {
    
    313
    +        awaitCompletedTimeoutsOrIO(cap->iomgr);
    
    314
    +        if (emptyRunQueue(cap)) continue; // look for work again
    
    315
    +    }
    
    316
    +#endif
    
    313 317
     
    
    314 318
     #if defined(THREADED_RTS)
    
    315 319
         scheduleYield(&cap,task);
    
    ... ... @@ -317,22 +321,6 @@ schedule (Capability *initialCapability, Task *task)
    317 321
         if (emptyRunQueue(cap)) continue; // look for work again
    
    318 322
     #endif
    
    319 323
     
    
    320
    -#if !defined(THREADED_RTS)
    
    321
    -    if ( emptyRunQueue(cap) ) {
    
    322
    -#if defined(mingw32_HOST_OS)
    
    323
    -        /* Notify the I/O manager that we have nothing to do.  If there are
    
    324
    -           any outstanding I/O requests we'll block here.  If there are not
    
    325
    -           then this is a user error and we will abort soon.  */
    
    326
    -        /* TODO: see if we can rationalise these two awaitCompletedTimeoutsOrIO
    
    327
    -         *       calls before and after scheduleDetectDeadlock().
    
    328
    -         */
    
    329
    -        awaitCompletedTimeoutsOrIO(cap->iomgr);
    
    330
    -#else
    
    331
    -        ASSERT(getSchedState() >= SCHED_INTERRUPTING);
    
    332
    -#endif
    
    333
    -    }
    
    334
    -#endif
    
    335
    -
    
    336 324
         //
    
    337 325
         // Get a thread to run
    
    338 326
         //
    
    ... ... @@ -403,13 +391,12 @@ schedule (Capability *initialCapability, Task *task)
    403 391
         }
    
    404 392
     #endif
    
    405 393
     
    
    406
    -    /* context switches are initiated by the timer signal, unless
    
    407
    -     * the user specified "context switch as often as possible", with
    
    408
    -     * +RTS -C0
    
    409
    -     */
    
    410
    -    if (RtsFlags.ConcFlags.ctxtSwitchTicks == 0 &&
    
    411
    -        (!emptyRunQueue(cap) ||
    
    412
    -          anyPendingTimeoutsOrIO(cap->iomgr))) {
    
    394
    +    // Context switches are normally initiated by the timer signal. If however
    
    395
    +    // the user specified "context switch as often as possible", with +RTS -C0
    
    396
    +    // then we now arrange for an early context switch. Context switching very
    
    397
    +    // often is expensive, so as an optimisation if there's no other threads
    
    398
    +    // to run then we don't arrange a context switch.
    
    399
    +    if (RtsFlags.ConcFlags.ctxtSwitchTicks == 0 && !emptyRunQueue(cap)) {
    
    413 400
             RELAXED_STORE(&cap->context_switch, 1);
    
    414 401
         }
    
    415 402
     
    
    ... ... @@ -591,42 +578,12 @@ run_thread:
    591 578
       } /* end of while() */
    
    592 579
     }
    
    593 580
     
    
    594
    -/* -----------------------------------------------------------------------------
    
    595
    - * Run queue operations
    
    596
    - * -------------------------------------------------------------------------- */
    
    597
    -
    
    598
    -static void
    
    599
    -removeFromRunQueue (Capability *cap, StgTSO *tso)
    
    600
    -{
    
    601
    -    if (tso->block_info.prev == END_TSO_QUEUE) {
    
    602
    -        ASSERT(cap->run_queue_hd == tso);
    
    603
    -        cap->run_queue_hd = tso->_link;
    
    604
    -    } else {
    
    605
    -        setTSOLink(cap, tso->block_info.prev, tso->_link);
    
    606
    -    }
    
    607
    -    if (tso->_link == END_TSO_QUEUE) {
    
    608
    -        ASSERT(cap->run_queue_tl == tso);
    
    609
    -        cap->run_queue_tl = tso->block_info.prev;
    
    610
    -    } else {
    
    611
    -        setTSOPrev(cap, tso->_link, tso->block_info.prev);
    
    612
    -    }
    
    613
    -    tso->_link = tso->block_info.prev = END_TSO_QUEUE;
    
    614
    -    cap->n_run_queue--;
    
    615
    -
    
    616
    -    IF_DEBUG(sanity, checkRunQueue(cap));
    
    617
    -}
    
    618
    -
    
    619
    -void
    
    620
    -promoteInRunQueue (Capability *cap, StgTSO *tso)
    
    621
    -{
    
    622
    -    removeFromRunQueue(cap, tso);
    
    623
    -    pushOnRunQueue(cap, tso);
    
    624
    -}
    
    625
    -
    
    626 581
     /* -----------------------------------------------------------------------------
    
    627 582
      * scheduleFindWork()
    
    628 583
      *
    
    629 584
      * Search for work to do, and handle messages from elsewhere.
    
    585
    + *
    
    586
    + * This does *not* block/wait, even in the non-threaded case.
    
    630 587
      * -------------------------------------------------------------------------- */
    
    631 588
     
    
    632 589
     static void
    
    ... ... @@ -635,16 +592,17 @@ scheduleFindWork (Capability **pcap)
    635 592
     #if defined(mingw32_HOST_OS) && !defined(THREADED_RTS)
    
    636 593
         queueIOThread();
    
    637 594
     #endif
    
    638
    -#if defined(RTS_USER_SIGNALS)
    
    639
    -    startPendingSignalHandlers(*pcap);
    
    640
    -#endif
    
    641
    -
    
    642 595
         scheduleProcessInbox(pcap);
    
    643 596
     
    
    644
    -    scheduleCheckBlockedThreads(*pcap);
    
    597
    +    /* From here on, the cap can't change. */
    
    598
    +    Capability *cap = *pcap;
    
    599
    +
    
    600
    +#if !defined(THREADED_RTS)
    
    601
    +    pollCompletedTimeoutsOrIO(cap->iomgr);
    
    602
    +#endif
    
    645 603
     
    
    646 604
     #if defined(THREADED_RTS)
    
    647
    -    if (emptyRunQueue(*pcap)) { scheduleActivateSpark(*pcap); }
    
    605
    +    if (emptyRunQueue(cap)) { scheduleActivateSpark(cap); }
    
    648 606
     #endif
    
    649 607
     }
    
    650 608
     
    
    ... ... @@ -889,115 +847,158 @@ schedulePushWork(Capability *cap USED_IF_THREADS,
    889 847
     
    
    890 848
     }
    
    891 849
     
    
    892
    -/* ----------------------------------------------------------------------------
    
    893
    - * Check for blocked threads that can be woken up.
    
    894
    - * ------------------------------------------------------------------------- */
    
    895
    -
    
    896
    -static void
    
    897
    -scheduleCheckBlockedThreads(Capability *cap USED_IF_NOT_THREADS)
    
    898
    -{
    
    899
    -#if !defined(THREADED_RTS)
    
    900
    -    /* Check whether there is any completed I/O or expired timers. If so,
    
    901
    -     * process the competions as appropriate, which will typically cause some
    
    902
    -     * waiting threads to be woken up.
    
    903
    -     *
    
    904
    -     * If the run queue is empty, and there are no other threads running, we
    
    905
    -     * can wait indefinitely for something to happen.
    
    906
    -     *
    
    907
    -     * TODO: see if we can rationalise these two awaitCompletedTimeoutsOrIO
    
    908
    -     * calls before and after scheduleDetectDeadlock()
    
    909
    -     *
    
    910
    -     * TODO: this test anyPendingTimeoutsOrIO does not have a proper
    
    911
    -     * implementation the WinIO I/O manager!
    
    912
    -     *
    
    913
    -     * The select() I/O manager uses the sleeping_queue and the blocked_queue,
    
    914
    -     * and the test checks both. The legacy win32 I/O manager only consults
    
    915
    -     * the blocked_queue, but then it puts threads waiting on delay# on the
    
    916
    -     * blocked_queue too, so that's ok.
    
    917
    -     *
    
    918
    -     * The WinIO I/O manager does not use either the sleeping_queue or the
    
    919
    -     * blocked_queue, but it's implementation of anyPendingTimeoutsOrIO still
    
    920
    -     * checks both! Since both queues will _always_ be empty then it will
    
    921
    -     * _always_ return false and so awaitCompletedTimeoutsOrIO will _never_ be
    
    922
    -     * called here for WinIO. This may explain why there is a second call to
    
    923
    -     * awaitCompletedTimeoutsOrIO below for the case of !defined(THREADED_RTS)
    
    924
    -     * && defined(mingw32_HOST_OS).
    
    925
    -     */
    
    926
    -    if (anyPendingTimeoutsOrIO(cap->iomgr))
    
    927
    -    {
    
    928
    -        if (emptyRunQueue(cap)) {
    
    929
    -            // block and wait
    
    930
    -            awaitCompletedTimeoutsOrIO(cap->iomgr);
    
    931
    -        } else {
    
    932
    -            // poll but do not wait
    
    933
    -            pollCompletedTimeoutsOrIO(cap->iomgr);
    
    934
    -        }
    
    935
    -    }
    
    936
    -#endif
    
    937
    -}
    
    938
    -
    
    939 850
     /* ----------------------------------------------------------------------------
    
    940 851
      * Detect deadlock conditions and attempt to resolve them.
    
    941 852
      * ------------------------------------------------------------------------- */
    
    942 853
     
    
    854
    +/* Note [Deadlock detection]
    
    855
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    856
    +
    
    857
    +For the purpose of this explanation we define:
    
    858
    + * a /partial deadlock/ to be a set of threads that are deadlocked; and
    
    859
    + * a /system deadlock/ is when all threads are deadlocked.
    
    860
    +
    
    861
    +Obviously, we can have a partial deadlock without having a system
    
    862
    +deadlock. The design goal of deadlock detection is to guarantee to
    
    863
    +detect (and resolve) system deadlock, but to also try to detect (and
    
    864
    +resolve) partial deadlocks.
    
    865
    +
    
    866
    +There are two designs that the RTS has used for deadlock detection: a
    
    867
    +simple historical design originally used in the non-threaded RTS and a
    
    868
    +modern design for the threaded RTS. These days we use the modern design
    
    869
    +in both the threaded and non-threaded RTS.
    
    870
    +
    
    871
    +A high level way to think about the two designs is as follows:
    
    872
    + 1. the historical design looks for situations in which there *must* be
    
    873
    +    a system deadlock; whereas
    
    874
    + 2. the modern design looks for partial deadlocks opportunistically,
    
    875
    +    with the guarantee that if the overall system is deadlocked that we
    
    876
    +    will *eventually* detect this.
    
    877
    +
    
    878
    +An advantage of the historical design is that it will detect system
    
    879
    +deadlock promptly. A disadvantage is that it will never detect a
    
    880
    +partial deadlock (that isn't also a system deadlock).
    
    881
    +
    
    882
    +The modern design can detect partial deadlock, but it is not guaranteed
    
    883
    +to detect system deadlock promptly, just eventually.
    
    884
    +
    
    885
    +The mechanism for deadlock detection is garbage collection. GC can be
    
    886
    +instructed to look for deadlocked threads and if it finds them to throw
    
    887
    +exceptions to one or more threads involved in the deadlock. This
    
    888
    +mechanism can find partial deadlocks. It is however expensive -- more
    
    889
    +expensive than a normal major GC. So the difference in the historical
    
    890
    +and modern designs is in when we do this expensive GC check.
    
    891
    +
    
    892
    +The historical design
    
    893
    +---------------------
    
    894
    +
    
    895
    +When there was just one capability, as in the single threaded RTS, it
    
    896
    +is possible to follow a very simple design. When there are no runnable
    
    897
    +threads, and no threads blocked on pending I/O or on timers then there
    
    898
    +*must* be a deadlock. And thus running deadlock detection promptly in
    
    899
    +this situation is guaranteed to find the deadlock and wake up one or
    
    900
    +more threads. Thus we can guarantee afterwards that there are runnable
    
    901
    +threads.
    
    902
    +
    
    903
    +There are a couple problems with this design, but the biggest problem
    
    904
    +is that it cannot be extended to multiple capabilities. When there are
    
    905
    +multiple capabilities then the fact that there are no runnable threads
    
    906
    +on the current capability says nothing about runnable threads on other
    
    907
    +capabilities. Runnable threads elsewhere might wake up threads on this
    
    908
    +capability, and so there is no implication that there is a deadlock.
    
    909
    +
    
    910
    +The other problems with this design are:
    
    911
    + 1. it cannot find genuine deadlocks when there are any unrelated
    
    912
    +    threads blocked on I/O or timers (see issue #26408); and
    
    913
    + 2. it requires treating signals specially.
    
    914
    +
    
    915
    +The problem with signals is that they're a weird kind of I/O. Threads
    
    916
    +do not block waiting on signals. Rather signals can have handlers such
    
    917
    +that when a signal arrives, a new thread is started to execute the
    
    918
    +handler. This means it doesn't neatly fit into the condition "no
    
    919
    +threads blocked on pending I/O or on timers". And if we did shoehorn it
    
    920
    +into that definition then we would not look for deadlocks if there were
    
    921
    +any signal handlers registered, and we would still end up with no
    
    922
    +runnable threads after skipping deadlock detection, which violates the
    
    923
    +post-condition that there be runnable threads. So the solution was that
    
    924
    +after deadlock detection, if there are still no runnable threads and
    
    925
    +there are registered signal handlers then we conclude we must wait for
    
    926
    +a signal to be received -- which will start a thread and thus we will
    
    927
    +end up with runnable threads. But of course this is horrible: we have
    
    928
    +entangled two features far too tightly: deadlock detection with a weird
    
    929
    +-- and platform specific -- kind of I/O.
    
    930
    +
    
    931
    +The modern design
    
    932
    +-----------------
    
    933
    +
    
    934
    +A change of perspective is required. Instead of thinking of conditions
    
    935
    +in which there must be a deadlock, we simply look for deadlocks in such
    
    936
    +a way in which we will eventually find deadlocks if they exist. A
    
    937
    +benefit of this approach is that we can find deadlocks that the simple
    
    938
    +approach cannot. For example we can find deadlocks when there unrelated
    
    939
    +threads blocked on I/O or timers (see issue #26408).
    
    940
    +
    
    941
    +The question is when to run GC it its more expensive deadlock detection
    
    942
    +mode. We obviously do not want to do it too frequently. The design
    
    943
    +choice is to do it during idle GC, at least sometimes. Idle GC is only
    
    944
    +run some time after a capability goes idle. This is a good opportunity.
    
    945
    +We know there are no runnable threads on the capability, so there
    
    946
    +*might* be a deadlock, and when there's nothing else to do is also a
    
    947
    +good moment to do a more expensive GC.
    
    948
    +
    
    949
    +The idle GC is controlled by the RecentActivity status, which
    
    950
    +progresses through 4 stages: yes, maybe_no, inactive, done_gc. We only
    
    951
    +invoke a deadlock-detecting major GC in the inactive state. We get into
    
    952
    +the inactive state when:
    
    953
    + * the timer tick goes off
    
    954
    + * we were already in the maybe_no state (which itself requires no
    
    955
    +   activity on any capability for a whole timer tick)
    
    956
    + * idle GC is enabled
    
    957
    + * it's been long enough since the most recent idle GC.
    
    958
    +This timer tick also wakes up the I/O manager to ensue we get back to
    
    959
    +the scheduler, and thus to scheduleDetectDeadlock.
    
    960
    +
    
    961
    +Note that this means that deadlock detection is disabled if users
    
    962
    +disable idle GC (by setting +RTS -I0). Historically, idle GC was not
    
    963
    +used by default in the non-threaded RTS, but the modern design relies
    
    964
    +on it, so it is enabled by default in all cases.
    
    965
    +
    
    966
    +But if idle GC is enabled, then if there is a full system deadlock then
    
    967
    +eventually we will run a major GC with deadlock detection and detect
    
    968
    +and resolve the deadlock. It is not prompt. It must wait at least for
    
    969
    +an idle GC, which by default is 0.3s after all capabilities go idle.
    
    970
    +
    
    971
    +Furthermore, there is no post-condition for scheduleDetectDeadlock,
    
    972
    +because of the non-prompt "eventually" nature of the deadlock detection
    
    973
    +design. In particular there can still be no runnable threads. In the
    
    974
    +threaded RTS if there's no runnable threads after this we will yield the
    
    975
    +capability, while in the non-threaded we will ask the I/O manager to
    
    976
    +block and wait for I/O, timers or signals.
    
    977
    +*/
    
    978
    +
    
    943 979
     static void
    
    944 980
     scheduleDetectDeadlock (Capability **pcap, Task *task)
    
    945 981
     {
    
    946
    -    Capability *cap = *pcap;
    
    947
    -    /*
    
    948
    -     * Detect deadlock: when we have no threads to run, there are no
    
    949
    -     * threads blocked, waiting for I/O, or sleeping, and all the
    
    950
    -     * other tasks are waiting for work, we must have a deadlock of
    
    951
    -     * some description.
    
    952
    -     */
    
    953
    -    if ( emptyRunQueue(cap) && !anyPendingTimeoutsOrIO(cap->iomgr) )
    
    954
    -    {
    
    955
    -#if defined(THREADED_RTS)
    
    956
    -        /*
    
    957
    -         * In the threaded RTS, we only check for deadlock if there
    
    958
    -         * has been no activity in a complete timeslice.  This means
    
    959
    -         * we won't eagerly start a full GC just because we don't have
    
    960
    -         * any threads to run currently.
    
    961
    -         */
    
    962
    -        if (getRecentActivity() != ACTIVITY_INACTIVE) return;
    
    963
    -#endif
    
    964
    -
    
    965
    -        debugTrace(DEBUG_sched, "deadlocked, forcing major GC...");
    
    966
    -
    
    967
    -        // Garbage collection can release some new threads due to
    
    968
    -        // either (a) finalizers or (b) threads resurrected because
    
    969
    -        // they are unreachable and will therefore be sent an
    
    970
    -        // exception.  Any threads thus released will be immediately
    
    971
    -        // runnable.
    
    972
    -        scheduleDoGC (pcap, task, true/*force major GC*/, false /* Whether it is an overflow GC */, true/*deadlock detection*/, false/*nonconcurrent*/);
    
    973
    -        cap = *pcap;
    
    974
    -        // when force_major == true. scheduleDoGC sets
    
    975
    -        // recent_activity to ACTIVITY_DONE_GC and turns off the timer
    
    976
    -        // signal.
    
    982
    +    /* See Note [Deadlock detection] */
    
    983
    +    if (getRecentActivity() == ACTIVITY_INACTIVE) {
    
    977 984
     
    
    978
    -        if ( !emptyRunQueue(cap) ) return;
    
    985
    +        debugTrace(DEBUG_sched, "maybe deadlocked, forcing major GC...");
    
    979 986
     
    
    980
    -#if defined(RTS_USER_SIGNALS) && !defined(THREADED_RTS)
    
    981
    -        /* If we have user-installed signal handlers, then wait
    
    982
    -         * for signals to arrive rather then bombing out with a
    
    983
    -         * deadlock.
    
    987
    +        /* Garbage collection can release some new threads due to
    
    988
    +         * either (a) finalizers or (b) threads resurrected because
    
    989
    +         * they are unreachable and will therefore be sent an
    
    990
    +         * exception.  Any threads thus released will be immediately
    
    991
    +         * runnable.
    
    992
    +         */
    
    993
    +        scheduleDoGC (pcap, task,
    
    994
    +                      true  /* force major GC */,
    
    995
    +                      false /* Whether it is an overflow GC */,
    
    996
    +                      true  /* deadlock detection */,
    
    997
    +                      false /* nonconcurrent */);
    
    998
    +        /* When force_major == true, scheduleDoGC sets recent activity to
    
    999
    +         * getRecentActivity() == ACTIVITY_DONE_GC and turns off the timer
    
    1000
    +         * signal.
    
    984 1001
              */
    
    985
    -        if ( RtsFlags.MiscFlags.install_signal_handlers && anyUserHandlers() ) {
    
    986
    -            debugTrace(DEBUG_sched,
    
    987
    -                       "still deadlocked, waiting for signals...");
    
    988
    -
    
    989
    -            awaitUserSignals();
    
    990
    -
    
    991
    -            if (signals_pending()) {
    
    992
    -                startSignalHandlers(cap);
    
    993
    -            }
    
    994
    -
    
    995
    -            // either we have threads to run, or we were interrupted:
    
    996
    -            ASSERT(!emptyRunQueue(cap) || getSchedState() >= SCHED_INTERRUPTING);
    
    997
    -
    
    998
    -            return;
    
    999
    -        }
    
    1000
    -#endif
    
    1001 1002
         }
    
    1002 1003
     }
    
    1003 1004
     
    
    ... ... @@ -2191,7 +2192,15 @@ forkProcess(HsStablePtr *entry
    2191 2192
                 // bound threads for which the corresponding Task does not
    
    2192 2193
                 // exist.
    
    2193 2194
                 truncateRunQueue(cap);
    
    2194
    -            cap->n_run_queue = 0;
    
    2195
    +
    
    2196
    +            // Reset and re-initialise the capability's I/O manager,
    
    2197
    +            // to get the I/O manager ready again.
    
    2198
    +            //
    
    2199
    +            // Any threads waiting on I/O or timers should have been
    
    2200
    +            // removed from I/O manager queues by deleteThread_ above.
    
    2201
    +            // TODO: but we could assert that here.
    
    2202
    +            freeCapabilityIOManager(cap->iomgr);
    
    2203
    +            initCapabilityIOManager(cap->iomgr);
    
    2195 2204
     
    
    2196 2205
                 // Any suspended C-calling Tasks are no more, their OS threads
    
    2197 2206
                 // don't exist now:
    
    ... ... @@ -2208,7 +2217,7 @@ forkProcess(HsStablePtr *entry
    2208 2217
                 cap->n_returning_tasks = 0;
    
    2209 2218
     #endif
    
    2210 2219
     
    
    2211
    -            // Release all caps except 0, we'll use that for starting
    
    2220
    +            // Release all caps except 0, we'll use that for restarting
    
    2212 2221
                 // the IO manager and running the client action below.
    
    2213 2222
                 if (cap->no != 0) {
    
    2214 2223
                     task->cap = cap;
    
    ... ... @@ -2232,7 +2241,7 @@ forkProcess(HsStablePtr *entry
    2232 2241
             // like startup event, capabilities, process info etc
    
    2233 2242
             traceTaskCreate(task, cap);
    
    2234 2243
     
    
    2235
    -        initIOManagerAfterFork(cap->iomgr, &cap);
    
    2244
    +        restartIOManager(cap->iomgr, &cap);
    
    2236 2245
     
    
    2237 2246
             // start timer after the IOManager is initialized
    
    2238 2247
             // (the idle GC may wake up the IOManager)
    
    ... ... @@ -2337,6 +2346,10 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS)
    2337 2346
             // the capability; we don't have to worry about GC data
    
    2338 2347
             // structures, the nursery, etc.
    
    2339 2348
             //
    
    2349
    +        // This approach also handles threads blocked on I/O. Such threads
    
    2350
    +        // remain blocked, and when I/O completes and threads become runnable
    
    2351
    +        // then they are migrated away.
    
    2352
    +        //
    
    2340 2353
             for (n = new_n_capabilities; n < enabled_capabilities; n++) {
    
    2341 2354
                 getCapability(n)->disabled = true;
    
    2342 2355
                 traceCapDisable(getCapability(n));
    
    ... ... @@ -2897,9 +2910,7 @@ interruptStgRts(void)
    2897 2910
         ASSERT(getSchedState() != SCHED_SHUTTING_DOWN);
    
    2898 2911
         setSchedState(SCHED_INTERRUPTING);
    
    2899 2912
         interruptAllCapabilities();
    
    2900
    -#if defined(THREADED_RTS)
    
    2901 2913
         wakeUpRts();
    
    2902
    -#endif
    
    2903 2914
     }
    
    2904 2915
     
    
    2905 2916
     /* -----------------------------------------------------------------------------
    
    ... ... @@ -2915,15 +2926,13 @@ interruptStgRts(void)
    2915 2926
        will have interrupted any blocking system call in progress anyway.
    
    2916 2927
        -------------------------------------------------------------------------- */
    
    2917 2928
     
    
    2918
    -#if defined(THREADED_RTS)
    
    2919 2929
     void wakeUpRts(void)
    
    2920 2930
     {
    
    2921
    -    // This forces the IO Manager thread to wakeup, which will
    
    2931
    +    // This forces the IO Manager to wakeup, which will
    
    2922 2932
         // in turn ensure that some OS thread wakes up and runs the
    
    2923 2933
         // scheduler loop, which will cause a GC and deadlock check.
    
    2924 2934
         wakeupIOManager();
    
    2925 2935
     }
    
    2926
    -#endif
    
    2927 2936
     
    
    2928 2937
     /* -----------------------------------------------------------------------------
    
    2929 2938
        Deleting threads
    
    ... ... @@ -2997,7 +3006,7 @@ pushOnRunQueue (Capability *cap, StgTSO *tso)
    2997 3006
         cap->n_run_queue++;
    
    2998 3007
     }
    
    2999 3008
     
    
    3000
    -StgTSO *popRunQueue (Capability *cap)
    
    3009
    +static StgTSO *popRunQueue (Capability *cap)
    
    3001 3010
     {
    
    3002 3011
         ASSERT(cap->n_run_queue > 0);
    
    3003 3012
         StgTSO *t = cap->run_queue_hd;
    
    ... ... @@ -3017,6 +3026,45 @@ StgTSO *popRunQueue (Capability *cap)
    3017 3026
         return t;
    
    3018 3027
     }
    
    3019 3028
     
    
    3029
    +#if defined(FORKPROCESS_PRIMOP_SUPPORTED)
    
    3030
    +static void truncateRunQueue(Capability *cap)
    
    3031
    +{
    
    3032
    +    // Can only be called by the task owning the capability.
    
    3033
    +    TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_hd, "truncateRunQueue");
    
    3034
    +    TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_tl, "truncateRunQueue");
    
    3035
    +    TSAN_ANNOTATE_BENIGN_RACE(&cap->n_run_queue, "truncateRunQueue");
    
    3036
    +    cap->run_queue_hd = END_TSO_QUEUE;
    
    3037
    +    cap->run_queue_tl = END_TSO_QUEUE;
    
    3038
    +    cap->n_run_queue = 0;
    
    3039
    +}
    
    3040
    +#endif
    
    3041
    +
    
    3042
    +static void removeFromRunQueue (Capability *cap, StgTSO *tso)
    
    3043
    +{
    
    3044
    +    if (tso->block_info.prev == END_TSO_QUEUE) {
    
    3045
    +        ASSERT(cap->run_queue_hd == tso);
    
    3046
    +        cap->run_queue_hd = tso->_link;
    
    3047
    +    } else {
    
    3048
    +        setTSOLink(cap, tso->block_info.prev, tso->_link);
    
    3049
    +    }
    
    3050
    +    if (tso->_link == END_TSO_QUEUE) {
    
    3051
    +        ASSERT(cap->run_queue_tl == tso);
    
    3052
    +        cap->run_queue_tl = tso->block_info.prev;
    
    3053
    +    } else {
    
    3054
    +        setTSOPrev(cap, tso->_link, tso->block_info.prev);
    
    3055
    +    }
    
    3056
    +    tso->_link = tso->block_info.prev = END_TSO_QUEUE;
    
    3057
    +    cap->n_run_queue--;
    
    3058
    +
    
    3059
    +    IF_DEBUG(sanity, checkRunQueue(cap));
    
    3060
    +}
    
    3061
    +
    
    3062
    +void promoteInRunQueue (Capability *cap, StgTSO *tso)
    
    3063
    +{
    
    3064
    +    removeFromRunQueue(cap, tso);
    
    3065
    +    pushOnRunQueue(cap, tso);
    
    3066
    +}
    
    3067
    +
    
    3020 3068
     
    
    3021 3069
     /* -----------------------------------------------------------------------------
    
    3022 3070
        raiseExceptionHelper
    

  • rts/Schedule.h
    ... ... @@ -39,9 +39,7 @@ void scheduleThreadOn(Capability *cap, StgWord cpu, StgTSO *tso);
    39 39
      *
    
    40 40
      * Causes an OS thread to wake up and run the scheduler, if necessary.
    
    41 41
      */
    
    42
    -#if defined(THREADED_RTS)
    
    43 42
     void wakeUpRts(void);
    
    44
    -#endif
    
    45 43
     
    
    46 44
     /* raiseExceptionHelper */
    
    47 45
     StgWord raiseExceptionHelper (StgRegTable *reg, StgTSO *tso, StgClosure *exception);
    
    ... ... @@ -164,10 +162,6 @@ void appendToRunQueue (Capability *cap, StgTSO *tso);
    164 162
      */
    
    165 163
     void pushOnRunQueue (Capability *cap, StgTSO *tso);
    
    166 164
     
    
    167
    -/* Pop the first thread off the runnable queue.
    
    168
    - */
    
    169
    -StgTSO *popRunQueue (Capability *cap);
    
    170
    -
    
    171 165
     INLINE_HEADER StgTSO *
    
    172 166
     peekRunQueue (Capability *cap)
    
    173 167
     {
    
    ... ... @@ -184,18 +178,6 @@ emptyRunQueue(Capability *cap)
    184 178
         return cap->n_run_queue == 0;
    
    185 179
     }
    
    186 180
     
    
    187
    -INLINE_HEADER void
    
    188
    -truncateRunQueue(Capability *cap)
    
    189
    -{
    
    190
    -    // Can only be called by the task owning the capability.
    
    191
    -    TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_hd, "truncateRunQueue");
    
    192
    -    TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_tl, "truncateRunQueue");
    
    193
    -    TSAN_ANNOTATE_BENIGN_RACE(&cap->n_run_queue, "truncateRunQueue");
    
    194
    -    cap->run_queue_hd = END_TSO_QUEUE;
    
    195
    -    cap->run_queue_tl = END_TSO_QUEUE;
    
    196
    -    cap->n_run_queue = 0;
    
    197
    -}
    
    198
    -
    
    199 181
     #endif /* !IN_STG_CODE */
    
    200 182
     
    
    201 183
     #include "EndPrivate.h"

  • rts/Timer.c
    ... ... @@ -149,11 +149,9 @@ handle_tick(int unused STG_UNUSED)
    149 149
                   setRecentActivity(ACTIVITY_INACTIVE);
    
    150 150
                   inter_gc_ticks_to_gc = RtsFlags.GcFlags.interIdleGCWait /
    
    151 151
                                          RtsFlags.MiscFlags.tickInterval;
    
    152
    -#if defined(THREADED_RTS)
    
    153 152
                   wakeUpRts();
    
    154 153
                   // The scheduler will call stopTimer() when it has done
    
    155 154
                   // the GC.
    
    156
    -#endif
    
    157 155
               } else {
    
    158 156
                   setRecentActivity(ACTIVITY_DONE_GC);
    
    159 157
                   // disable timer signals (see #1623, #5991, #9105)
    

  • rts/posix/FdWakeup.c
    1
    +/* -----------------------------------------------------------------------------
    
    2
    + *
    
    3
    + * (c) The GHC Team 2025
    
    4
    + *
    
    5
    + * Utilities for a simple fd-based cross-thread wakeup mechanism.
    
    6
    + *
    
    7
    + * This is used in I/O managers, to provide a mechanism to wake them when they
    
    8
    + * are blocked waiting on fds and timeouts. The mechanism works by including
    
    9
    + * the read end fd into the set of fds the I/O manager waits on, and when a
    
    10
    + * wake up is needed, the write end fd is used.
    
    11
    + *
    
    12
    + * This is implemented using either eventfd() or pipe().
    
    13
    + *
    
    14
    + * Linux 2.6.22+ and FreeBSD 13+ support eventfd. It is a single fd with a
    
    15
    + * 64bit counter. It uses less resources than a pipe, and is probably a tad
    
    16
    + * faster. Using write() adds to the counter, while read() reads and resets
    
    17
    + * it. This gives us event combining.
    
    18
    + *
    
    19
    + * Otherwise we use a classic unix pipe.
    
    20
    + *
    
    21
    + * -------------------------------------------------------------------------*/
    
    22
    +
    
    23
    +#include "rts/PosixSource.h"
    
    24
    +#include "Rts.h"
    
    25
    +
    
    26
    +#include "FdWakeup.h"
    
    27
    +
    
    28
    +#include <fcntl.h>
    
    29
    +#include <unistd.h>
    
    30
    +
    
    31
    +#ifdef HAVE_SYS_EVENTFD_H
    
    32
    +#include <sys/eventfd.h>
    
    33
    +#endif
    
    34
    +
    
    35
    +#if !defined(HAVE_EVENTFD) \
    
    36
    + || (defined(HAVE_EVENTFD) && !(defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)))
    
    37
    +static void fcntl_CLOEXEC_NONBLOCK(int fd)
    
    38
    +{
    
    39
    +    int res1 = fcntl(fd, F_SETFD, FD_CLOEXEC);
    
    40
    +    int res2 = fcntl(fd, F_SETFL, O_NONBLOCK);
    
    41
    +    if (RTS_UNLIKELY(res1 < 0 || res2 < 0)) {
    
    42
    +        sysErrorBelch("newFdWakeup fcntl()");
    
    43
    +        stg_exit(EXIT_FAILURE);
    
    44
    +    }
    
    45
    +}
    
    46
    +#endif
    
    47
    +
    
    48
    +void newFdWakeup(int *wakeup_fd_r, int *wakeup_fd_w)
    
    49
    +{
    
    50
    +#if defined(HAVE_EVENTFD)
    
    51
    +    int wakeup_fd;
    
    52
    +#if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
    
    53
    +    wakeup_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
    
    54
    +#else
    
    55
    +    wakeup_fd = eventfd(0, 0);
    
    56
    +    if (wakeup_fd >= 0) fcntl_CLOEXEC_NONBLOCK(wakeup_fd);
    
    57
    +#endif
    
    58
    +    if (RTS_UNLIKELY(wakeup_fd < 0)) {
    
    59
    +        sysErrorBelch("newFdWakeup eventfd()");
    
    60
    +        stg_exit(EXIT_FAILURE);
    
    61
    +    }
    
    62
    +    /* eventfd uses the same fd for each end */
    
    63
    +    *wakeup_fd_r = wakeup_fd;
    
    64
    +    *wakeup_fd_w = wakeup_fd;
    
    65
    +#else
    
    66
    +    int pipefd[2];
    
    67
    +    int res;
    
    68
    +    res = pipe(pipefd);
    
    69
    +    if (RTS_UNLIKELY(res < 0)) {
    
    70
    +        sysErrorBelch("newFdWakeup pipe");
    
    71
    +        stg_exit(EXIT_FAILURE);
    
    72
    +    }
    
    73
    +    fcntl_CLOEXEC_NONBLOCK(pipefd[0]);
    
    74
    +    fcntl_CLOEXEC_NONBLOCK(pipefd[1]);
    
    75
    +    *wakeup_fd_r = pipefd[0]; /* read end */
    
    76
    +    *wakeup_fd_w = pipefd[1]; /* write end */
    
    77
    +#endif
    
    78
    +}
    
    79
    +
    
    80
    +void closeFdWakeup(int wakeup_fd_r, int wakeup_fd_w)
    
    81
    +{
    
    82
    +#if defined(HAVE_EVENTFD)
    
    83
    +    ASSERT(wakeup_fd_r == wakeup_fd_w);
    
    84
    +    close(wakeup_fd_r);
    
    85
    +#else
    
    86
    +    ASSERT(wakeup_fd_r != wakeup_fd_w);
    
    87
    +    close(wakeup_fd_r);
    
    88
    +    close(wakeup_fd_w);
    
    89
    +#endif
    
    90
    +}
    
    91
    +
    
    92
    +void sendFdWakeup(int wakeup_fd_w)
    
    93
    +{
    
    94
    +    int res;
    
    95
    +#if defined(HAVE_EVENTFD)
    
    96
    +    uint64_t val = 1;
    
    97
    +    res = write(wakeup_fd_w, &val, 8);
    
    98
    +#else
    
    99
    +    unsigned char buf = 1;
    
    100
    +    res = write(wakeup_fd_w, &buf, 1);
    
    101
    +#endif
    
    102
    +    if (RTS_UNLIKELY(res < 0)) {
    
    103
    +        /* Unlikely the pipe buffer will fill, but it would not be an error. */
    
    104
    +        if (errno == EAGAIN) return;
    
    105
    +        sysErrorBelch("sendFdWakeup write");
    
    106
    +        stg_exit(EXIT_FAILURE);
    
    107
    +    }
    
    108
    +}
    
    109
    +
    
    110
    +void collectFdWakeup(int wakeup_fd_r)
    
    111
    +{
    
    112
    +    int res;
    
    113
    +#if defined(HAVE_EVENTFD)
    
    114
    +    uint64_t buf;
    
    115
    +    /* eventfd combines events into one counter, so a single read is enough */
    
    116
    +    res = read(wakeup_fd_r, &buf, 8);
    
    117
    +#else
    
    118
    +    /* Drain the pipe buffer. Multiple wakeup notifications could
    
    119
    +     * have been sent before we have a chance to collect them.
    
    120
    +     */
    
    121
    +    uint64_t buf;
    
    122
    +    do {
    
    123
    +        res = read(wakeup_fd_r, &buf, 8);
    
    124
    +    } while (res == 8);
    
    125
    +#endif
    
    126
    +    if (RTS_UNLIKELY(res < 0)) {
    
    127
    +        /* After the first pipe read, it could block */
    
    128
    +        if (errno == EAGAIN) return;
    
    129
    +        sysErrorBelch("collectFdWakeup read");
    
    130
    +        stg_exit(EXIT_FAILURE);
    
    131
    +    }
    
    132
    +}

  • rts/posix/FdWakeup.h
    1
    +/* -----------------------------------------------------------------------------
    
    2
    + *
    
    3
    + * (c) The GHC Team 2025
    
    4
    + *
    
    5
    + * Utilities for a simple fd-based cross-thread wakeup mechanism.
    
    6
    + *
    
    7
    + * This is used in I/O managers, to provide a mechanism to wake them when they
    
    8
    + * are blocked waiting on fds and timeouts. The mechanism works by including
    
    9
    + * the read end fd into the set of fds the I/O manager waits on, and when a
    
    10
    + * wake up is needed, the write end fd is used.
    
    11
    + *
    
    12
    + * Prototypes for functions in FdWakeup.c
    
    13
    + *
    
    14
    + * -------------------------------------------------------------------------*/
    
    15
    +
    
    16
    +#pragma once
    
    17
    +
    
    18
    +#include "BeginPrivate.h"
    
    19
    +
    
    20
    +void newFdWakeup(int *fd_r, int *fd_w);
    
    21
    +void closeFdWakeup(int fd_r, int fd_w);
    
    22
    +
    
    23
    +void sendFdWakeup(int fd_w);
    
    24
    +void collectFdWakeup(int fd_r);
    
    25
    +
    
    26
    +#include "EndPrivate.h"
    
    27
    +

  • rts/posix/MIO.c
    1
    +/* -----------------------------------------------------------------------------
    
    2
    + *
    
    3
    + * (c) The GHC Team, 1998-2005
    
    4
    + *
    
    5
    + * Signal processing / handling.
    
    6
    + *
    
    7
    + * ---------------------------------------------------------------------------*/
    
    8
    +
    
    9
    +#include "rts/PosixSource.h"
    
    10
    +#include "Rts.h"
    
    11
    +
    
    12
    +#include "Schedule.h"
    
    13
    +#include "RtsUtils.h"
    
    14
    +#include "Prelude.h"
    
    15
    +#include "ThreadLabels.h"
    
    16
    +
    
    17
    +#include "MIO.h"
    
    18
    +#include "IOManager.h"
    
    19
    +#include "IOManagerInternals.h"
    
    20
    +
    
    21
    +#if defined(HAVE_ERRNO_H)
    
    22
    +# include <errno.h>
    
    23
    +#endif
    
    24
    +
    
    25
    +#include <stdlib.h>
    
    26
    +#include <unistd.h>
    
    27
    +
    
    28
    +// Here's the pipe into which we will send our signals
    
    29
    +static int io_manager_wakeup_fd = -1;
    
    30
    +static int timer_manager_control_wr_fd = -1;
    
    31
    +// TODO: Eliminate these globals. Put then into the CapIOManager, but the
    
    32
    +// problem is these are shared across all caps, not per cap.
    
    33
    +
    
    34
    +#define IO_MANAGER_WAKEUP 0xff
    
    35
    +#define IO_MANAGER_DIE    0xfe
    
    36
    +#define IO_MANAGER_SYNC   0xfd
    
    37
    +
    
    38
    +void setTimerManagerControlFd(int fd) {
    
    39
    +    RELAXED_STORE(&timer_manager_control_wr_fd, fd);
    
    40
    +}
    
    41
    +
    
    42
    +void
    
    43
    +setIOManagerWakeupFd (int fd)
    
    44
    +{
    
    45
    +    // only called when THREADED_RTS, but unconditionally
    
    46
    +    // compiled here because GHC.Event.Control depends on it.
    
    47
    +    SEQ_CST_STORE(&io_manager_wakeup_fd, fd);
    
    48
    +}
    
    49
    +
    
    50
    +#if defined(THREADED_RTS)
    
    51
    +void timerManagerNotifySignal(int sig, siginfo_t *info)
    
    52
    +{
    
    53
    +    StgWord8 buf[sizeof(siginfo_t) + 1];
    
    54
    +    int r;
    
    55
    +
    
    56
    +    buf[0] = sig;
    
    57
    +    if (info == NULL) {
    
    58
    +        // info may be NULL on Solaris (see #3790)
    
    59
    +        memset(buf+1, 0, sizeof(siginfo_t));
    
    60
    +    } else {
    
    61
    +        memcpy(buf+1, info, sizeof(siginfo_t));
    
    62
    +    }
    
    63
    +
    
    64
    +    int timer_control_fd = RELAXED_LOAD(&timer_manager_control_wr_fd);
    
    65
    +    if (0 <= timer_control_fd)
    
    66
    +    {
    
    67
    +        r = write(timer_control_fd, buf, sizeof(siginfo_t)+1);
    
    68
    +        if (r == -1 && errno == EAGAIN) {
    
    69
    +            errorBelch("lost signal due to full pipe: %d\n", sig);
    
    70
    +        }
    
    71
    +    }
    
    72
    +
    
    73
    +    // If the IO manager hasn't told us what the FD of the write end
    
    74
    +    // of its pipe is, there's not much we can do here, so just ignore
    
    75
    +    // the signal..
    
    76
    +}
    
    77
    +#endif
    
    78
    +
    
    79
    +
    
    80
    +/* -----------------------------------------------------------------------------
    
    81
    + * Wake up at least one IO or timer manager HS thread.
    
    82
    + * -------------------------------------------------------------------------- */
    
    83
    +void
    
    84
    +ioManagerWakeup (void)
    
    85
    +{
    
    86
    +    int r;
    
    87
    +    const int wakeup_fd = SEQ_CST_LOAD(&io_manager_wakeup_fd);
    
    88
    +    // Wake up the IO Manager thread by sending a byte down its pipe
    
    89
    +    if (wakeup_fd >= 0) {
    
    90
    +#if defined(HAVE_EVENTFD)
    
    91
    +        StgWord64 n = (StgWord64)IO_MANAGER_WAKEUP;
    
    92
    +        r = write(wakeup_fd, (char *) &n, 8);
    
    93
    +#else
    
    94
    +        StgWord8 byte = (StgWord8)IO_MANAGER_WAKEUP;
    
    95
    +        r = write(wakeup_fd, &byte, 1);
    
    96
    +#endif
    
    97
    +        /* N.B. If the TimerManager is shutting down as we run this
    
    98
    +         * then there is a possibility that our first read of
    
    99
    +         * io_manager_wakeup_fd is non-negative, but before we get to the
    
    100
    +         * write the file is closed. If this occurs, io_manager_wakeup_fd
    
    101
    +         * will be written into with -1 (GHC.Event.Control does this prior
    
    102
    +         * to closing), so checking this allows us to distinguish this case.
    
    103
    +         * To ensure we observe the correct ordering, we declare the
    
    104
    +         * io_manager_wakeup_fd as volatile.
    
    105
    +         * Since this is not an error condition, we do not print the error
    
    106
    +         * message in this case.
    
    107
    +         */
    
    108
    +        if (r == -1 && SEQ_CST_LOAD(&io_manager_wakeup_fd) >= 0) {
    
    109
    +            sysErrorBelch("ioManagerWakeup: write");
    
    110
    +        }
    
    111
    +    }
    
    112
    +}
    
    113
    +
    
    114
    +#if defined(THREADED_RTS)
    
    115
    +void
    
    116
    +ioManagerDie (void)
    
    117
    +{
    
    118
    +    StgWord8 byte = (StgWord8)IO_MANAGER_DIE;
    
    119
    +    uint32_t i;
    
    120
    +    int r;
    
    121
    +
    
    122
    +    {
    
    123
    +        // Shut down timer manager
    
    124
    +        const int fd = RELAXED_LOAD(&timer_manager_control_wr_fd);
    
    125
    +        if (0 <= fd) {
    
    126
    +            r = write(fd, &byte, 1);
    
    127
    +            if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
    
    128
    +            RELAXED_STORE(&timer_manager_control_wr_fd, -1);
    
    129
    +        }
    
    130
    +    }
    
    131
    +
    
    132
    +    {
    
    133
    +        // Shut down IO managers
    
    134
    +        for (i=0; i < getNumCapabilities(); i++) {
    
    135
    +            const int fd = RELAXED_LOAD(&getCapability(i)->iomgr->control_fd);
    
    136
    +            if (0 <= fd) {
    
    137
    +                r = write(fd, &byte, 1);
    
    138
    +                if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
    
    139
    +                RELAXED_STORE(&getCapability(i)->iomgr->control_fd, -1);
    
    140
    +            }
    
    141
    +        }
    
    142
    +    }
    
    143
    +}
    
    144
    +
    
    145
    +void
    
    146
    +ioManagerStartCap (Capability **cap)
    
    147
    +{
    
    148
    +    rts_evalIO(cap,ensureIOManagerIsRunning_closure,NULL);
    
    149
    +}
    
    150
    +
    
    151
    +void
    
    152
    +ioManagerStart (void)
    
    153
    +{
    
    154
    +    // Make sure the IO manager thread is running
    
    155
    +    Capability *cap;
    
    156
    +    if (SEQ_CST_LOAD(&timer_manager_control_wr_fd) < 0 || SEQ_CST_LOAD(&io_manager_wakeup_fd) < 0) {
    
    157
    +        cap = rts_lock();
    
    158
    +        ioManagerStartCap(&cap);
    
    159
    +        rts_unlock(cap);
    
    160
    +    }
    
    161
    +}
    
    162
    +#endif
    
    163
    +

  • rts/posix/MIO.h
    1
    +/* -----------------------------------------------------------------------------
    
    2
    + *
    
    3
    + * (c) The GHC Team, 1998-2005
    
    4
    + *
    
    5
    + * Signal processing / handling.
    
    6
    + *
    
    7
    + * ---------------------------------------------------------------------------*/
    
    8
    +
    
    9
    +#pragma once
    
    10
    +
    
    11
    +#include "IOManager.h"
    
    12
    +
    
    13
    +#if defined(HAVE_SIGNAL_H)
    
    14
    +# include <signal.h>
    
    15
    +#endif
    
    16
    +
    
    17
    +#include "BeginPrivate.h"
    
    18
    +
    
    19
    +/* Communicating with the IO manager thread (see GHC.Conc).
    
    20
    + */
    
    21
    +void ioManagerWakeup (void);
    
    22
    +#if defined(THREADED_RTS)
    
    23
    +void ioManagerDie (void);
    
    24
    +void ioManagerStart (void);
    
    25
    +void ioManagerStartCap (/* inout */ Capability **cap);
    
    26
    +
    
    27
    +void timerManagerNotifySignal(int sig, siginfo_t *info);
    
    28
    +#endif
    
    29
    +
    
    30
    +#include "EndPrivate.h"

  • rts/posix/Poll.c
    ... ... @@ -41,6 +41,7 @@
    41 41
     
    
    42 42
     #include "IOManagerInternals.h"
    
    43 43
     #include "Timeout.h"
    
    44
    +#include "FdWakeup.h"
    
    44 45
     
    
    45 46
     /******************************************************************************
    
    46 47
     
    
    ... ... @@ -107,8 +108,9 @@ timeout (if any) as the poll() timeout parameter.
    107 108
     The CapIOManager structure for this I/O manager contains:
    
    108 109
     
    
    109 110
         ClosureTable     aiop_table;
    
    110
    -    struct pollfd   *aiop_poll_table;
    
    111
    +    struct pollfd   *aiop_poll_table, *full_poll_table;
    
    111 112
         StgTimeoutQueue *timeout_queue;
    
    113
    +    int wakeup_fd_r, wakeup_fd_w;
    
    112 114
     
    
    113 115
     We also support the Linux-specific ppoll API which supports higher resolution
    
    114 116
     time delays -- nanoseconds rather than milliseconds as in classic poll(). It
    
    ... ... @@ -117,6 +119,15 @@ also allows the signal mask to be adjusted, but we do not make use of this.
    117 119
        int ppoll(struct pollfd *fds, nfds_t nfds,
    
    118 120
                const struct timespec *tmo_p, const sigset_t *sigmask);
    
    119 121
     
    
    122
    +We have both aiop_poll_table and full_poll_table. This is to cope with needing
    
    123
    +to wait on the special extra file descriptor wakeup_fd_r. This fd is used to
    
    124
    +support waking the I/O manager when we are blocked in a poll call. This
    
    125
    +requires waiting on an extra fd that has no corresponding entry in the
    
    126
    +aiop_table. To manage this quirk, we alias the aiop_poll_table to be the tail
    
    127
    +of the full_poll_table and have the first entry of the full_poll_table be the
    
    128
    +wakeup_fd_r. This means the aiop_poll_table indicies match up exactly with the
    
    129
    +aiop_table, but still allows the full_poll_table to have an extra entry.
    
    130
    +
    
    120 131
     ******************************************************************************/
    
    121 132
     
    
    122 133
     /* Forward declarations */
    
    ... ... @@ -129,8 +140,31 @@ static void reportPollError(int res, nfds_t nfds) STG_NORETURN;
    129 140
     void initCapabilityIOManagerPoll(CapIOManager *iomgr)
    
    130 141
     {
    
    131 142
         initClosureTable(&iomgr->aiop_table, ClosureTableCompact);
    
    132
    -    iomgr->aiop_poll_table = NULL;
    
    133 143
         iomgr->timeout_queue = emptyTimeoutQueue();
    
    144
    +
    
    145
    +    newFdWakeup(&iomgr->wakeup_fd_r, &iomgr->wakeup_fd_w);
    
    146
    +
    
    147
    +    iomgr->full_poll_table = stgMallocBytes(sizeof(struct pollfd) /* size 1 */,
    
    148
    +                                            "initCapabilityIOManagerPoll");
    
    149
    +    iomgr->full_poll_table[0] = (struct pollfd) {
    
    150
    +                                  .fd      = iomgr->wakeup_fd_r,
    
    151
    +                                  .events  = POLLIN,
    
    152
    +                                  .revents = 0
    
    153
    +                                };
    
    154
    +    iomgr->aiop_poll_table = iomgr->full_poll_table+1; /* hence empty */
    
    155
    +}
    
    156
    +
    
    157
    +
    
    158
    +void freeCapabilityIOManagerPoll(CapIOManager *iomgr)
    
    159
    +{
    
    160
    +    stgFree(iomgr->full_poll_table);
    
    161
    +    closeFdWakeup(iomgr->wakeup_fd_r, iomgr->wakeup_fd_w);
    
    162
    +}
    
    163
    +
    
    164
    +
    
    165
    +void wakeupIOManagerPoll(CapIOManager *iomgr)
    
    166
    +{
    
    167
    +    sendFdWakeup(iomgr->wakeup_fd_w);
    
    134 168
     }
    
    135 169
     
    
    136 170
     
    
    ... ... @@ -227,13 +261,6 @@ static void ioCancel(CapIOManager *iomgr, StgAsyncIOOp *aiop)
    227 261
     }
    
    228 262
     
    
    229 263
     
    
    230
    -bool anyPendingTimeoutsOrIOPoll(CapIOManager *iomgr)
    
    231
    -{
    
    232
    -    return !isEmptyTimeoutQueue(iomgr->timeout_queue)
    
    233
    -        || !isEmptyClosureTable(&iomgr->aiop_table);
    
    234
    -}
    
    235
    -
    
    236
    -
    
    237 264
     static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop)
    
    238 265
     {
    
    239 266
         ASSERT(aiop->outcome != IOOpOutcomeInFlight);
    
    ... ... @@ -275,7 +302,7 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop)
    275 302
     }
    
    276 303
     
    
    277 304
     
    
    278
    -static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
    
    305
    +static bool processIOCompletions(CapIOManager *iomgr, int ncompletions)
    
    279 306
     {
    
    280 307
         /* The scheme we use with poll is that we have a dense poll table, and a
    
    281 308
          * corresponding table that maps to the closure table index. The poll
    
    ... ... @@ -285,6 +312,19 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
    285 312
          */
    
    286 313
         debugTrace(DEBUG_iomanager, "processIOCompletions(ncompletions = %d)",
    
    287 314
                                     ncompletions);
    
    315
    +
    
    316
    +    bool wakeup;
    
    317
    +    /* If the wakeup_fd_r is ready, collect it */
    
    318
    +    if (iomgr->full_poll_table[0].revents) {
    
    319
    +        ASSERT(iomgr->full_poll_table[0].fd == iomgr->wakeup_fd_r);
    
    320
    +        collectFdWakeup(iomgr->wakeup_fd_r);
    
    321
    +        ncompletions--;
    
    322
    +        wakeup = true;
    
    323
    +        debugTrace(DEBUG_iomanager, "Received wakeup in poll I/O manager.");
    
    324
    +    } else {
    
    325
    +        wakeup = false;
    
    326
    +    }
    
    327
    +
    
    288 328
         struct pollfd *aiop_poll_table = iomgr->aiop_poll_table;
    
    289 329
         int n = ncompletions;
    
    290 330
         int i = 0;
    
    ... ... @@ -337,11 +377,14 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
    337 377
                 i++;
    
    338 378
             }
    
    339 379
         }
    
    380
    +    return wakeup;
    
    340 381
     }
    
    341 382
     
    
    342 383
     
    
    343 384
     void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    
    344 385
     {
    
    386
    +    ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1);
    
    387
    +
    
    345 388
         if (!isEmptyTimeoutQueue(iomgr->timeout_queue)) {
    
    346 389
             Time now = getProcessElapsedTime();
    
    347 390
             processTimeoutCompletions(iomgr, now);
    
    ... ... @@ -349,20 +392,20 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    349 392
     
    
    350 393
         if (!isEmptyClosureTable(&iomgr->aiop_table)) {
    
    351 394
     
    
    352
    -        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table);
    
    395
    +        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1;
    
    353 396
     
    
    354 397
             /* Poll for I/O readiness, without waiting. */
    
    355 398
     #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
    
    356 399
             /* We could use poll here, since we use no timeout, but for
    
    357 400
                consistency we use the same syscall as at the other call site. */
    
    358 401
             struct timespec tv = (struct timespec) { .tv_sec = 0, .tv_nsec = 0 };
    
    359
    -        int res = ppoll(iomgr->aiop_poll_table, nfds, &tv, NULL);
    
    402
    +        int res = ppoll(iomgr->full_poll_table, nfds, &tv, NULL);
    
    360 403
     
    
    361 404
             debugTrace(DEBUG_iomanager,
    
    362 405
                        "ppoll(nfds = %d, timeout.sec = 0, timeout.nsec = 0) = %d",
    
    363 406
                        nfds, res);
    
    364 407
     #else
    
    365
    -        int res = poll(iomgr->aiop_poll_table, nfds, 0);
    
    408
    +        int res = poll(iomgr->full_poll_table, nfds, 0);
    
    366 409
     
    
    367 410
             debugTrace(DEBUG_iomanager,
    
    368 411
                        "poll(nfds = %d, timeout_ms = 0) = %d",
    
    ... ... @@ -385,11 +428,19 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    385 428
                 reportPollError(res, nfds);
    
    386 429
             }
    
    387 430
         }
    
    431
    +
    
    432
    +#if defined(RTS_USER_SIGNALS)
    
    433
    +    startPendingSignalHandlers(iomgr->cap);
    
    434
    +#endif
    
    388 435
     }
    
    389 436
     
    
    390 437
     
    
    391 438
     void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    
    392 439
     {
    
    440
    +    bool wakeup = false; /* got woken up via wakeupIOManager */
    
    441
    +
    
    442
    +    ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1);
    
    443
    +
    
    393 444
         /* Loop until we've woken up some threads. This loop is needed because the
    
    394 445
          * poll() timing isn't accurate, we sometimes sleep for a while but not
    
    395 446
          * long enough to wake up a thread in a threadDelay. Or we may need to
    
    ... ... @@ -397,9 +448,10 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    397 448
          * that select() supports.
    
    398 449
          */
    
    399 450
         do {
    
    400
    -        /* There is either pending I/O or pending timers. */
    
    401
    -        ASSERT(!isEmptyTimeoutQueue(iomgr->timeout_queue) ||
    
    402
    -               !isEmptyClosureTable(&iomgr->aiop_table));
    
    451
    +        /* We do /not/ require that there be pending I/O or pending timers.
    
    452
    +         * If there is neither, it's because the scheduler wants us to wait
    
    453
    +         * on signals only.
    
    454
    +         */
    
    403 455
     
    
    404 456
             Time now = getProcessElapsedTime();
    
    405 457
             processTimeoutCompletions(iomgr, now);
    
    ... ... @@ -422,9 +474,9 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    422 474
     #endif
    
    423 475
     
    
    424 476
             /* Check for I/O readiness, possibly waiting. */
    
    425
    -        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table);
    
    477
    +        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1;
    
    426 478
     #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
    
    427
    -        int res = ppoll(iomgr->aiop_poll_table, nfds, timeout_ns, NULL);
    
    479
    +        int res = ppoll(iomgr->full_poll_table, nfds, timeout_ns, NULL);
    
    428 480
     
    
    429 481
             debugTrace(DEBUG_iomanager,
    
    430 482
                        "ppoll(nfds = %d, timeout.sec = %d, timeout.nsec = %d) = %d",
    
    ... ... @@ -432,7 +484,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    432 484
                              timeout_ns == NULL ?  0 : timeout_ns->tv_nsec,
    
    433 485
                        res);
    
    434 486
     #else
    
    435
    -        int res = poll(iomgr->aiop_poll_table, nfds, timeout_ms);
    
    487
    +        int res = poll(iomgr->full_poll_table, nfds, timeout_ms);
    
    436 488
     
    
    437 489
             debugTrace(DEBUG_iomanager,
    
    438 490
                        "poll(nfds = %d, timeout_ms = %d) = %d",
    
    ... ... @@ -454,17 +506,16 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    454 506
             } else if (res > 0) {
    
    455 507
                 int ncompletions = res;
    
    456 508
                 ASSERT(ncompletions <= (int)nfds);
    
    457
    -            processIOCompletions(iomgr, ncompletions);
    
    509
    +            wakeup = processIOCompletions(iomgr, ncompletions);
    
    458 510
     
    
    459 511
             } else if (errno == EINTR) {
    
    460
    -            /* We got interrupted by a signal. In the non-threaded RTS, if the
    
    461
    -             * signal is one of ours we need to return to the scheduler to let
    
    462
    -             * it handle it. Otherwise we would loop and keep waiting for I/O
    
    463
    -             * or timeouts, meaning we would block for a long time before the
    
    464
    -             * signal is serviced.
    
    465
    -             */
    
    512
    +            /* We got interrupted by a signal. */
    
    513
    +
    
    466 514
     #if defined(RTS_USER_SIGNALS)
    
    467
    -            if (startPendingSignalHandlers(iomgr->cap)) break;
    
    515
    +            /* Start any corresponding user signal handlers. If any, the run
    
    516
    +             * queue will become non-empty and we will drop out of the loop.
    
    517
    +             */
    
    518
    +            startPendingSignalHandlers(iomgr->cap);
    
    468 519
     #endif
    
    469 520
     
    
    470 521
                 /* We can also be interrupted by the shutdown signal handler, which
    
    ... ... @@ -479,6 +530,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    479 530
             }
    
    480 531
     
    
    481 532
         } while (emptyRunQueue(iomgr->cap)
    
    533
    +         && !wakeup
    
    482 534
              && (getSchedState() == SCHED_RUNNING));
    
    483 535
     }
    
    484 536
     
    
    ... ... @@ -508,13 +560,17 @@ static bool enlargeTables(CapIOManager *iomgr)
    508 560
         bool ok = enlargeClosureTable(iomgr->cap, &iomgr->aiop_table, newcapacity);
    
    509 561
         if (RTS_UNLIKELY(!ok)) return false;
    
    510 562
     
    
    511
    -    /* Update the auxiliary aiop_poll_table to match */
    
    512
    -    struct pollfd *aiop_poll_table;
    
    513
    -    aiop_poll_table = stgReallocBytes(iomgr->aiop_poll_table,
    
    514
    -                                      sizeof(struct pollfd) * newcapacity,
    
    515
    -                                      "Poll.c: enlargeTables");
    
    516
    -    iomgr->aiop_poll_table = aiop_poll_table;
    
    563
    +    /* Update the auxiliary aiop_poll_table to match. The full_poll_table is
    
    564
    +     * one bigger than the aiop_poll_table, since it has an extra entry at the
    
    565
    +     * front for wakeup_fd_r, with no corresponding aiop. */
    
    566
    +    iomgr->full_poll_table =
    
    567
    +        stgReallocBytes(iomgr->full_poll_table,
    
    568
    +                        sizeof(struct pollfd) * (newcapacity+1),
    
    569
    +                        "Poll.c: enlargeTables");
    
    570
    +    iomgr->aiop_poll_table = iomgr->full_poll_table+1;
    
    571
    +
    
    517 572
         /* Initialise the new part of the aiop_poll_table */
    
    573
    +    struct pollfd *aiop_poll_table = iomgr->aiop_poll_table;
    
    518 574
         for (int i = oldcapacity; i < newcapacity; i++) {
    
    519 575
             aiop_poll_table[i] = (struct pollfd) {
    
    520 576
                                    .fd      = -1,
    

  • rts/posix/Poll.h
    ... ... @@ -17,6 +17,8 @@
    17 17
     #if defined(IOMGR_ENABLED_POLL)
    
    18 18
     
    
    19 19
     void initCapabilityIOManagerPoll(CapIOManager *iomgr);
    
    20
    +void freeCapabilityIOManagerPoll(CapIOManager *iomgr);
    
    21
    +void wakeupIOManagerPoll(CapIOManager *iomgr);
    
    20 22
     
    
    21 23
     /* Synchronous I/O and timer operations */
    
    22 24
     bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
    
    ... ... @@ -29,7 +31,6 @@ bool asyncIOWaitReadyPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop,
    29 31
     void asyncIOCancelPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop);
    
    30 32
     
    
    31 33
     /* Scheduler operations */
    
    32
    -bool anyPendingTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    33 34
     void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    34 35
     void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    35 36
     
    

  • rts/posix/Select.c
    ... ... @@ -12,7 +12,7 @@
    12 12
     #include "rts/PosixSource.h"
    
    13 13
     #include "Rts.h"
    
    14 14
     
    
    15
    -#include "Signals.h"
    
    15
    +#include "RtsSignals.h"
    
    16 16
     #include "Schedule.h"
    
    17 17
     #include "Prelude.h"
    
    18 18
     #include "RaiseAsync.h"
    
    ... ... @@ -22,6 +22,7 @@
    22 22
     #include "IOManagerInternals.h"
    
    23 23
     #include "Stats.h"
    
    24 24
     #include "GetTime.h"
    
    25
    +#include "FdWakeup.h"
    
    25 26
     
    
    26 27
     # if defined(HAVE_SYS_SELECT_H)
    
    27 28
     #  include <sys/select.h>
    
    ... ... @@ -54,6 +55,25 @@
    54 55
     #define TimeToLowResTimeRoundUp(t)   (t)
    
    55 56
     #endif
    
    56 57
     
    
    58
    +void initCapabilityIOManagerSelect(CapIOManager *iomgr)
    
    59
    +{
    
    60
    +    iomgr->blocked_queue_hd = END_TSO_QUEUE;
    
    61
    +    iomgr->blocked_queue_tl = END_TSO_QUEUE;
    
    62
    +    iomgr->sleeping_queue   = END_TSO_QUEUE;
    
    63
    +
    
    64
    +    newFdWakeup(&iomgr->wakeup_fd_r, &iomgr->wakeup_fd_w);
    
    65
    +}
    
    66
    +
    
    67
    +void freeCapabilityIOManagerSelect(CapIOManager *iomgr)
    
    68
    +{
    
    69
    +    closeFdWakeup(iomgr->wakeup_fd_r, iomgr->wakeup_fd_w);
    
    70
    +}
    
    71
    +
    
    72
    +void wakeupIOManagerSelect(CapIOManager *iomgr)
    
    73
    +{
    
    74
    +    sendFdWakeup(iomgr->wakeup_fd_w);
    
    75
    +}
    
    76
    +
    
    57 77
     /*
    
    58 78
      * Return the time since the program started, in LowResTime,
    
    59 79
      * rounded down.
    
    ... ... @@ -225,6 +245,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    225 245
         bool seen_bad_fd = false;
    
    226 246
         struct timeval tv, *ptv;
    
    227 247
         LowResTime now;
    
    248
    +    bool wakeup = false; /* got woken up via wakeupIOManager */
    
    228 249
     
    
    229 250
         IF_DEBUG(scheduler,
    
    230 251
                  debugBelch("scheduler: checking for threads blocked on I/O");
    
    ... ... @@ -252,6 +273,13 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    252 273
           FD_ZERO(&rfd);
    
    253 274
           FD_ZERO(&wfd);
    
    254 275
     
    
    276
    +      /* We're always interested in our wakeup fd */
    
    277
    +      {
    
    278
    +          int fd = iomgr->wakeup_fd_r;
    
    279
    +          maxfd = (fd > maxfd) ? fd : maxfd;
    
    280
    +          FD_SET(fd, &rfd);
    
    281
    +      }
    
    282
    +
    
    255 283
           for(tso = iomgr->blocked_queue_hd;
    
    256 284
               tso != END_TSO_QUEUE;
    
    257 285
               tso = next) {
    
    ... ... @@ -346,16 +374,11 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    346 374
                 }
    
    347 375
               }
    
    348 376
     
    
    349
    -          /* We got a signal; could be one of ours.  If so, we need
    
    350
    -           * to start up the signal handler straight away, otherwise
    
    351
    -           * we could block for a long time before the signal is
    
    352
    -           * serviced.
    
    353
    -           */
    
    354 377
     #if defined(RTS_USER_SIGNALS)
    
    355
    -          if (RtsFlags.MiscFlags.install_signal_handlers && signals_pending()) {
    
    356
    -              startSignalHandlers(iomgr->cap);
    
    357
    -              return; /* still hold the lock */
    
    358
    -          }
    
    378
    +          /* Start any corresponding user signal handlers. If any, the run
    
    379
    +           * queue will become non-empty and we will drop out of the loop.
    
    380
    +           */
    
    381
    +          startPendingSignalHandlers(iomgr->cap);
    
    359 382
     #endif
    
    360 383
     
    
    361 384
               /* we were interrupted, return to the scheduler immediately.
    
    ... ... @@ -376,6 +399,13 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    376 399
               }
    
    377 400
           }
    
    378 401
     
    
    402
    +      /* If the wakeup_fd_r is ready, collect it */
    
    403
    +      if (FD_ISSET(iomgr->wakeup_fd_r, &rfd)) {
    
    404
    +          collectFdWakeup(iomgr->wakeup_fd_r);
    
    405
    +          wakeup = true;
    
    406
    +          debugTrace(DEBUG_iomanager, "Received wakeup in select I/O manager.");
    
    407
    +      }
    
    408
    +
    
    379 409
           /* Step through the waiting queue, unblocking every thread that now has
    
    380 410
            * a file descriptor in a ready state.
    
    381 411
            */
    
    ... ... @@ -458,7 +488,8 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    458 488
           }
    
    459 489
     
    
    460 490
         } while (wait && getSchedState() == SCHED_RUNNING
    
    461
    -                  && emptyRunQueue(iomgr->cap));
    
    491
    +                  && emptyRunQueue(iomgr->cap)
    
    492
    +                  && !wakeup);
    
    462 493
     }
    
    463 494
     
    
    464 495
     #endif /* IOMGR_ENABLED_SELECT */

  • rts/posix/Select.h
    ... ... @@ -15,6 +15,10 @@ typedef StgWord LowResTime;
    15 15
     
    
    16 16
     LowResTime getDelayTarget (HsInt us);
    
    17 17
     
    
    18
    +void initCapabilityIOManagerSelect(CapIOManager *iomgr);
    
    19
    +void freeCapabilityIOManagerSelect(CapIOManager *iomgr);
    
    20
    +void wakeupIOManagerSelect(CapIOManager *iomgr);
    
    21
    +
    
    18 22
     void awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait);
    
    19 23
     
    
    20 24
     #include "EndPrivate.h"
    

  • rts/posix/Signals.c
    ... ... @@ -9,21 +9,13 @@
    9 9
     #include "rts/PosixSource.h"
    
    10 10
     #include "Rts.h"
    
    11 11
     
    
    12
    -#include "Schedule.h"
    
    13 12
     #include "RtsSignals.h"
    
    14
    -#include "Signals.h"
    
    15
    -#include "IOManager.h"
    
    13
    +#include "posix/Signals.h"
    
    16 14
     #include "RtsUtils.h"
    
    15
    +#include "Schedule.h"
    
    17 16
     #include "Prelude.h"
    
    18
    -#include "Ticker.h"
    
    19 17
     #include "ThreadLabels.h"
    
    20
    -#include "Libdw.h"
    
    21
    -
    
    22
    -/* TODO: eliminate this include. This file should be about signals, not be
    
    23
    - * part of an I/O manager implementation. The code here that are really part
    
    24
    - * of an I/O manager should be moved into an appropriate I/O manager impl.
    
    25
    - */
    
    26
    -#include "IOManagerInternals.h"
    
    18
    +#include "MIO.h"
    
    27 19
     
    
    28 20
     #if defined(alpha_HOST_ARCH)
    
    29 21
     # if defined(linux_HOST_OS)
    
    ... ... @@ -45,10 +37,6 @@
    45 37
     # include <errno.h>
    
    46 38
     #endif
    
    47 39
     
    
    48
    -#if defined(HAVE_EVENTFD_H)
    
    49
    -# include <sys/eventfd.h>
    
    50
    -#endif
    
    51
    -
    
    52 40
     #if defined(HAVE_TERMIOS_H)
    
    53 41
     #include <termios.h>
    
    54 42
     #endif
    
    ... ... @@ -107,6 +95,11 @@ freeSignalHandlers(void) {
    107 95
     #endif
    
    108 96
     }
    
    109 97
     
    
    98
    +void finiUserSignals(void)
    
    99
    +{
    
    100
    +    /* nothing */
    
    101
    +};
    
    102
    +
    
    110 103
     /* -----------------------------------------------------------------------------
    
    111 104
      * Allocate/resize the table of signal handlers.
    
    112 105
      * -------------------------------------------------------------------------- */
    
    ... ... @@ -134,110 +127,6 @@ more_handlers(int sig)
    134 127
         nHandlers = sig + 1;
    
    135 128
     }
    
    136 129
     
    
    137
    -// Here's the pipe into which we will send our signals
    
    138
    -static int io_manager_wakeup_fd = -1;
    
    139
    -static int timer_manager_control_wr_fd = -1;
    
    140
    -
    
    141
    -#define IO_MANAGER_WAKEUP 0xff
    
    142
    -#define IO_MANAGER_DIE    0xfe
    
    143
    -#define IO_MANAGER_SYNC   0xfd
    
    144
    -
    
    145
    -void setTimerManagerControlFd(int fd) {
    
    146
    -    RELAXED_STORE(&timer_manager_control_wr_fd, fd);
    
    147
    -}
    
    148
    -
    
    149
    -void
    
    150
    -setIOManagerWakeupFd (int fd)
    
    151
    -{
    
    152
    -    // only called when THREADED_RTS, but unconditionally
    
    153
    -    // compiled here because GHC.Event.Control depends on it.
    
    154
    -    SEQ_CST_STORE(&io_manager_wakeup_fd, fd);
    
    155
    -}
    
    156
    -
    
    157
    -/* -----------------------------------------------------------------------------
    
    158
    - * Wake up at least one IO or timer manager HS thread.
    
    159
    - * -------------------------------------------------------------------------- */
    
    160
    -void
    
    161
    -ioManagerWakeup (void)
    
    162
    -{
    
    163
    -    int r;
    
    164
    -    const int wakeup_fd = SEQ_CST_LOAD(&io_manager_wakeup_fd);
    
    165
    -    // Wake up the IO Manager thread by sending a byte down its pipe
    
    166
    -    if (wakeup_fd >= 0) {
    
    167
    -#if defined(HAVE_EVENTFD)
    
    168
    -        StgWord64 n = (StgWord64)IO_MANAGER_WAKEUP;
    
    169
    -        r = write(wakeup_fd, (char *) &n, 8);
    
    170
    -#else
    
    171
    -        StgWord8 byte = (StgWord8)IO_MANAGER_WAKEUP;
    
    172
    -        r = write(wakeup_fd, &byte, 1);
    
    173
    -#endif
    
    174
    -        /* N.B. If the TimerManager is shutting down as we run this
    
    175
    -         * then there is a possibility that our first read of
    
    176
    -         * io_manager_wakeup_fd is non-negative, but before we get to the
    
    177
    -         * write the file is closed. If this occurs, io_manager_wakeup_fd
    
    178
    -         * will be written into with -1 (GHC.Event.Control does this prior
    
    179
    -         * to closing), so checking this allows us to distinguish this case.
    
    180
    -         * To ensure we observe the correct ordering, we declare the
    
    181
    -         * io_manager_wakeup_fd as volatile.
    
    182
    -         * Since this is not an error condition, we do not print the error
    
    183
    -         * message in this case.
    
    184
    -         */
    
    185
    -        if (r == -1 && SEQ_CST_LOAD(&io_manager_wakeup_fd) >= 0) {
    
    186
    -            sysErrorBelch("ioManagerWakeup: write");
    
    187
    -        }
    
    188
    -    }
    
    189
    -}
    
    190
    -
    
    191
    -#if defined(THREADED_RTS)
    
    192
    -void
    
    193
    -ioManagerDie (void)
    
    194
    -{
    
    195
    -    StgWord8 byte = (StgWord8)IO_MANAGER_DIE;
    
    196
    -    uint32_t i;
    
    197
    -    int r;
    
    198
    -
    
    199
    -    {
    
    200
    -        // Shut down timer manager
    
    201
    -        const int fd = RELAXED_LOAD(&timer_manager_control_wr_fd);
    
    202
    -        if (0 <= fd) {
    
    203
    -            r = write(fd, &byte, 1);
    
    204
    -            if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
    
    205
    -            RELAXED_STORE(&timer_manager_control_wr_fd, -1);
    
    206
    -        }
    
    207
    -    }
    
    208
    -
    
    209
    -    {
    
    210
    -        // Shut down IO managers
    
    211
    -        for (i=0; i < getNumCapabilities(); i++) {
    
    212
    -            const int fd = RELAXED_LOAD(&getCapability(i)->iomgr->control_fd);
    
    213
    -            if (0 <= fd) {
    
    214
    -                r = write(fd, &byte, 1);
    
    215
    -                if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
    
    216
    -                RELAXED_STORE(&getCapability(i)->iomgr->control_fd, -1);
    
    217
    -            }
    
    218
    -        }
    
    219
    -    }
    
    220
    -}
    
    221
    -
    
    222
    -void
    
    223
    -ioManagerStartCap (Capability **cap)
    
    224
    -{
    
    225
    -    rts_evalIO(cap,ensureIOManagerIsRunning_closure,NULL);
    
    226
    -}
    
    227
    -
    
    228
    -void
    
    229
    -ioManagerStart (void)
    
    230
    -{
    
    231
    -    // Make sure the IO manager thread is running
    
    232
    -    Capability *cap;
    
    233
    -    if (SEQ_CST_LOAD(&timer_manager_control_wr_fd) < 0 || SEQ_CST_LOAD(&io_manager_wakeup_fd) < 0) {
    
    234
    -        cap = rts_lock();
    
    235
    -        ioManagerStartCap(&cap);
    
    236
    -        rts_unlock(cap);
    
    237
    -    }
    
    238
    -}
    
    239
    -#endif
    
    240
    -
    
    241 130
     #if !defined(THREADED_RTS)
    
    242 131
     
    
    243 132
     #define N_PENDING_HANDLERS 16
    
    ... ... @@ -245,6 +134,10 @@ ioManagerStart (void)
    245 134
     siginfo_t pending_handler_buf[N_PENDING_HANDLERS];
    
    246 135
     siginfo_t *next_pending_handler = pending_handler_buf;
    
    247 136
     
    
    137
    +static inline bool signals_pending(void) {
    
    138
    +    return (next_pending_handler != pending_handler_buf);
    
    139
    +}
    
    140
    +
    
    248 141
     #endif /* THREADED_RTS */
    
    249 142
     
    
    250 143
     /* -----------------------------------------------------------------------------
    
    ... ... @@ -260,31 +153,9 @@ generic_handler(int sig USED_IF_THREADS,
    260 153
                     void *p STG_UNUSED)
    
    261 154
     {
    
    262 155
     #if defined(THREADED_RTS)
    
    263
    -
    
    264
    -    StgWord8 buf[sizeof(siginfo_t) + 1];
    
    265
    -    int r;
    
    266
    -
    
    267
    -    buf[0] = sig;
    
    268
    -    if (info == NULL) {
    
    269
    -        // info may be NULL on Solaris (see #3790)
    
    270
    -        memset(buf+1, 0, sizeof(siginfo_t));
    
    271
    -    } else {
    
    272
    -        memcpy(buf+1, info, sizeof(siginfo_t));
    
    273
    -    }
    
    274
    -
    
    275
    -    int timer_control_fd = RELAXED_LOAD(&timer_manager_control_wr_fd);
    
    276
    -    if (0 <= timer_control_fd)
    
    277
    -    {
    
    278
    -        r = write(timer_control_fd, buf, sizeof(siginfo_t)+1);
    
    279
    -        if (r == -1 && errno == EAGAIN) {
    
    280
    -            errorBelch("lost signal due to full pipe: %d\n", sig);
    
    281
    -        }
    
    282
    -    }
    
    283
    -
    
    284
    -    // If the IO manager hasn't told us what the FD of the write end
    
    285
    -    // of its pipe is, there's not much we can do here, so just ignore
    
    286
    -    // the signal..
    
    287
    -
    
    156
    +    //TODO: This calls MIO directly. We should go via IOManager API.
    
    157
    +    // The IOManager API should be extended to cover signals.
    
    158
    +    timerManagerNotifySignal(sig, info);
    
    288 159
     #else /* not THREADED_RTS */
    
    289 160
     
    
    290 161
         /* Can't call allocate from here.  Probably can't call malloc
    
    ... ... @@ -346,22 +217,6 @@ unblockUserSignals(void)
    346 217
         sigprocmask(SIG_SETMASK, &savedSignals, NULL);
    
    347 218
     }
    
    348 219
     
    
    349
    -bool
    
    350
    -anyUserHandlers(void)
    
    351
    -{
    
    352
    -    return n_haskell_handlers != 0;
    
    353
    -}
    
    354
    -
    
    355
    -#if !defined(THREADED_RTS)
    
    356
    -void
    
    357
    -awaitUserSignals(void)
    
    358
    -{
    
    359
    -    while (!signals_pending() && getSchedState() == SCHED_RUNNING) {
    
    360
    -        pause();
    
    361
    -    }
    
    362
    -}
    
    363
    -#endif
    
    364
    -
    
    365 220
     /* -----------------------------------------------------------------------------
    
    366 221
      * Install a Haskell signal handler.
    
    367 222
      *
    
    ... ... @@ -468,11 +323,13 @@ stg_sig_install(int sig, int spi, void *mask)
    468 323
     
    
    469 324
     #if !defined(THREADED_RTS)
    
    470 325
     void
    
    471
    -startSignalHandlers(Capability *cap)
    
    326
    +startPendingSignalHandlers(Capability *cap)
    
    472 327
     {
    
    473 328
       siginfo_t *info;
    
    474 329
       int sig;
    
    475 330
     
    
    331
    +  if (!signals_pending()) return;
    
    332
    +
    
    476 333
       blockUserSignals();
    
    477 334
     
    
    478 335
       while (next_pending_handler != pending_handler_buf) {
    
    ... ... @@ -484,7 +341,7 @@ startSignalHandlers(Capability *cap)
    484 341
             continue; // handler has been changed.
    
    485 342
         }
    
    486 343
     
    
    487
    -    info = stgMallocBytes(sizeof(siginfo_t), "startSignalHandlers");
    
    344
    +    info = stgMallocBytes(sizeof(siginfo_t), "startPendingSignalHandlers");
    
    488 345
                // freed by runHandler
    
    489 346
         memcpy(info, next_pending_handler, sizeof(siginfo_t));
    
    490 347
     
    

  • rts/posix/Signals.h
    ... ... @@ -2,43 +2,19 @@
    2 2
      *
    
    3 3
      * (c) The GHC Team, 1998-2005
    
    4 4
      *
    
    5
    - * Signal processing / handling.
    
    5
    + * POSIX signal processing / handling.
    
    6
    + *
    
    7
    + * Most of the API for this is common between POSIX and Win32 console events.
    
    8
    + * The common part of the API lives in RtsSignals.h.
    
    6 9
      *
    
    7 10
      * ---------------------------------------------------------------------------*/
    
    8 11
     
    
    9 12
     #pragma once
    
    10 13
     
    
    11
    -#if defined(HAVE_SIGNAL_H)
    
    12
    -# include <signal.h>
    
    13
    -#endif
    
    14
    -
    
    15 14
     #include "Ticker.h"
    
    16 15
     
    
    17 16
     #include "BeginPrivate.h"
    
    18 17
     
    
    19
    -bool anyUserHandlers(void);
    
    20
    -
    
    21
    -#if !defined(THREADED_RTS) && defined(RTS_USER_SIGNALS)
    
    22
    -extern siginfo_t pending_handler_buf[];
    
    23
    -extern siginfo_t *next_pending_handler;
    
    24
    -#define signals_pending() (next_pending_handler != pending_handler_buf)
    
    25
    -void startSignalHandlers(Capability *cap);
    
    26
    -#endif
    
    27
    -
    
    28 18
     void install_vtalrm_handler(int sig, TickProc handle_tick);
    
    29 19
     
    
    30
    -/* Communicating with the IO manager thread (see GHC.Conc).
    
    31
    - *
    
    32
    - * TODO: these I/O manager things are not related to signals and ought to live
    
    33
    - * elsewhere, e.g. in a module specifically for the I/O manager.
    
    34
    - */
    
    35
    -void ioManagerWakeup (void);
    
    36
    -#if defined(THREADED_RTS)
    
    37
    -void ioManagerDie (void);
    
    38
    -void ioManagerStart (void);
    
    39
    -void ioManagerStartCap (/* inout */ Capability **cap);
    
    40
    -#endif
    
    41
    -
    
    42
    -extern StgInt *signal_handlers;
    
    43
    -
    
    44 20
     #include "EndPrivate.h"

  • rts/rts.cabal
    ... ... @@ -569,6 +569,7 @@ library
    569 569
                        wasm/OSThreads.c
    
    570 570
                        wasm/JSFFI.c
    
    571 571
                        wasm/JSFFIGlobals.c
    
    572
    +                   posix/FdWakeup.c
    
    572 573
                        posix/Select.c
    
    573 574
                        posix/Poll.c
    
    574 575
                        posix/Timeout.c
    
    ... ... @@ -581,6 +582,8 @@ library
    581 582
                         posix/Ticker.c
    
    582 583
                         posix/OSMem.c
    
    583 584
                         posix/OSThreads.c
    
    585
    +                    posix/FdWakeup.c
    
    586
    +                    posix/MIO.c
    
    584 587
                         posix/Poll.c
    
    585 588
                         posix/Select.c
    
    586 589
                         posix/Signals.c
    

  • rts/win32/AwaitEvent.c
    ... ... @@ -14,6 +14,7 @@
    14 14
      *
    
    15 15
      */
    
    16 16
     #include "Rts.h"
    
    17
    +#include "RtsSignals.h"
    
    17 18
     #include "RtsFlags.h"
    
    18 19
     #include "Schedule.h"
    
    19 20
     #include "IOManager.h"
    
    ... ... @@ -41,14 +42,9 @@ awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait)
    41 42
           awaitRequests(wait);
    
    42 43
         workerWaitingForRequests = false;
    
    43 44
     
    
    44
    -    // If a signal was raised, we need to service it
    
    45
    -    // XXX the scheduler loop really should be calling
    
    46
    -    // startSignalHandlers(), but this is the way that posix/Select.c
    
    47
    -    // does it and I'm feeling too paranoid to refactor it today --SDM
    
    48
    -    if (stg_pending_events != 0) {
    
    49
    -        startSignalHandlers(cap);
    
    50
    -        return;
    
    51
    -    }
    
    45
    +    // If a signal was raised, we need to service it. This will typically
    
    46
    +    // start a thread, which will cause us to drop out of the loop.
    
    47
    +    startPendingSignalHandlers(cap);
    
    52 48
     
    
    53 49
         // The return value from awaitRequests() is a red herring: ignore
    
    54 50
         // it.  Return to the scheduler if !wait, or
    

  • rts/win32/ConsoleHandler.c
    ... ... @@ -154,29 +154,18 @@ unblockUserSignals(void)
    154 154
     }
    
    155 155
     
    
    156 156
     
    
    157
    -/*
    
    158
    - * Function: awaitUserSignals()
    
    159
    - *
    
    160
    - * Wait for the next console event. Currently a NOP (returns immediately.)
    
    161
    - */
    
    162
    -void awaitUserSignals(void)
    
    163
    -{
    
    164
    -    return;
    
    165
    -}
    
    166
    -
    
    167
    -
    
    168 157
     #if !defined(THREADED_RTS)
    
    169 158
     /*
    
    170
    - * Function: startSignalHandlers()
    
    159
    + * Function: startPendingSignalHandlers()
    
    171 160
      *
    
    172
    - * Run the handlers associated with the stacked up console events. Console
    
    173
    - * event delivery is blocked for the duration of this call.
    
    161
    + * If there are any queued up console events, run the handlers associated with
    
    162
    + * them. Console event delivery is blocked for the duration of this call.
    
    174 163
      */
    
    175
    -void startSignalHandlers(Capability *cap)
    
    164
    +void startPendingSignalHandlers(Capability *cap)
    
    176 165
     {
    
    177 166
         StgStablePtr handler;
    
    178 167
     
    
    179
    -    if (console_handler < 0) {
    
    168
    +    if (stg_pending_events <= 0 || console_handler < 0) {
    
    180 169
             return;
    
    181 170
         }
    
    182 171
     
    

  • rts/win32/ConsoleHandler.h
    ... ... @@ -23,36 +23,6 @@
    23 23
      * thread, which starts up the handler.  See ThrIOManager.c.
    
    24 24
      */
    
    25 25
     
    
    26
    -/*
    
    27
    - * Function: signals_pending()
    
    28
    - *
    
    29
    - * Used by the RTS to check whether new signals have been 'recently' reported.
    
    30
    - * If so, the RTS arranges for the delivered signals to be handled by
    
    31
    - * de-queueing them from their table, running the associated Haskell
    
    32
    - * signal handler.
    
    33
    - */
    
    34
    -extern StgInt stg_pending_events;
    
    35
    -
    
    36
    -#define signals_pending() ( stg_pending_events > 0)
    
    37
    -
    
    38
    -/*
    
    39
    - * Function: anyUserHandlers()
    
    40
    - *
    
    41
    - * Used by the Scheduler to decide whether its worth its while to stick
    
    42
    - * around waiting for an external signal when there are no threads
    
    43
    - * runnable. A console handler is used to handle termination events (Ctrl+C)
    
    44
    - * and isn't considered a 'user handler'.
    
    45
    - */
    
    46
    -#define anyUserHandlers() (false)
    
    47
    -
    
    48
    -/*
    
    49
    - * Function: startSignalHandlers()
    
    50
    - *
    
    51
    - * Run the handlers associated with the queued up console events. Console
    
    52
    - * event delivery is blocked for the duration of this call.
    
    53
    - */
    
    54
    -extern void startSignalHandlers(Capability *cap);
    
    55
    -
    
    56 26
     /*
    
    57 27
      * Function: rts_waitConsoleHandlerCompletion()
    
    58 28
      *
    
    ... ... @@ -62,10 +32,3 @@ extern void startSignalHandlers(Capability *cap);
    62 32
     extern int rts_waitConsoleHandlerCompletion(void);
    
    63 33
     
    
    64 34
     #endif /* THREADED_RTS */
    65
    -
    
    66
    -/*
    
    67
    - * Function: finiUserSignals()
    
    68
    - *
    
    69
    - * Tear down and shut down user signal processing.
    
    70
    - */
    
    71
    -extern void finiUserSignals(void);

  • testsuite/tests/rts/T26408.hs
    1
    +import Control.Concurrent
    
    2
    +import Control.Concurrent.STM
    
    3
    +import Control.Exception
    
    4
    +import Control.Monad
    
    5
    +
    
    6
    +-- | Test to make sure that deadlock detection works even when there are other
    
    7
    +-- unrelated threads that are blocked on I\/O or timeouts.
    
    8
    +-- Historically however this did affect things in the non-threaded RTS which
    
    9
    +-- would only do deadlock detection if there were no runnable threads /and/
    
    10
    +-- no pending I\/O. See <https://gitlab.haskell.org/ghc/ghc/-/issues/26408>
    
    11
    +main :: IO ()
    
    12
    +main = do
    
    13
    +
    
    14
    +  -- Set up two threads that are deadlocked on each other
    
    15
    +  aDone <- newTVarIO False
    
    16
    +  bDone <- newTVarIO False
    
    17
    +  let blockingThread theirDone ourDone =
    
    18
    +        atomically $ do
    
    19
    +          done <- readTVar theirDone
    
    20
    +          guard done
    
    21
    +          writeTVar ourDone True
    
    22
    +  _ <- forkIO (blockingThread bDone aDone)
    
    23
    +  _ <- forkIO (blockingThread aDone bDone)
    
    24
    +
    
    25
    +  -- Set up another thread that is blocked on a long timeout.
    
    26
    +  --
    
    27
    +  -- We use a timeout rather than I/O as it's more portable, whereas I/O waits
    
    28
    +  -- are different between posix and windows I/O managers.
    
    29
    +  --
    
    30
    +  -- One gotcha is that when the timeout completes then the deadlock will be
    
    31
    +  -- detected again (since the bug is about I/O or timeouts masking deadlock
    
    32
    +  -- detection). So for a reliable test the timeout used here must be longer
    
    33
    +  -- than the test framework's own timeout. So we use maxBound, and we adjust
    
    34
    +  -- the test framework's timeout to be short (see run_timeout_multiplier).
    
    35
    +  _ <- forkIO (threadDelay maxBound)
    
    36
    +
    
    37
    +  -- Wait on the deadlocked threads to terminate. We now expect that the threads
    
    38
    +  -- that are deadlocked are detected as such and an exception is raised.
    
    39
    +  -- Note that if this fails, the test itself will effectively deadlock and
    
    40
    +  -- will rely on the test framework's timeout.
    
    41
    +  atomically $ do
    
    42
    +    status <- mapM readTVar [aDone, bDone]
    
    43
    +    guard (or status)

  • testsuite/tests/rts/T26408.stderr
    1
    +T26408: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.BlockedIndefinitelyOnSTM:
    
    2
    +
    
    3
    +thread blocked indefinitely in an STM transaction

  • testsuite/tests/rts/all.T
    ... ... @@ -657,6 +657,8 @@ test('T22859',
    657 657
           omit_ways(llvm_ways)],
    
    658 658
          compile_and_run, ['-with-rtsopts -A8K'])
    
    659 659
     
    
    660
    +test('T26408', [exit_code(1), run_timeout_multiplier(0.1)], compile_and_run, [''])
    
    661
    +
    
    660 662
     # These tests need access to the internal RTS headers.
    
    661 663
     # TODO: there is probably some cleaner way to do this, and it should probably
    
    662 664
     # be guarded for in-tree tests, since it cannot work against an arbitrary