Duncan Coutts pushed to branch wip/dcoutts/io-manager-tidy at Glasgow Haskell Compiler / GHC

Commits:

18 changed files:

Changes:

  • libraries/ghc-internal/src/GHC/Internal/Event/Control.hs
    ... ... @@ -39,7 +39,7 @@ import GHC.Internal.Show (Show)
    39 39
     import GHC.Internal.Types (Bool(..), Int, IO)
    
    40 40
     import GHC.Internal.Word (Word8)
    
    41 41
     import GHC.Internal.Foreign.C.Error (throwErrnoIfMinus1_, throwErrno, getErrno)
    
    42
    -import GHC.Internal.Foreign.C.Types (CInt(..), CSize(..))
    
    42
    +import GHC.Internal.Foreign.C.Types (CSize(..))
    
    43 43
     import GHC.Internal.Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrBytes, withForeignPtr)
    
    44 44
     import GHC.Internal.Foreign.Marshal.Alloc (alloca, allocaBytes)
    
    45 45
     import GHC.Internal.Foreign.Marshal.Array (allocaArray)
    
    ... ... @@ -51,7 +51,7 @@ import GHC.Internal.System.Posix.Types (Fd)
    51 51
     
    
    52 52
     #if defined(HAVE_EVENTFD)
    
    53 53
     import GHC.Internal.Foreign.C.Error (throwErrnoIfMinus1, eBADF)
    
    54
    -import GHC.Internal.Foreign.C.Types (CULLong(..))
    
    54
    +import GHC.Internal.Foreign.C.Types (CInt(..), CULLong(..))
    
    55 55
     #else
    
    56 56
     import GHC.Internal.Foreign.C.Error (eAGAIN, eWOULDBLOCK, eBADF)
    
    57 57
     #endif
    
    ... ... @@ -78,7 +78,10 @@ data Control = W {
    78 78
         , wakeupReadFd   :: {-# UNPACK #-} !Fd
    
    79 79
         , wakeupWriteFd  :: {-# UNPACK #-} !Fd
    
    80 80
     #endif
    
    81
    -    , didRegisterWakeupFd :: !Bool
    
    81
    +    , didRegisterWakeupFd :: !Bool -- ^ Now redundant. Always False.
    
    82
    +      --TODO: remove ^^ this redundant field.
    
    83
    +      -- Technically, removing this is an API change to base. Sigh.
    
    84
    +
    
    82 85
           -- | Have this Control's fds been cleaned up?
    
    83 86
         , controlIsDead  :: !(IORef Bool)
    
    84 87
         }
    
    ... ... @@ -91,8 +94,8 @@ wakeupReadFd = controlEventFd
    91 94
     
    
    92 95
     -- | Create the structure (usually a pipe) used for waking up the IO
    
    93 96
     -- manager thread from another thread.
    
    94
    -newControl :: Bool -> IO Control
    
    95
    -newControl shouldRegister = allocaArray 2 $ \fds -> do
    
    97
    +newControl :: IO Control
    
    98
    +newControl = allocaArray 2 $ \fds -> do
    
    96 99
       let createPipe = do
    
    97 100
             throwErrnoIfMinus1_ "pipe" $ c_pipe fds
    
    98 101
             rd <- peekElemOff fds 0
    
    ... ... @@ -108,10 +111,8 @@ newControl shouldRegister = allocaArray 2 $ \fds -> do
    108 111
       ev <- throwErrnoIfMinus1 "eventfd" $ c_eventfd 0 0
    
    109 112
       setNonBlockingFD ev True
    
    110 113
       setCloseOnExec ev
    
    111
    -  when shouldRegister $ c_setIOManagerWakeupFd ev
    
    112 114
     #else
    
    113 115
       (wake_rd, wake_wr) <- createPipe
    
    114
    -  when shouldRegister $ c_setIOManagerWakeupFd wake_wr
    
    115 116
     #endif
    
    116 117
       isDead <- newIORef False
    
    117 118
       return W { controlReadFd  = fromIntegral ctrl_rd
    
    ... ... @@ -122,25 +123,16 @@ newControl shouldRegister = allocaArray 2 $ \fds -> do
    122 123
                , wakeupReadFd   = fromIntegral wake_rd
    
    123 124
                , wakeupWriteFd  = fromIntegral wake_wr
    
    124 125
     #endif
    
    125
    -           , didRegisterWakeupFd = shouldRegister
    
    126
    +           , didRegisterWakeupFd = False
    
    126 127
                , controlIsDead  = isDead
    
    127 128
                }
    
    128 129
     
    
    129 130
     -- | Close the control structure used by the IO manager thread.
    
    130
    --- N.B. If this Control is the Control whose wakeup file was registered with
    
    131
    --- the RTS, then *BEFORE* the wakeup file is closed, we must call
    
    132
    --- c_setIOManagerWakeupFd (-1), so that the RTS does not try to use the wakeup
    
    133
    --- file after it has been closed.
    
    134
    ---
    
    135
    --- Note, however, that even if we do the above, this function is still racy
    
    136
    --- since we do not synchronize between here and ioManagerWakeup.
    
    137
    --- ioManagerWakeup ignores failures that arise from this case.
    
    138 131
     closeControl :: Control -> IO ()
    
    139 132
     closeControl w = do
    
    140 133
       _ <- atomicSwapIORef (controlIsDead w) True
    
    141 134
       _ <- c_close . fromIntegral . controlReadFd $ w
    
    142 135
       _ <- c_close . fromIntegral . controlWriteFd $ w
    
    143
    -  when (didRegisterWakeupFd w) $ c_setIOManagerWakeupFd (-1)
    
    144 136
     #if defined(HAVE_EVENTFD)
    
    145 137
       _ <- c_close . fromIntegral . controlEventFd $ w
    
    146 138
     #else
    
    ... ... @@ -248,11 +240,3 @@ foreign import ccall unsafe "sys/eventfd.h eventfd"
    248 240
     foreign import ccall unsafe "sys/eventfd.h eventfd_write"
    
    249 241
        c_eventfd_write :: CInt -> CULLong -> IO CInt
    
    250 242
     #endif
    251
    -
    
    252
    -#if defined(wasm32_HOST_ARCH)
    
    253
    -c_setIOManagerWakeupFd :: CInt -> IO ()
    
    254
    -c_setIOManagerWakeupFd _ = return ()
    
    255
    -#else
    
    256
    -foreign import ccall unsafe "setIOManagerWakeupFd"
    
    257
    -   c_setIOManagerWakeupFd :: CInt -> IO ()
    
    258
    -#endif

  • libraries/ghc-internal/src/GHC/Internal/Event/Manager.hs
    ... ... @@ -194,7 +194,7 @@ newWith :: Backend -> IO EventManager
    194 194
     newWith be = do
    
    195 195
       iofds <- fmap (listArray (0, callbackArraySize-1)) $
    
    196 196
                replicateM callbackArraySize (newMVar =<< IT.new 8)
    
    197
    -  ctrl <- newControl False
    
    197
    +  ctrl <- newControl
    
    198 198
       state <- newIORef Created
    
    199 199
       us <- newSource
    
    200 200
       _ <- mkWeakIORef state $ do
    

  • libraries/ghc-internal/src/GHC/Internal/Event/TimerManager.hs
    ... ... @@ -126,7 +126,7 @@ new = newWith =<< newDefaultBackend
    126 126
     newWith :: Backend -> IO TimerManager
    
    127 127
     newWith be = do
    
    128 128
       timeouts <- newIORef Q.empty
    
    129
    -  ctrl <- newControl True
    
    129
    +  ctrl <- newControl
    
    130 130
       state <- newIORef Created
    
    131 131
       us <- newSource
    
    132 132
       _ <- mkWeakIORef state $ do
    

  • rts/IOManager.c
    ... ... @@ -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
     
    
    ... ... @@ -376,6 +374,12 @@ void initCapabilityIOManager(CapIOManager *iomgr)
    376 374
     void freeCapabilityIOManager(CapIOManager *iomgr)
    
    377 375
     {
    
    378 376
         switch (iomgr_type) {
    
    377
    +#if defined(IOMGR_ENABLED_SELECT)
    
    378
    +        case IO_MANAGER_SELECT:
    
    379
    +            freeCapabilityIOManagerSelect(iomgr);
    
    380
    +            break;
    
    381
    +#endif
    
    382
    +
    
    379 383
     #if defined(IOMGR_ENABLED_POLL)
    
    380 384
             case IO_MANAGER_POLL:
    
    381 385
                 freeCapabilityIOManagerPoll(iomgr);
    
    ... ... @@ -555,42 +559,6 @@ exitIOManager(bool wait_threads)
    555 559
         }
    
    556 560
     }
    
    557 561
     
    
    558
    -/* Wakeup hook: called from the scheduler's wakeUpRts (currently only in
    
    559
    - * threaded mode).
    
    560
    - */
    
    561
    -void wakeupIOManager(void)
    
    562
    -{
    
    563
    -    switch (iomgr_type) {
    
    564
    -
    
    565
    -#if defined(IOMGR_ENABLED_MIO_POSIX)
    
    566
    -        case IO_MANAGER_MIO_POSIX:
    
    567
    -            /* MIO Posix implementation in posix/Signals.c */
    
    568
    -            ioManagerWakeup();
    
    569
    -            break;
    
    570
    -#endif
    
    571
    -#if defined(IOMGR_ENABLED_MIO_WIN32)
    
    572
    -        case IO_MANAGER_MIO_WIN32:
    
    573
    -            /* MIO Windows implementation in win32/ThrIOManager.c
    
    574
    -             * Yes, this is shared with the WinIO (threaded) impl.
    
    575
    -             */
    
    576
    -            ioManagerWakeup();
    
    577
    -            break;
    
    578
    -#endif
    
    579
    -#if defined(IOMGR_ENABLED_WINIO)
    
    580
    -        case IO_MANAGER_WINIO:
    
    581
    -#if defined(THREADED_RTS)
    
    582
    -            /* WinIO threaded implementation in win32/ThrIOManager.c
    
    583
    -             * Yes, this is shared with the MIO win32 impl.
    
    584
    -             */
    
    585
    -            ioManagerWakeup();
    
    586
    -#endif
    
    587
    -            break;
    
    588
    -#endif
    
    589
    -        default:
    
    590
    -            break;
    
    591
    -    }
    
    592
    -}
    
    593
    -
    
    594 562
     void markCapabilityIOManager(evac_fn evac, void *user, CapIOManager *iomgr)
    
    595 563
     {
    
    596 564
         switch (iomgr_type) {
    
    ... ... @@ -764,19 +732,20 @@ void pollCompletedTimeoutsOrIO(CapIOManager *iomgr)
    764 732
     }
    
    765 733
     
    
    766 734
     
    
    767
    -void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr)
    
    735
    +bool awaitCompletedTimeoutsOrIO(CapIOManager *iomgr)
    
    768 736
     {
    
    769 737
         debugTrace(DEBUG_iomanager, "waiting for completed IO or timeouts");
    
    738
    +    bool completed = true; // wait completed or interrupted?
    
    770 739
         switch (iomgr_type) {
    
    771 740
     #if defined(IOMGR_ENABLED_SELECT)
    
    772 741
             case IO_MANAGER_SELECT:
    
    773
    -          awaitCompletedTimeoutsOrIOSelect(iomgr, true);
    
    742
    +          completed = awaitCompletedTimeoutsOrIOSelect(iomgr, true);
    
    774 743
               break;
    
    775 744
     #endif
    
    776 745
     
    
    777 746
     #if defined(IOMGR_ENABLED_POLL)
    
    778 747
             case IO_MANAGER_POLL:
    
    779
    -          awaitCompletedTimeoutsOrIOPoll(iomgr);
    
    748
    +          completed = awaitCompletedTimeoutsOrIOPoll(iomgr);
    
    780 749
               break;
    
    781 750
     #endif
    
    782 751
     
    
    ... ... @@ -788,13 +757,56 @@ void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr)
    788 757
     #if defined(IOMGR_ENABLED_WINIO)
    
    789 758
             case IO_MANAGER_WINIO:
    
    790 759
     #endif
    
    791
    -          awaitCompletedTimeoutsOrIOWin32(iomgr->cap, true);
    
    760
    +          completed = awaitCompletedTimeoutsOrIOWin32(iomgr->cap, true);
    
    792 761
               break;
    
    793 762
     #endif
    
    794 763
             default:
    
    795
    -            barf("pollCompletedTimeoutsOrIO not implemented");
    
    764
    +            barf("awaitCompletedTimeoutsOrIO not implemented");
    
    765
    +    }
    
    766
    +    ASSERT(!emptyRunQueue(iomgr->cap) ||
    
    767
    +           getSchedState() != SCHED_RUNNING ||
    
    768
    +           !completed);
    
    769
    +    return completed;
    
    770
    +}
    
    771
    +
    
    772
    +
    
    773
    +/* Interrupt the I/O manager if it is blocked in awaitCompletedTimeoutsOrIO,
    
    774
    + * causing it to return early and return false.
    
    775
    + */
    
    776
    +void interruptIOManager(CapIOManager *iomgr)
    
    777
    +{
    
    778
    +    debugTrace(DEBUG_iomanager, "Interrupting the I/O manager...");
    
    779
    +    switch (iomgr_type) {
    
    780
    +
    
    781
    +#if defined(IOMGR_ENABLED_SELECT)
    
    782
    +        case IO_MANAGER_SELECT:
    
    783
    +            interruptIOManagerSelect(iomgr);
    
    784
    +            break;
    
    785
    +#endif
    
    786
    +
    
    787
    +#if defined(IOMGR_ENABLED_POLL)
    
    788
    +        case IO_MANAGER_POLL:
    
    789
    +            interruptIOManagerPoll(iomgr);
    
    790
    +            break;
    
    791
    +#endif
    
    792
    +
    
    793
    +#if defined(IOMGR_ENABLED_WIN32_LEGACY)
    
    794
    +        case IO_MANAGER_WIN32_LEGACY:
    
    795
    +            abandonRequestWait();
    
    796
    +            break;
    
    797
    +#endif
    
    798
    +
    
    799
    +#if defined(IOMGR_ENABLED_WINIO)
    
    800
    +        case IO_MANAGER_WINIO:
    
    801
    +            /* FIXME: no support yet for interrupting in WinIO I/O manager
    
    802
    +             * See issue #27403
    
    803
    +             */
    
    804
    +            break;
    
    805
    +#endif
    
    806
    +
    
    807
    +        default:
    
    808
    +            break;
    
    796 809
         }
    
    797
    -    ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING);
    
    798 810
     }
    
    799 811
     
    
    800 812
     
    

  • rts/IOManager.h
    ... ... @@ -306,23 +306,6 @@ void stopIOManager(void);
    306 306
     void exitIOManager(bool wait_threads);
    
    307 307
     
    
    308 308
     
    
    309
    -/* Wakeup hook: called from the scheduler's wakeUpRts (currently only in
    
    310
    - * threaded mode).
    
    311
    - *
    
    312
    - * The I/O manager can be blocked waiting on I/O or timers. Sometimes there are
    
    313
    - * other external events where we need to wake up the I/O manager and return
    
    314
    - * to the schedulr.
    
    315
    - *
    
    316
    - * At the moment, all the non-threaded I/O managers will do this automagically
    
    317
    - * since a signal will interrupt any waiting system calls, so at the moment
    
    318
    - * the implementation for the non-threaded I/O managers does nothing.
    
    319
    - *
    
    320
    - * For the I/O managers in threaded mode, this arranges to unblock the I/O
    
    321
    - * manager if it waa blocked waiting.
    
    322
    - */
    
    323
    -void wakeupIOManager(void);
    
    324
    -
    
    325
    -
    
    326 309
     /* GC hook: mark any per-capability GC roots the I/O manager uses.
    
    327 310
      */
    
    328 311
     void markCapabilityIOManager(evac_fn evac, void *user, CapIOManager *iomgr);
    
    ... ... @@ -382,20 +365,32 @@ bool anyPendingTimeoutsOrIO(CapIOManager *iomgr);
    382 365
      */
    
    383 366
     void pollCompletedTimeoutsOrIO(CapIOManager *iomgr);
    
    384 367
     
    
    385
    - /* If there are any completed I/O operations or expired timers, process the
    
    368
    +/* If there are any completed I/O operations or expired timers, process the
    
    386 369
      * completions as appropriate. If there are none, wait until I/O or a timer
    
    387 370
      * does complete (or we get a signal with a handler) and process the
    
    388 371
      * completions as appropriate.
    
    389 372
      *
    
    390
    - * Upon return this guarantees that the scheduler run queue is non-empty or
    
    391
    - * that the scheduler is no longer in the running state. Succinctly, the
    
    392
    - * post-condition is (!emptyRunQueue(cap) || getSchedState() != SCHED_RUNNING).
    
    373
    + * Upon returning true this guarantees that the scheduler run queue is
    
    374
    + * non-empty or that the scheduler is no longer in the running state.
    
    375
    + * Succinctly, the post-condition in the return true case is
    
    376
    + * (!emptyRunQueue(cap) || getSchedState() != SCHED_RUNNING).
    
    377
    + * A false result means the wait was interrupted by interruptIOManager, and
    
    378
    + * there is no post-condition in this case.
    
    393 379
      *
    
    394 380
      * This is only expected to be called if anyPendingTimeoutsOrIO() returns true,
    
    395 381
      * i.e. there actually is something to wait for.
    
    396 382
      *
    
    397 383
      * Called from schedule() both *before* and *after* scheduleDetectDeadlock().
    
    398 384
      */
    
    399
    -void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr);
    
    385
    +bool awaitCompletedTimeoutsOrIO(CapIOManager *iomgr);
    
    386
    +
    
    387
    +/* Interrupt the I/O manager if it is blocked in awaitCompletedTimeoutsOrIO,
    
    388
    + * causing it to return early.
    
    389
    + *
    
    390
    + * Its use is inherently concurrent and racy: the interrupt races against any
    
    391
    + * I/O or timer completion. This does not matter for the intended use case of
    
    392
    + * returning control to the scheduler.
    
    393
    + */
    
    394
    +void interruptIOManager(CapIOManager *iomgr);
    
    400 395
     
    
    401 396
     #include "EndPrivate.h"

  • 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 interrupt_fd_r, interrupt_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/RtsSymbols.c
    ... ... @@ -265,7 +265,6 @@ extern char **environ;
    265 265
     #define RTS_USER_SIGNALS_SYMBOLS        \
    
    266 266
        SymI_HasProto(setIOManagerControlFd) \
    
    267 267
        SymI_HasProto(setTimerManagerControlFd) \
    
    268
    -   SymI_HasProto(setIOManagerWakeupFd)  \
    
    269 268
        SymI_HasProto(blockUserSignals)      \
    
    270 269
        SymI_HasProto(unblockUserSignals)
    
    271 270
     #else
    

  • rts/include/rts/IOInterface.h
    ... ... @@ -33,7 +33,6 @@ void ioManagerFinished (void);
    33 33
     
    
    34 34
     void     setIOManagerControlFd   (uint32_t cap_no, int fd);
    
    35 35
     void     setTimerManagerControlFd(int fd);
    
    36
    -void     setIOManagerWakeupFd   (int fd);
    
    37 36
     
    
    38 37
     #endif
    
    39 38
     

  • rts/posix/MIO.c
    ... ... @@ -30,27 +30,16 @@
    30 30
     #include <unistd.h>
    
    31 31
     
    
    32 32
     // Here's the pipe into which we will send our signals
    
    33
    -static int io_manager_wakeup_fd = -1;
    
    34 33
     static int timer_manager_control_wr_fd = -1;
    
    35 34
     // TODO: Eliminate these globals. Put then into the CapIOManager, but the
    
    36 35
     // problem is these are shared across all caps, not per cap.
    
    37 36
     
    
    38
    -#define IO_MANAGER_WAKEUP 0xff
    
    39 37
     #define IO_MANAGER_DIE    0xfe
    
    40
    -#define IO_MANAGER_SYNC   0xfd
    
    41 38
     
    
    42 39
     void setTimerManagerControlFd(int fd) {
    
    43 40
         RELAXED_STORE(&timer_manager_control_wr_fd, fd);
    
    44 41
     }
    
    45 42
     
    
    46
    -void
    
    47
    -setIOManagerWakeupFd (int fd)
    
    48
    -{
    
    49
    -    // only called when THREADED_RTS, but unconditionally
    
    50
    -    // compiled here because GHC.Event.Control depends on it.
    
    51
    -    SEQ_CST_STORE(&io_manager_wakeup_fd, fd);
    
    52
    -}
    
    53
    -
    
    54 43
     #if defined(THREADED_RTS)
    
    55 44
     void timerManagerNotifySignal(int sig, siginfo_t *info)
    
    56 45
     {
    
    ... ... @@ -81,40 +70,6 @@ void timerManagerNotifySignal(int sig, siginfo_t *info)
    81 70
     #endif
    
    82 71
     
    
    83 72
     
    
    84
    -/* -----------------------------------------------------------------------------
    
    85
    - * Wake up at least one IO or timer manager HS thread.
    
    86
    - * -------------------------------------------------------------------------- */
    
    87
    -void
    
    88
    -ioManagerWakeup (void)
    
    89
    -{
    
    90
    -    int r;
    
    91
    -    const int wakeup_fd = SEQ_CST_LOAD(&io_manager_wakeup_fd);
    
    92
    -    // Wake up the IO Manager thread by sending a byte down its pipe
    
    93
    -    if (wakeup_fd >= 0) {
    
    94
    -#if defined(HAVE_EVENTFD)
    
    95
    -        StgWord64 n = (StgWord64)IO_MANAGER_WAKEUP;
    
    96
    -        r = write(wakeup_fd, (char *) &n, 8);
    
    97
    -#else
    
    98
    -        StgWord8 byte = (StgWord8)IO_MANAGER_WAKEUP;
    
    99
    -        r = write(wakeup_fd, &byte, 1);
    
    100
    -#endif
    
    101
    -        /* N.B. If the TimerManager is shutting down as we run this
    
    102
    -         * then there is a possibility that our first read of
    
    103
    -         * io_manager_wakeup_fd is non-negative, but before we get to the
    
    104
    -         * write the file is closed. If this occurs, io_manager_wakeup_fd
    
    105
    -         * will be written into with -1 (GHC.Event.Control does this prior
    
    106
    -         * to closing), so checking this allows us to distinguish this case.
    
    107
    -         * To ensure we observe the correct ordering, we declare the
    
    108
    -         * io_manager_wakeup_fd as volatile.
    
    109
    -         * Since this is not an error condition, we do not print the error
    
    110
    -         * message in this case.
    
    111
    -         */
    
    112
    -        if (r == -1 && SEQ_CST_LOAD(&io_manager_wakeup_fd) >= 0) {
    
    113
    -            sysErrorBelch("ioManagerWakeup: write");
    
    114
    -        }
    
    115
    -    }
    
    116
    -}
    
    117
    -
    
    118 73
     #if defined(THREADED_RTS)
    
    119 74
     void
    
    120 75
     ioManagerDie (void)
    
    ... ... @@ -157,7 +112,7 @@ ioManagerStart (void)
    157 112
     {
    
    158 113
         // Make sure the IO manager thread is running
    
    159 114
         Capability *cap;
    
    160
    -    if (SEQ_CST_LOAD(&timer_manager_control_wr_fd) < 0 || SEQ_CST_LOAD(&io_manager_wakeup_fd) < 0) {
    
    115
    +    if (SEQ_CST_LOAD(&timer_manager_control_wr_fd) < 0) {
    
    161 116
             cap = rts_lock();
    
    162 117
             ioManagerStartCap(&cap);
    
    163 118
             rts_unlock(cap);
    

  • rts/posix/MIO.h
    ... ... @@ -18,7 +18,6 @@
    18 18
     
    
    19 19
     /* Communicating with the IO manager thread (see GHC.Conc).
    
    20 20
      */
    
    21
    -void ioManagerWakeup (void);
    
    22 21
     #if defined(THREADED_RTS)
    
    23 22
     void ioManagerDie (void);
    
    24 23
     void ioManagerStart (void);
    

  • 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 interrupt_fd_r, interrupt_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 interrupt_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
    +interrupt_fd_r. This means the aiop_poll_table indicies match up exactly with
    
    129
    +the aiop_table, but still allows the full_poll_table to have an extra entry.
    
    130
    +
    
    120 131
     ******************************************************************************/
    
    121 132
     
    
    122 133
     /* Forward declarations */
    
    ... ... @@ -129,16 +140,25 @@ 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->interrupt_fd_r, &iomgr->interrupt_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->interrupt_fd_r,
    
    151
    +                                  .events  = POLLIN,
    
    152
    +                                  .revents = 0
    
    153
    +                                };
    
    154
    +    iomgr->aiop_poll_table = iomgr->full_poll_table+1; /* hence empty */
    
    134 155
     }
    
    135 156
     
    
    136 157
     
    
    137 158
     void freeCapabilityIOManagerPoll(CapIOManager *iomgr)
    
    138 159
     {
    
    139
    -    if (iomgr->aiop_poll_table) {
    
    140
    -        stgFree(iomgr->aiop_poll_table);
    
    141
    -    }
    
    160
    +    stgFree(iomgr->full_poll_table);
    
    161
    +    closeFdWakeup(iomgr->interrupt_fd_r, iomgr->interrupt_fd_w);
    
    142 162
     }
    
    143 163
     
    
    144 164
     
    
    ... ... @@ -283,7 +303,7 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop)
    283 303
     }
    
    284 304
     
    
    285 305
     
    
    286
    -static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
    
    306
    +static bool processIOCompletions(CapIOManager *iomgr, int ncompletions)
    
    287 307
     {
    
    288 308
         /* The scheme we use with poll is that we have a dense poll table, and a
    
    289 309
          * corresponding table that maps to the closure table index. The poll
    
    ... ... @@ -293,6 +313,19 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
    293 313
          */
    
    294 314
         debugTrace(DEBUG_iomanager, "processIOCompletions(ncompletions = %d)",
    
    295 315
                                     ncompletions);
    
    316
    +
    
    317
    +    bool interrupt;
    
    318
    +    /* If the interrupt_fd_r is ready, collect it */
    
    319
    +    if (iomgr->full_poll_table[0].revents) {
    
    320
    +        ASSERT(iomgr->full_poll_table[0].fd == iomgr->interrupt_fd_r);
    
    321
    +        collectFdWakeup(iomgr->interrupt_fd_r);
    
    322
    +        ncompletions--;
    
    323
    +        interrupt = true;
    
    324
    +        debugTrace(DEBUG_iomanager, "Received interrupt in poll I/O manager");
    
    325
    +    } else {
    
    326
    +        interrupt = false;
    
    327
    +    }
    
    328
    +
    
    296 329
         struct pollfd *aiop_poll_table = iomgr->aiop_poll_table;
    
    297 330
         int n = ncompletions;
    
    298 331
         int i = 0;
    
    ... ... @@ -345,11 +378,14 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
    345 378
                 i++;
    
    346 379
             }
    
    347 380
         }
    
    381
    +    return interrupt;
    
    348 382
     }
    
    349 383
     
    
    350 384
     
    
    351 385
     void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    
    352 386
     {
    
    387
    +    ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1);
    
    388
    +
    
    353 389
         if (!isEmptyTimeoutQueue(iomgr->timeout_queue)) {
    
    354 390
             Time now = getProcessElapsedTime();
    
    355 391
             processTimeoutCompletions(iomgr, now);
    
    ... ... @@ -357,20 +393,20 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    357 393
     
    
    358 394
         if (!isEmptyClosureTable(&iomgr->aiop_table)) {
    
    359 395
     
    
    360
    -        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table);
    
    396
    +        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1;
    
    361 397
     
    
    362 398
             /* Poll for I/O readiness, without waiting. */
    
    363 399
     #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
    
    364 400
             /* We could use poll here, since we use no timeout, but for
    
    365 401
                consistency we use the same syscall as at the other call site. */
    
    366 402
             struct timespec tv = (struct timespec) { .tv_sec = 0, .tv_nsec = 0 };
    
    367
    -        int res = ppoll(iomgr->aiop_poll_table, nfds, &tv, NULL);
    
    403
    +        int res = ppoll(iomgr->full_poll_table, nfds, &tv, NULL);
    
    368 404
     
    
    369 405
             debugTrace(DEBUG_iomanager,
    
    370 406
                        "ppoll(nfds = %d, timeout.sec = 0, timeout.nsec = 0) = %d",
    
    371 407
                        nfds, res);
    
    372 408
     #else
    
    373
    -        int res = poll(iomgr->aiop_poll_table, nfds, 0);
    
    409
    +        int res = poll(iomgr->full_poll_table, nfds, 0);
    
    374 410
     
    
    375 411
             debugTrace(DEBUG_iomanager,
    
    376 412
                        "poll(nfds = %d, timeout_ms = 0) = %d",
    
    ... ... @@ -396,8 +432,12 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    396 432
     }
    
    397 433
     
    
    398 434
     
    
    399
    -void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    
    435
    +bool awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    
    400 436
     {
    
    437
    +    bool interrupt = false; /* got woken up via interruptIOManager */
    
    438
    +
    
    439
    +    ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1);
    
    440
    +
    
    401 441
         /* Loop until we've woken up some threads. This loop is needed because the
    
    402 442
          * poll() timing isn't accurate, we sometimes sleep for a while but not
    
    403 443
          * long enough to wake up a thread in a threadDelay. Or we may need to
    
    ... ... @@ -430,9 +470,9 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    430 470
     #endif
    
    431 471
     
    
    432 472
             /* Check for I/O readiness, possibly waiting. */
    
    433
    -        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table);
    
    473
    +        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1;
    
    434 474
     #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
    
    435
    -        int res = ppoll(iomgr->aiop_poll_table, nfds, timeout_ns, NULL);
    
    475
    +        int res = ppoll(iomgr->full_poll_table, nfds, timeout_ns, NULL);
    
    436 476
     
    
    437 477
             debugTrace(DEBUG_iomanager,
    
    438 478
                        "ppoll(nfds = %d, timeout.sec = %d, timeout.nsec = %d) = %d",
    
    ... ... @@ -440,7 +480,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    440 480
                              timeout_ns == NULL ?  0 : timeout_ns->tv_nsec,
    
    441 481
                        res);
    
    442 482
     #else
    
    443
    -        int res = poll(iomgr->aiop_poll_table, nfds, timeout_ms);
    
    483
    +        int res = poll(iomgr->full_poll_table, nfds, timeout_ms);
    
    444 484
     
    
    445 485
             debugTrace(DEBUG_iomanager,
    
    446 486
                        "poll(nfds = %d, timeout_ms = %d) = %d",
    
    ... ... @@ -462,7 +502,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    462 502
             } else if (res > 0) {
    
    463 503
                 int ncompletions = res;
    
    464 504
                 ASSERT(ncompletions <= (int)nfds);
    
    465
    -            processIOCompletions(iomgr, ncompletions);
    
    505
    +            interrupt = processIOCompletions(iomgr, ncompletions);
    
    466 506
                 // FIXME: do we also need to check for timeout completions now?
    
    467 507
                 // we have a non-empty queue, but if !wait then we have also moved
    
    468 508
                 // on and so we sould check for timeouts.
    
    ... ... @@ -490,7 +530,9 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    490 530
             }
    
    491 531
     
    
    492 532
         } while (emptyRunQueue(iomgr->cap)
    
    533
    +         && !interrupt
    
    493 534
              && (getSchedState() == SCHED_RUNNING));
    
    535
    +    return !interrupt;
    
    494 536
     }
    
    495 537
     
    
    496 538
     static void reportPollError(int res, nfds_t nfds)
    
    ... ... @@ -509,6 +551,12 @@ static void reportPollError(int res, nfds_t nfds)
    509 551
     }
    
    510 552
     
    
    511 553
     
    
    554
    +void interruptIOManagerPoll(CapIOManager *iomgr)
    
    555
    +{
    
    556
    +    sendFdWakeup(iomgr->interrupt_fd_w);
    
    557
    +}
    
    558
    +
    
    559
    +
    
    512 560
     /* Helper function to double the size of the aiop_table and aiop_poll_table.
    
    513 561
      */
    
    514 562
     static bool enlargeTables(CapIOManager *iomgr)
    
    ... ... @@ -519,13 +567,17 @@ static bool enlargeTables(CapIOManager *iomgr)
    519 567
         bool ok = enlargeClosureTable(iomgr->cap, &iomgr->aiop_table, newcapacity);
    
    520 568
         if (RTS_UNLIKELY(!ok)) return false;
    
    521 569
     
    
    522
    -    /* Update the auxiliary aiop_poll_table to match */
    
    523
    -    struct pollfd *aiop_poll_table;
    
    524
    -    aiop_poll_table = stgReallocBytes(iomgr->aiop_poll_table,
    
    525
    -                                      sizeof(struct pollfd) * newcapacity,
    
    526
    -                                      "Poll.c: enlargeTables");
    
    527
    -    iomgr->aiop_poll_table = aiop_poll_table;
    
    570
    +    /* Update the auxiliary aiop_poll_table to match. The full_poll_table is
    
    571
    +     * one bigger than the aiop_poll_table, since it has an extra entry at the
    
    572
    +     * front for interrupt_fd_r, with no corresponding aiop. */
    
    573
    +    iomgr->full_poll_table =
    
    574
    +        stgReallocBytes(iomgr->full_poll_table,
    
    575
    +                        sizeof(struct pollfd) * (newcapacity+1),
    
    576
    +                        "Poll.c: enlargeTables");
    
    577
    +    iomgr->aiop_poll_table = iomgr->full_poll_table+1;
    
    578
    +
    
    528 579
         /* Initialise the new part of the aiop_poll_table */
    
    580
    +    struct pollfd *aiop_poll_table = iomgr->aiop_poll_table;
    
    529 581
         for (int i = oldcapacity; i < newcapacity; i++) {
    
    530 582
             aiop_poll_table[i] = (struct pollfd) {
    
    531 583
                                    .fd      = -1,
    

  • rts/posix/Poll.h
    ... ... @@ -32,7 +32,8 @@ void asyncIOCancelPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop);
    32 32
     /* Scheduler operations */
    
    33 33
     bool anyPendingTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    34 34
     void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    35
    -void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    35
    +bool awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    36
    +void interruptIOManagerPoll(CapIOManager *iomgr);
    
    36 37
     
    
    37 38
     #endif /* IOMGR_ENABLED_POLL */
    
    38 39
     
    

  • rts/posix/Select.c
    ... ... @@ -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->interrupt_fd_r, &iomgr->interrupt_fd_w);
    
    65
    +}
    
    66
    +
    
    67
    +void freeCapabilityIOManagerSelect(CapIOManager *iomgr)
    
    68
    +{
    
    69
    +    closeFdWakeup(iomgr->interrupt_fd_r, iomgr->interrupt_fd_w);
    
    70
    +}
    
    71
    +
    
    72
    +void interruptIOManagerSelect(CapIOManager *iomgr)
    
    73
    +{
    
    74
    +    sendFdWakeup(iomgr->interrupt_fd_w);
    
    75
    +}
    
    76
    +
    
    57 77
     /*
    
    58 78
      * Return the time since the program started, in LowResTime,
    
    59 79
      * rounded down.
    
    ... ... @@ -215,7 +235,7 @@ static enum FdState fdPollWriteState (int fd)
    215 235
      * not write handles.
    
    216 236
      *
    
    217 237
      */
    
    218
    -void
    
    238
    +bool
    
    219 239
     awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    
    220 240
     {
    
    221 241
         StgTSO *tso, *prev, *next;
    
    ... ... @@ -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 interrupt = false; /* got interrupted up via interruptIOManager */
    
    228 249
     
    
    229 250
         IF_DEBUG(scheduler,
    
    230 251
                  debugBelch("scheduler: checking for threads blocked on I/O");
    
    ... ... @@ -243,7 +264,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    243 264
     
    
    244 265
           now = getLowResTimeOfDay();
    
    245 266
           if (wakeUpSleepingThreads(iomgr, now)) {
    
    246
    -          return;
    
    267
    +          return true;
    
    247 268
           }
    
    248 269
     
    
    249 270
           /*
    
    ... ... @@ -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 interrupt fd */
    
    277
    +      {
    
    278
    +          int fd = iomgr->interrupt_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) {
    
    ... ... @@ -354,14 +382,14 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    354 382
     #if defined(RTS_USER_SIGNALS)
    
    355 383
               if (RtsFlags.MiscFlags.install_signal_handlers && signals_pending()) {
    
    356 384
                   startSignalHandlers(iomgr->cap);
    
    357
    -              return; /* still hold the lock */
    
    385
    +              return true; /* still hold the lock */
    
    358 386
               }
    
    359 387
     #endif
    
    360 388
     
    
    361 389
               /* we were interrupted, return to the scheduler immediately.
    
    362 390
                */
    
    363 391
               if (getSchedState() >= SCHED_INTERRUPTING) {
    
    364
    -              return; /* still hold the lock */
    
    392
    +              return true; /* still hold the lock */
    
    365 393
               }
    
    366 394
     
    
    367 395
               /* check for threads that need waking up
    
    ... ... @@ -372,10 +400,17 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    372 400
                * I/O and run them.
    
    373 401
                */
    
    374 402
               if (!emptyRunQueue(iomgr->cap)) {
    
    375
    -              return; /* still hold the lock */
    
    403
    +              return true; /* still hold the lock */
    
    376 404
               }
    
    377 405
           }
    
    378 406
     
    
    407
    +      /* If the interrupt_fd_r is ready, collect it */
    
    408
    +      if (FD_ISSET(iomgr->interrupt_fd_r, &rfd)) {
    
    409
    +          collectFdWakeup(iomgr->interrupt_fd_r);
    
    410
    +          interrupt = true;
    
    411
    +          debugTrace(DEBUG_iomanager, "Received interrupt in select I/O manager");
    
    412
    +      }
    
    413
    +
    
    379 414
           /* Step through the waiting queue, unblocking every thread that now has
    
    380 415
            * a file descriptor in a ready state.
    
    381 416
            */
    
    ... ... @@ -458,7 +493,9 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    458 493
           }
    
    459 494
     
    
    460 495
         } while (wait && getSchedState() == SCHED_RUNNING
    
    461
    -                  && emptyRunQueue(iomgr->cap));
    
    496
    +                  && emptyRunQueue(iomgr->cap)
    
    497
    +                  && !interrupt);
    
    498
    +    return !interrupt;
    
    462 499
     }
    
    463 500
     
    
    464 501
     #endif /* IOMGR_ENABLED_SELECT */

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

  • rts/win32/AsyncMIO.c
    ... ... @@ -221,8 +221,12 @@ shutdownAsyncIO(bool wait_threads)
    221 221
      * requests to make further progress. In the latter scenario,
    
    222 222
      * awaitRequests() will simply block waiting for worker threads
    
    223 223
      * to complete if the 'completedTable' is empty.
    
    224
    + *
    
    225
    + * The result reports if the wait completed successfully (typically with some
    
    226
    + * work available), or was interrupted by abandonRequestWait(), with true
    
    227
    + * meaning completed, and false meaning interrupted.
    
    224 228
      */
    
    225
    -int
    
    229
    +bool
    
    226 230
     awaitRequests(bool wait)
    
    227 231
     {
    
    228 232
     #if !defined(THREADED_RTS)
    
    ... ... @@ -246,7 +250,7 @@ start:
    246 250
     #endif
    
    247 251
             ) {
    
    248 252
             OS_RELEASE_LOCK(&queue_lock);
    
    249
    -        return 0;
    
    253
    +        return true;
    
    250 254
         }
    
    251 255
         if (completed_hw == 0) {
    
    252 256
             // empty table, drop lock and wait
    
    ... ... @@ -259,22 +263,24 @@ start:
    259 263
                     // a request was completed
    
    260 264
                     break;
    
    261 265
                 case WAIT_OBJECT_0 + 1:
    
    266
    +                // abandon_req_wait signaled, by abandonRequestWait()
    
    267
    +                return false;
    
    262 268
                 case WAIT_TIMEOUT:
    
    263 269
                     // timeout (unlikely) or told to abandon waiting
    
    264
    -                return 0;
    
    270
    +                return true;
    
    265 271
                 case WAIT_FAILED: {
    
    266 272
                     DWORD dw = GetLastError();
    
    267 273
                     fprintf(stderr, "awaitRequests: wait failed -- "
    
    268 274
                                     "error code: %lu\n", dw); fflush(stderr);
    
    269
    -                return 0;
    
    275
    +                return true;
    
    270 276
                 }
    
    271 277
                 default:
    
    272 278
                     fprintf(stderr, "awaitRequests: unexpected wait return "
    
    273 279
                                     "code %lu\n", dwRes); fflush(stderr);
    
    274
    -                return 0;
    
    280
    +                return true;
    
    275 281
                 }
    
    276 282
             } else {
    
    277
    -            return 0;
    
    283
    +            return true;
    
    278 284
             }
    
    279 285
             goto start;
    
    280 286
         } else {
    
    ... ... @@ -352,7 +358,7 @@ start:
    352 358
             completed_hw = 0;
    
    353 359
             ResetEvent(completed_req_event);
    
    354 360
             OS_RELEASE_LOCK(&queue_lock);
    
    355
    -        return 1;
    
    361
    +        return true;
    
    356 362
         }
    
    357 363
     #endif /* !THREADED_RTS */
    
    358 364
     }
    
    ... ... @@ -383,12 +389,6 @@ abandonRequestWait( void )
    383 389
         interruptIOManagerEvent ();
    
    384 390
     }
    
    385 391
     
    
    386
    -void
    
    387
    -resetAbandonRequestWait( void )
    
    388
    -{
    
    389
    -    ResetEvent(abandon_req_wait);
    
    390
    -}
    
    391
    -
    
    392 392
     #endif /* !defined(THREADED_RTS) */
    
    393 393
     
    
    394 394
     HsInt rts_EINTR(void)
    

  • rts/win32/AsyncMIO.h
    ... ... @@ -25,7 +25,7 @@ extern unsigned int addDoProcRequest(void* proc, void* param);
    25 25
     extern int  startupAsyncIO(void);
    
    26 26
     extern void shutdownAsyncIO(bool wait_threads);
    
    27 27
     
    
    28
    -extern int awaitRequests(bool wait);
    
    28
    +extern bool awaitRequests(bool wait);
    
    29 29
     
    
    30 30
     extern void abandonRequestWait(void);
    
    31 31
     extern void resetAbandonRequestWait(void);
    

  • rts/win32/AwaitEvent.c
    ... ... @@ -28,17 +28,21 @@
    28 28
     // Protected by sched_mutex.
    
    29 29
     static bool workerWaitingForRequests = false;
    
    30 30
     
    
    31
    -void
    
    31
    +bool
    
    32 32
     awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait)
    
    33 33
     {
    
    34
    +  bool interrupt = false;
    
    34 35
       do {
    
    35 36
         /* Try to de-queue completed IO requests
    
    36 37
          */
    
    37 38
         workerWaitingForRequests = true;
    
    38 39
         if (is_io_mng_native_p())
    
    39 40
           awaitAsyncRequests(wait);
    
    41
    +      /* FIXME: no support yet for interrupting in WinIO I/O manager
    
    42
    +       * See issue #27403
    
    43
    +       */
    
    40 44
         else
    
    41
    -      awaitRequests(wait);
    
    45
    +      interrupt = !awaitRequests(wait);
    
    42 46
         workerWaitingForRequests = false;
    
    43 47
     
    
    44 48
         // If a signal was raised, we need to service it
    
    ... ... @@ -47,11 +51,12 @@ awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait)
    47 51
         // does it and I'm feeling too paranoid to refactor it today --SDM
    
    48 52
         if (stg_pending_events != 0) {
    
    49 53
             startSignalHandlers(cap);
    
    50
    -        return;
    
    54
    +        // This will normally cause emptyRunQueue to become false and
    
    55
    +        // thus we will drop out of the loop.
    
    51 56
         }
    
    52 57
     
    
    53
    -    // The return value from awaitRequests() is a red herring: ignore
    
    54
    -    // it.  Return to the scheduler if !wait, or
    
    58
    +    // The return value from awaitRequests() reports if it was interrupted by
    
    59
    +    // abandonRequestWait(). Return to the scheduler if !wait, or
    
    55 60
         //
    
    56 61
         //  - we were interrupted
    
    57 62
         //  - the run-queue is now non- empty
    
    ... ... @@ -59,6 +64,8 @@ awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait)
    59 64
       } while (wait
    
    60 65
                && getSchedState() == SCHED_RUNNING
    
    61 66
                && emptyRunQueue(cap)
    
    67
    +           && !interrupt
    
    62 68
           );
    
    69
    +  return !interrupt;
    
    63 70
     }
    
    64 71
     #endif

  • rts/win32/AwaitEvent.h
    ... ... @@ -2,6 +2,6 @@
    2 2
     
    
    3 3
     #include "BeginPrivate.h"
    
    4 4
     
    
    5
    -void awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait);
    
    5
    +bool awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait);
    
    6 6
     
    
    7 7
     #include "EndPrivate.h"