Duncan Coutts pushed to branch wip/dcoutts/io-manager-tidy at Glasgow Haskell Compiler / GHC
Commits:
-
d37b0453
by Duncan Coutts at 2026-07-08T12:20:28+01:00
-
e15e8577
by Duncan Coutts at 2026-07-08T12:20:28+01:00
-
fbaa3157
by Duncan Coutts at 2026-07-08T12:20:28+01:00
-
6c27b769
by Duncan Coutts at 2026-07-08T12:20:28+01:00
-
2daa8f5e
by Duncan Coutts at 2026-07-08T12:26:04+01:00
-
79d1cf40
by Duncan Coutts at 2026-07-08T12:26:04+01:00
-
fd1eca2a
by Duncan Coutts at 2026-07-08T12:26:04+01:00
-
4ecf19cf
by Duncan Coutts at 2026-07-08T12:26:04+01:00
-
7b59929c
by Duncan Coutts at 2026-07-08T12:26:05+01:00
-
03cd3ca9
by Duncan Coutts at 2026-07-08T12:26:05+01:00
-
d9414b2c
by Duncan Coutts at 2026-07-08T12:26:05+01:00
28 changed files:
- libraries/ghc-internal/src/GHC/Internal/Event/Control.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Manager.hs
- libraries/ghc-internal/src/GHC/Internal/Event/TimerManager.hs
- rts/Capability.c
- rts/IOManager.c
- rts/IOManager.h
- rts/RtsStartup.c
- rts/RtsSymbols.c
- rts/Schedule.c
- rts/Schedule.h
- rts/include/rts/IOInterface.h
- rts/include/rts/storage/Closures.h
- rts/include/rts/storage/TSO.h
- + rts/posix/MIO.c
- + rts/posix/MIO.h
- rts/posix/Poll.c
- rts/posix/Poll.h
- rts/posix/Signals.c
- rts/posix/Signals.h
- rts/rts.cabal
- rts/win32/AsyncMIO.h
- rts/win32/AsyncWinIO.h
- rts/win32/AwaitEvent.h
- rts/win32/ConsoleHandler.h
- rts/win32/MIOManager.h
- rts/win32/ThrIOManager.h
- rts/win32/WorkQueue.h
- rts/win32/veh_excn.h
Changes:
| ... | ... | @@ -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 |
| ... | ... | @@ -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
|
| ... | ... | @@ -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
|
| ... | ... | @@ -1293,6 +1293,8 @@ shutdownCapabilities(Task *task, bool safe) |
| 1293 | 1293 | static void
|
| 1294 | 1294 | freeCapability (Capability *cap)
|
| 1295 | 1295 | {
|
| 1296 | + freeCapabilityIOManager(cap->iomgr);
|
|
| 1297 | + stgFree(cap->iomgr);
|
|
| 1296 | 1298 | stgFree(cap->mut_lists);
|
| 1297 | 1299 | stgFree(cap->saved_mut_lists);
|
| 1298 | 1300 | if (cap->current_segments) {
|
| ... | ... | @@ -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 | |
| ... | ... | @@ -373,11 +373,25 @@ void initCapabilityIOManager(CapIOManager *iomgr) |
| 373 | 373 | }
|
| 374 | 374 | |
| 375 | 375 | |
| 376 | +void freeCapabilityIOManager(CapIOManager *iomgr)
|
|
| 377 | +{
|
|
| 378 | + switch (iomgr_type) {
|
|
| 379 | +#if defined(IOMGR_ENABLED_POLL)
|
|
| 380 | + case IO_MANAGER_POLL:
|
|
| 381 | + freeCapabilityIOManagerPoll(iomgr);
|
|
| 382 | + break;
|
|
| 383 | +#endif
|
|
| 384 | + default:
|
|
| 385 | + break;
|
|
| 386 | + }
|
|
| 387 | +}
|
|
| 388 | + |
|
| 389 | + |
|
| 376 | 390 | /* Called late in the RTS initialisation
|
| 377 | 391 | */
|
| 378 | -void initIOManager(void)
|
|
| 392 | +void startIOManager(void)
|
|
| 379 | 393 | {
|
| 380 | - debugTrace(DEBUG_iomanager, "initialising %s I/O manager", showIOManager());
|
|
| 394 | + debugTrace(DEBUG_iomanager, "starting %s I/O manager", showIOManager());
|
|
| 381 | 395 | |
| 382 | 396 | switch (iomgr_type) {
|
| 383 | 397 | |
| ... | ... | @@ -441,7 +455,7 @@ void initIOManager(void) |
| 441 | 455 | /* Called from forkProcess in the child process on the surviving capability.
|
| 442 | 456 | */
|
| 443 | 457 | void
|
| 444 | -initIOManagerAfterFork(CapIOManager *iomgr, Capability **pcap)
|
|
| 458 | +restartIOManager(CapIOManager *iomgr, Capability **pcap)
|
|
| 445 | 459 | {
|
| 446 | 460 | |
| 447 | 461 | switch (iomgr_type) {
|
| ... | ... | @@ -541,42 +555,6 @@ exitIOManager(bool wait_threads) |
| 541 | 555 | }
|
| 542 | 556 | }
|
| 543 | 557 | |
| 544 | -/* Wakeup hook: called from the scheduler's wakeUpRts (currently only in
|
|
| 545 | - * threaded mode).
|
|
| 546 | - */
|
|
| 547 | -void wakeupIOManager(void)
|
|
| 548 | -{
|
|
| 549 | - switch (iomgr_type) {
|
|
| 550 | - |
|
| 551 | -#if defined(IOMGR_ENABLED_MIO_POSIX)
|
|
| 552 | - case IO_MANAGER_MIO_POSIX:
|
|
| 553 | - /* MIO Posix implementation in posix/Signals.c */
|
|
| 554 | - ioManagerWakeup();
|
|
| 555 | - break;
|
|
| 556 | -#endif
|
|
| 557 | -#if defined(IOMGR_ENABLED_MIO_WIN32)
|
|
| 558 | - case IO_MANAGER_MIO_WIN32:
|
|
| 559 | - /* MIO Windows implementation in win32/ThrIOManager.c
|
|
| 560 | - * Yes, this is shared with the WinIO (threaded) impl.
|
|
| 561 | - */
|
|
| 562 | - ioManagerWakeup();
|
|
| 563 | - break;
|
|
| 564 | -#endif
|
|
| 565 | -#if defined(IOMGR_ENABLED_WINIO)
|
|
| 566 | - case IO_MANAGER_WINIO:
|
|
| 567 | -#if defined(THREADED_RTS)
|
|
| 568 | - /* WinIO threaded implementation in win32/ThrIOManager.c
|
|
| 569 | - * Yes, this is shared with the MIO win32 impl.
|
|
| 570 | - */
|
|
| 571 | - ioManagerWakeup();
|
|
| 572 | -#endif
|
|
| 573 | - break;
|
|
| 574 | -#endif
|
|
| 575 | - default:
|
|
| 576 | - break;
|
|
| 577 | - }
|
|
| 578 | -}
|
|
| 579 | - |
|
| 580 | 558 | void markCapabilityIOManager(evac_fn evac, void *user, CapIOManager *iomgr)
|
| 581 | 559 | {
|
| 582 | 560 | switch (iomgr_type) {
|
| ... | ... | @@ -243,11 +243,33 @@ CapIOManager *allocCapabilityIOManager(Capability *cap); |
| 243 | 243 | */
|
| 244 | 244 | void initCapabilityIOManager(CapIOManager *iomgr);
|
| 245 | 245 | |
| 246 | +/* When shutting down a capability, or after forkProcess, free the resources
|
|
| 247 | + * held by a CapIOManager to put it back into a state in which either it can be
|
|
| 248 | + * re-initialised using initCapabilityIOManager, or the whole structure freed.
|
|
| 249 | + *
|
|
| 250 | + * Note that this does not free the CapIOManager structure itself, just the
|
|
| 251 | + * contents.
|
|
| 252 | + *
|
|
| 253 | + * This is used during capability shutdown, during RTS shutdown. It is not used
|
|
| 254 | + * when reducing the number of capabilities. Capabilities are disabled rather
|
|
| 255 | + * than freed entirely: the I/O manager keeps running but threads that become
|
|
| 256 | + * runnable are migrated away.
|
|
| 257 | + *
|
|
| 258 | + * It is also used after forkProcess.
|
|
| 259 | + */
|
|
| 260 | +void freeCapabilityIOManager(CapIOManager *iomgr);
|
|
| 261 | + |
|
| 262 | +/* CapIOManager life cycle:
|
|
| 263 | + *
|
|
| 264 | + * alloc -> init -> free -> free struct
|
|
| 265 | + * ^ |
|
|
| 266 | + * +--------+
|
|
| 267 | + */
|
|
| 246 | 268 | |
| 247 | 269 | /* Init hook: called from hs_init_ghc, very late in the startup after almost
|
| 248 | 270 | * everything else is done.
|
| 249 | 271 | */
|
| 250 | -void initIOManager(void);
|
|
| 272 | +void startIOManager(void);
|
|
| 251 | 273 | |
| 252 | 274 | |
| 253 | 275 | /* Init hook: called from forkProcess in the child process on the surviving
|
| ... | ... | @@ -256,8 +278,8 @@ void initIOManager(void); |
| 256 | 278 | * This is synchronous and can run Haskell code, so can change the given cap.
|
| 257 | 279 | * TODO: it would make for a cleaner API here if this were made asynchronous.
|
| 258 | 280 | */
|
| 259 | -void initIOManagerAfterFork(CapIOManager *iomgr,
|
|
| 260 | - /* inout */ Capability **pcap);
|
|
| 281 | +void restartIOManager(CapIOManager *iomgr,
|
|
| 282 | + /* inout */ Capability **pcap);
|
|
| 261 | 283 | |
| 262 | 284 | /* TODO: rationalise initIOManager and initIOManagerAfterFork into a single
|
| 263 | 285 | per-capability init function.
|
| ... | ... | @@ -284,23 +306,6 @@ void stopIOManager(void); |
| 284 | 306 | void exitIOManager(bool wait_threads);
|
| 285 | 307 | |
| 286 | 308 | |
| 287 | -/* Wakeup hook: called from the scheduler's wakeUpRts (currently only in
|
|
| 288 | - * threaded mode).
|
|
| 289 | - *
|
|
| 290 | - * The I/O manager can be blocked waiting on I/O or timers. Sometimes there are
|
|
| 291 | - * other external events where we need to wake up the I/O manager and return
|
|
| 292 | - * to the schedulr.
|
|
| 293 | - *
|
|
| 294 | - * At the moment, all the non-threaded I/O managers will do this automagically
|
|
| 295 | - * since a signal will interrupt any waiting system calls, so at the moment
|
|
| 296 | - * the implementation for the non-threaded I/O managers does nothing.
|
|
| 297 | - *
|
|
| 298 | - * For the I/O managers in threaded mode, this arranges to unblock the I/O
|
|
| 299 | - * manager if it waa blocked waiting.
|
|
| 300 | - */
|
|
| 301 | -void wakeupIOManager(void);
|
|
| 302 | - |
|
| 303 | - |
|
| 304 | 309 | /* GC hook: mark any per-capability GC roots the I/O manager uses.
|
| 305 | 310 | */
|
| 306 | 311 | void markCapabilityIOManager(evac_fn evac, void *user, CapIOManager *iomgr);
|
| ... | ... | @@ -427,7 +427,7 @@ hs_init_ghc(int *argc, char **argv[], RtsConfig rts_config) |
| 427 | 427 | }
|
| 428 | 428 | #endif
|
| 429 | 429 | |
| 430 | - initIOManager();
|
|
| 430 | + startIOManager();
|
|
| 431 | 431 | |
| 432 | 432 | x86_init_fpu();
|
| 433 | 433 |
| ... | ... | @@ -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
|
| ... | ... | @@ -175,6 +175,11 @@ static void deleteAllThreads (void); |
| 175 | 175 | static void deleteThread_(StgTSO *tso);
|
| 176 | 176 | #endif
|
| 177 | 177 | |
| 178 | +#if defined(FORKPROCESS_PRIMOP_SUPPORTED)
|
|
| 179 | +static void truncateRunQueue(Capability *cap);
|
|
| 180 | +#endif
|
|
| 181 | +static StgTSO *popRunQueue (Capability *cap);
|
|
| 182 | + |
|
| 178 | 183 | /* ---------------------------------------------------------------------------
|
| 179 | 184 | Main scheduling loop.
|
| 180 | 185 | |
| ... | ... | @@ -592,38 +597,6 @@ run_thread: |
| 592 | 597 | } /* end of while() */
|
| 593 | 598 | }
|
| 594 | 599 | |
| 595 | -/* -----------------------------------------------------------------------------
|
|
| 596 | - * Run queue operations
|
|
| 597 | - * -------------------------------------------------------------------------- */
|
|
| 598 | - |
|
| 599 | -static void
|
|
| 600 | -removeFromRunQueue (Capability *cap, StgTSO *tso)
|
|
| 601 | -{
|
|
| 602 | - if (tso->block_info.prev == END_TSO_QUEUE) {
|
|
| 603 | - ASSERT(cap->run_queue_hd == tso);
|
|
| 604 | - cap->run_queue_hd = tso->_link;
|
|
| 605 | - } else {
|
|
| 606 | - setTSOLink(cap, tso->block_info.prev, tso->_link);
|
|
| 607 | - }
|
|
| 608 | - if (tso->_link == END_TSO_QUEUE) {
|
|
| 609 | - ASSERT(cap->run_queue_tl == tso);
|
|
| 610 | - cap->run_queue_tl = tso->block_info.prev;
|
|
| 611 | - } else {
|
|
| 612 | - setTSOPrev(cap, tso->_link, tso->block_info.prev);
|
|
| 613 | - }
|
|
| 614 | - tso->_link = tso->block_info.prev = END_TSO_QUEUE;
|
|
| 615 | - cap->n_run_queue--;
|
|
| 616 | - |
|
| 617 | - IF_DEBUG(sanity, checkRunQueue(cap));
|
|
| 618 | -}
|
|
| 619 | - |
|
| 620 | -void
|
|
| 621 | -promoteInRunQueue (Capability *cap, StgTSO *tso)
|
|
| 622 | -{
|
|
| 623 | - removeFromRunQueue(cap, tso);
|
|
| 624 | - pushOnRunQueue(cap, tso);
|
|
| 625 | -}
|
|
| 626 | - |
|
| 627 | 600 | /* -----------------------------------------------------------------------------
|
| 628 | 601 | * scheduleFindWork()
|
| 629 | 602 | *
|
| ... | ... | @@ -2199,7 +2172,15 @@ forkProcess(HsStablePtr *entry |
| 2199 | 2172 | // bound threads for which the corresponding Task does not
|
| 2200 | 2173 | // exist.
|
| 2201 | 2174 | truncateRunQueue(cap);
|
| 2202 | - cap->n_run_queue = 0;
|
|
| 2175 | + |
|
| 2176 | + // Reset and re-initialise the capability's I/O manager,
|
|
| 2177 | + // to get the I/O manager ready again.
|
|
| 2178 | + //
|
|
| 2179 | + // Any threads waiting on I/O or timers should have been
|
|
| 2180 | + // removed from I/O manager queues by deleteThread_ above.
|
|
| 2181 | + // TODO: but we could assert that here.
|
|
| 2182 | + freeCapabilityIOManager(cap->iomgr);
|
|
| 2183 | + initCapabilityIOManager(cap->iomgr);
|
|
| 2203 | 2184 | |
| 2204 | 2185 | // Any suspended C-calling Tasks are no more, their OS threads
|
| 2205 | 2186 | // don't exist now:
|
| ... | ... | @@ -2216,7 +2197,7 @@ forkProcess(HsStablePtr *entry |
| 2216 | 2197 | cap->n_returning_tasks = 0;
|
| 2217 | 2198 | #endif
|
| 2218 | 2199 | |
| 2219 | - // Release all caps except 0, we'll use that for starting
|
|
| 2200 | + // Release all caps except 0, we'll use that for restarting
|
|
| 2220 | 2201 | // the IO manager and running the client action below.
|
| 2221 | 2202 | if (cap->no != 0) {
|
| 2222 | 2203 | task->cap = cap;
|
| ... | ... | @@ -2240,7 +2221,7 @@ forkProcess(HsStablePtr *entry |
| 2240 | 2221 | // like startup event, capabilities, process info etc
|
| 2241 | 2222 | traceTaskCreate(task, cap);
|
| 2242 | 2223 | |
| 2243 | - initIOManagerAfterFork(cap->iomgr, &cap);
|
|
| 2224 | + restartIOManager(cap->iomgr, &cap);
|
|
| 2244 | 2225 | |
| 2245 | 2226 | // start timer after the IOManager is initialized
|
| 2246 | 2227 | // (the idle GC may wake up the IOManager)
|
| ... | ... | @@ -2339,6 +2320,10 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS) |
| 2339 | 2320 | // the capability; we don't have to worry about GC data
|
| 2340 | 2321 | // structures, the nursery, etc.
|
| 2341 | 2322 | //
|
| 2323 | + // This approach also handles threads blocked on I/O. Such threads
|
|
| 2324 | + // remain blocked, and when I/O completes and threads become runnable
|
|
| 2325 | + // then they are migrated away.
|
|
| 2326 | + //
|
|
| 2342 | 2327 | for (n = new_n_capabilities; n < enabled_capabilities; n++) {
|
| 2343 | 2328 | getCapability(n)->disabled = true;
|
| 2344 | 2329 | traceCapDisable(getCapability(n));
|
| ... | ... | @@ -3016,7 +3001,7 @@ pushOnRunQueue (Capability *cap, StgTSO *tso) |
| 3016 | 3001 | cap->n_run_queue++;
|
| 3017 | 3002 | }
|
| 3018 | 3003 | |
| 3019 | -StgTSO *popRunQueue (Capability *cap)
|
|
| 3004 | +static StgTSO *popRunQueue (Capability *cap)
|
|
| 3020 | 3005 | {
|
| 3021 | 3006 | ASSERT(cap->n_run_queue > 0);
|
| 3022 | 3007 | StgTSO *t = cap->run_queue_hd;
|
| ... | ... | @@ -3036,6 +3021,45 @@ StgTSO *popRunQueue (Capability *cap) |
| 3036 | 3021 | return t;
|
| 3037 | 3022 | }
|
| 3038 | 3023 | |
| 3024 | +#if defined(FORKPROCESS_PRIMOP_SUPPORTED)
|
|
| 3025 | +static void truncateRunQueue(Capability *cap)
|
|
| 3026 | +{
|
|
| 3027 | + // Can only be called by the task owning the capability.
|
|
| 3028 | + TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_hd, "truncateRunQueue");
|
|
| 3029 | + TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_tl, "truncateRunQueue");
|
|
| 3030 | + TSAN_ANNOTATE_BENIGN_RACE(&cap->n_run_queue, "truncateRunQueue");
|
|
| 3031 | + cap->run_queue_hd = END_TSO_QUEUE;
|
|
| 3032 | + cap->run_queue_tl = END_TSO_QUEUE;
|
|
| 3033 | + cap->n_run_queue = 0;
|
|
| 3034 | +}
|
|
| 3035 | +#endif
|
|
| 3036 | + |
|
| 3037 | +static void removeFromRunQueue (Capability *cap, StgTSO *tso)
|
|
| 3038 | +{
|
|
| 3039 | + if (tso->block_info.prev == END_TSO_QUEUE) {
|
|
| 3040 | + ASSERT(cap->run_queue_hd == tso);
|
|
| 3041 | + cap->run_queue_hd = tso->_link;
|
|
| 3042 | + } else {
|
|
| 3043 | + setTSOLink(cap, tso->block_info.prev, tso->_link);
|
|
| 3044 | + }
|
|
| 3045 | + if (tso->_link == END_TSO_QUEUE) {
|
|
| 3046 | + ASSERT(cap->run_queue_tl == tso);
|
|
| 3047 | + cap->run_queue_tl = tso->block_info.prev;
|
|
| 3048 | + } else {
|
|
| 3049 | + setTSOPrev(cap, tso->_link, tso->block_info.prev);
|
|
| 3050 | + }
|
|
| 3051 | + tso->_link = tso->block_info.prev = END_TSO_QUEUE;
|
|
| 3052 | + cap->n_run_queue--;
|
|
| 3053 | + |
|
| 3054 | + IF_DEBUG(sanity, checkRunQueue(cap));
|
|
| 3055 | +}
|
|
| 3056 | + |
|
| 3057 | +void promoteInRunQueue (Capability *cap, StgTSO *tso)
|
|
| 3058 | +{
|
|
| 3059 | + removeFromRunQueue(cap, tso);
|
|
| 3060 | + pushOnRunQueue(cap, tso);
|
|
| 3061 | +}
|
|
| 3062 | + |
|
| 3039 | 3063 | |
| 3040 | 3064 | /* -----------------------------------------------------------------------------
|
| 3041 | 3065 | raiseExceptionHelper
|
| ... | ... | @@ -164,10 +164,6 @@ void appendToRunQueue (Capability *cap, StgTSO *tso); |
| 164 | 164 | */
|
| 165 | 165 | void pushOnRunQueue (Capability *cap, StgTSO *tso);
|
| 166 | 166 | |
| 167 | -/* Pop the first thread off the runnable queue.
|
|
| 168 | - */
|
|
| 169 | -StgTSO *popRunQueue (Capability *cap);
|
|
| 170 | - |
|
| 171 | 167 | INLINE_HEADER StgTSO *
|
| 172 | 168 | peekRunQueue (Capability *cap)
|
| 173 | 169 | {
|
| ... | ... | @@ -184,18 +180,6 @@ emptyRunQueue(Capability *cap) |
| 184 | 180 | return cap->n_run_queue == 0;
|
| 185 | 181 | }
|
| 186 | 182 | |
| 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 | 183 | #endif /* !IN_STG_CODE */
|
| 200 | 184 | |
| 201 | 185 | #include "EndPrivate.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 |
| ... | ... | @@ -829,11 +829,11 @@ typedef struct { |
| 829 | 829 | // here due to portability concerns for this C bitfield.
|
| 830 | 830 | uint16_t notify_type: 2;
|
| 831 | 831 | |
| 832 | - // The outcome:
|
|
| 833 | - // 0: still in-progress, no detail
|
|
| 834 | - // 1: success, result field contains the result code
|
|
| 835 | - // 2: failed, error field contains the error code
|
|
| 836 | - // 3: cancelled, no detail
|
|
| 832 | + // The outcome (see enum IOOpOutcome):
|
|
| 833 | + // 0: IOOpOutcomeInFlight: still in-progress, no further detail.
|
|
| 834 | + // 1: IOOpOutcomeSuccess: result field contains the result code.
|
|
| 835 | + // 2: IOOpOutcomeFailed: error field contains the error code.
|
|
| 836 | + // 3: IOOpOutcomeCancelled: cancelled, no further detail.
|
|
| 837 | 837 | uint16_t outcome: 2;
|
| 838 | 838 | |
| 839 | 839 | // 12 bits going spare!
|
| ... | ... | @@ -48,14 +48,16 @@ typedef union { |
| 48 | 48 | StgAsyncIOOp *aiop;
|
| 49 | 49 | StgTimeoutQueue *timeout;
|
| 50 | 50 | #if defined(mingw32_HOST_OS)
|
| 51 | + // Only used by the Legacy Win32 I/O manager: the async request id for the
|
|
| 52 | + // operation.
|
|
| 51 | 53 | StgWord async_reqID;
|
| 52 | 54 | #endif
|
| 53 | 55 | #if !defined(THREADED_RTS)
|
| 54 | 56 | StgWord target;
|
| 55 | - // Only for the non-threaded RTS: the target time for a thread
|
|
| 56 | - // blocked in threadDelay, in units of 1ms. This is a
|
|
| 57 | - // compromise: we don't want to take up much space in the TSO. If
|
|
| 58 | - // you want better resolution for threadDelay, use -threaded.
|
|
| 57 | + // Only for the legacy select I/O manager: the target time for a thread
|
|
| 58 | + // blocked in threadDelay, in units of 1ms. This is a compromise: we don't
|
|
| 59 | + // want to take up much space in the TSO. If you want better resolution
|
|
| 60 | + // for threadDelay, use *literally any* other I/O manager.
|
|
| 59 | 61 | #endif
|
| 60 | 62 | } StgTSOBlockInfo;
|
| 61 | 63 |
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team, 1998-2005
|
|
| 4 | + *
|
|
| 5 | + * The MIO I/O manager RTS stub.
|
|
| 6 | + *
|
|
| 7 | + * This I/O manager lives primarily in Haskell in the ghc-internal package.
|
|
| 8 | + * This file implementes the required subset of the RTS's internal I/O manager
|
|
| 9 | + * API.
|
|
| 10 | + *
|
|
| 11 | + * ---------------------------------------------------------------------------*/
|
|
| 12 | + |
|
| 13 | +#include "rts/PosixSource.h"
|
|
| 14 | +#include "Rts.h"
|
|
| 15 | + |
|
| 16 | +#include "Schedule.h"
|
|
| 17 | +#include "RtsUtils.h"
|
|
| 18 | +#include "Prelude.h"
|
|
| 19 | +#include "ThreadLabels.h"
|
|
| 20 | + |
|
| 21 | +#include "MIO.h"
|
|
| 22 | +#include "IOManager.h"
|
|
| 23 | +#include "IOManagerInternals.h"
|
|
| 24 | + |
|
| 25 | +#if defined(HAVE_ERRNO_H)
|
|
| 26 | +# include <errno.h>
|
|
| 27 | +#endif
|
|
| 28 | + |
|
| 29 | +#include <stdlib.h>
|
|
| 30 | +#include <unistd.h>
|
|
| 31 | + |
|
| 32 | +// Here's the pipe into which we will send our signals
|
|
| 33 | +static int timer_manager_control_wr_fd = -1;
|
|
| 34 | +// TODO: Eliminate these globals. Put then into the CapIOManager, but the
|
|
| 35 | +// problem is these are shared across all caps, not per cap.
|
|
| 36 | + |
|
| 37 | +#define IO_MANAGER_DIE 0xfe
|
|
| 38 | + |
|
| 39 | +void setTimerManagerControlFd(int fd) {
|
|
| 40 | + RELAXED_STORE(&timer_manager_control_wr_fd, fd);
|
|
| 41 | +}
|
|
| 42 | + |
|
| 43 | +#if defined(THREADED_RTS)
|
|
| 44 | +void timerManagerNotifySignal(int sig, siginfo_t *info)
|
|
| 45 | +{
|
|
| 46 | + StgWord8 buf[sizeof(siginfo_t) + 1];
|
|
| 47 | + int r;
|
|
| 48 | + |
|
| 49 | + buf[0] = sig;
|
|
| 50 | + if (info == NULL) {
|
|
| 51 | + // info may be NULL on Solaris (see #3790)
|
|
| 52 | + memset(buf+1, 0, sizeof(siginfo_t));
|
|
| 53 | + } else {
|
|
| 54 | + memcpy(buf+1, info, sizeof(siginfo_t));
|
|
| 55 | + }
|
|
| 56 | + |
|
| 57 | + int timer_control_fd = RELAXED_LOAD(&timer_manager_control_wr_fd);
|
|
| 58 | + if (0 <= timer_control_fd)
|
|
| 59 | + {
|
|
| 60 | + r = write(timer_control_fd, buf, sizeof(siginfo_t)+1);
|
|
| 61 | + if (r == -1 && errno == EAGAIN) {
|
|
| 62 | + errorBelch("lost signal due to full pipe: %d\n", sig);
|
|
| 63 | + }
|
|
| 64 | + }
|
|
| 65 | + |
|
| 66 | + // If the IO manager hasn't told us what the FD of the write end
|
|
| 67 | + // of its pipe is, there's not much we can do here, so just ignore
|
|
| 68 | + // the signal..
|
|
| 69 | +}
|
|
| 70 | +#endif
|
|
| 71 | + |
|
| 72 | + |
|
| 73 | +#if defined(THREADED_RTS)
|
|
| 74 | +void
|
|
| 75 | +ioManagerDie (void)
|
|
| 76 | +{
|
|
| 77 | + StgWord8 byte = (StgWord8)IO_MANAGER_DIE;
|
|
| 78 | + uint32_t i;
|
|
| 79 | + int r;
|
|
| 80 | + |
|
| 81 | + {
|
|
| 82 | + // Shut down timer manager
|
|
| 83 | + const int fd = RELAXED_LOAD(&timer_manager_control_wr_fd);
|
|
| 84 | + if (0 <= fd) {
|
|
| 85 | + r = write(fd, &byte, 1);
|
|
| 86 | + if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
|
|
| 87 | + RELAXED_STORE(&timer_manager_control_wr_fd, -1);
|
|
| 88 | + }
|
|
| 89 | + }
|
|
| 90 | + |
|
| 91 | + {
|
|
| 92 | + // Shut down IO managers
|
|
| 93 | + for (i=0; i < getNumCapabilities(); i++) {
|
|
| 94 | + const int fd = RELAXED_LOAD(&getCapability(i)->iomgr->control_fd);
|
|
| 95 | + if (0 <= fd) {
|
|
| 96 | + r = write(fd, &byte, 1);
|
|
| 97 | + if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
|
|
| 98 | + RELAXED_STORE(&getCapability(i)->iomgr->control_fd, -1);
|
|
| 99 | + }
|
|
| 100 | + }
|
|
| 101 | + }
|
|
| 102 | +}
|
|
| 103 | + |
|
| 104 | +void
|
|
| 105 | +ioManagerStartCap (Capability **cap)
|
|
| 106 | +{
|
|
| 107 | + rts_evalIO(cap,ensureIOManagerIsRunning_closure,NULL);
|
|
| 108 | +}
|
|
| 109 | + |
|
| 110 | +void
|
|
| 111 | +ioManagerStart (void)
|
|
| 112 | +{
|
|
| 113 | + // Make sure the IO manager thread is running
|
|
| 114 | + Capability *cap;
|
|
| 115 | + if (SEQ_CST_LOAD(&timer_manager_control_wr_fd) < 0) {
|
|
| 116 | + cap = rts_lock();
|
|
| 117 | + ioManagerStartCap(&cap);
|
|
| 118 | + rts_unlock(cap);
|
|
| 119 | + }
|
|
| 120 | +}
|
|
| 121 | +#endif
|
|
| 122 | + |
| 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 | +#if defined(THREADED_RTS)
|
|
| 22 | +void ioManagerDie (void);
|
|
| 23 | +void ioManagerStart (void);
|
|
| 24 | +void ioManagerStartCap (/* inout */ Capability **cap);
|
|
| 25 | + |
|
| 26 | +void timerManagerNotifySignal(int sig, siginfo_t *info);
|
|
| 27 | +#endif
|
|
| 28 | + |
|
| 29 | +#include "EndPrivate.h" |
| ... | ... | @@ -134,6 +134,14 @@ void initCapabilityIOManagerPoll(CapIOManager *iomgr) |
| 134 | 134 | }
|
| 135 | 135 | |
| 136 | 136 | |
| 137 | +void freeCapabilityIOManagerPoll(CapIOManager *iomgr)
|
|
| 138 | +{
|
|
| 139 | + if (iomgr->aiop_poll_table) {
|
|
| 140 | + stgFree(iomgr->aiop_poll_table);
|
|
| 141 | + }
|
|
| 142 | +}
|
|
| 143 | + |
|
| 144 | + |
|
| 137 | 145 | /* Used to implement syncIOWaitReady.
|
| 138 | 146 | * Result is true on success, or false on allocation failure. */
|
| 139 | 147 | bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
|
| ... | ... | @@ -194,6 +202,13 @@ void syncIOCancelPoll(CapIOManager *iomgr, StgTSO *tso) |
| 194 | 202 | * status, as that is done by removeFromQueues (in the throwTo* functions).
|
| 195 | 203 | */
|
| 196 | 204 | tso->block_info.closure = (StgClosure *)END_TSO_QUEUE;
|
| 205 | + |
|
| 206 | + /* We are in the TSO case, where the aiop was only reachable from the TSO
|
|
| 207 | + * itself, and thus it is now no longer be reachable at all.
|
|
| 208 | + */
|
|
| 209 | + IF_NONMOVING_WRITE_BARRIER_ENABLED {
|
|
| 210 | + updateRemembSetPushClosure(iomgr->cap, (StgClosure *)aiop);
|
|
| 211 | + }
|
|
| 197 | 212 | }
|
| 198 | 213 | |
| 199 | 214 | |
| ... | ... | @@ -251,7 +266,6 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop) |
| 251 | 266 | raiseAsync(iomgr->cap, tso,
|
| 252 | 267 | (StgClosure *)blockedOnBadFD_closure,
|
| 253 | 268 | false, NULL);
|
| 254 | - break;
|
|
| 255 | 269 | } else {
|
| 256 | 270 | /* We should be guaranteed that the tso is still on the same
|
| 257 | 271 | * cap because the tso was not on the run queue of any cap and
|
| ... | ... | @@ -262,6 +276,12 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop) |
| 262 | 276 | tso->_link = END_TSO_QUEUE;
|
| 263 | 277 | pushOnRunQueue(iomgr->cap, tso);
|
| 264 | 278 | }
|
| 279 | + /* For the TSO case, the aiop was only reachable from the TSO
|
|
| 280 | + * itself, and thus it is now no longer be reachable at all.
|
|
| 281 | + */
|
|
| 282 | + IF_NONMOVING_WRITE_BARRIER_ENABLED {
|
|
| 283 | + updateRemembSetPushClosure(iomgr->cap, (StgClosure *)aiop);
|
|
| 284 | + }
|
|
| 265 | 285 | break;
|
| 266 | 286 | }
|
| 267 | 287 | case NotifyMVar:
|
| ... | ... | @@ -455,6 +475,9 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) |
| 455 | 475 | int ncompletions = res;
|
| 456 | 476 | ASSERT(ncompletions <= (int)nfds);
|
| 457 | 477 | processIOCompletions(iomgr, ncompletions);
|
| 478 | + // FIXME: do we also need to check for timeout completions now?
|
|
| 479 | + // we have a non-empty queue, but if !wait then we have also moved
|
|
| 480 | + // on and so we sould check for timeouts.
|
|
| 458 | 481 | |
| 459 | 482 | } else if (errno == EINTR) {
|
| 460 | 483 | /* We got interrupted by a signal. In the non-threaded RTS, if the
|
| ... | ... | @@ -17,6 +17,7 @@ |
| 17 | 17 | #if defined(IOMGR_ENABLED_POLL)
|
| 18 | 18 | |
| 19 | 19 | void initCapabilityIOManagerPoll(CapIOManager *iomgr);
|
| 20 | +void freeCapabilityIOManagerPoll(CapIOManager *iomgr);
|
|
| 20 | 21 | |
| 21 | 22 | /* Synchronous I/O and timer operations */
|
| 22 | 23 | bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
|
| ... | ... | @@ -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"
|
|
| 16 | 13 | #include "RtsUtils.h"
|
| 14 | +#include "Schedule.h"
|
|
| 17 | 15 | #include "Prelude.h"
|
| 18 | -#include "Ticker.h"
|
|
| 19 | 16 | #include "ThreadLabels.h"
|
| 20 | 17 | #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)
|
| ... | ... | @@ -41,19 +33,10 @@ |
| 41 | 33 | # include <signal.h>
|
| 42 | 34 | #endif
|
| 43 | 35 | |
| 44 | -#if defined(HAVE_ERRNO_H)
|
|
| 45 | -# include <errno.h>
|
|
| 46 | -#endif
|
|
| 47 | - |
|
| 48 | -#if defined(HAVE_EVENTFD_H)
|
|
| 49 | -# include <sys/eventfd.h>
|
|
| 50 | -#endif
|
|
| 51 | - |
|
| 52 | 36 | #if defined(HAVE_TERMIOS_H)
|
| 53 | 37 | #include <termios.h>
|
| 54 | 38 | #endif
|
| 55 | 39 | |
| 56 | -#include <stdlib.h>
|
|
| 57 | 40 | #include <string.h>
|
| 58 | 41 | |
| 59 | 42 | /* This curious flag is provided for the benefit of the Haskell binding
|
| ... | ... | @@ -134,110 +117,6 @@ more_handlers(int sig) |
| 134 | 117 | nHandlers = sig + 1;
|
| 135 | 118 | }
|
| 136 | 119 | |
| 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 | 120 | #if !defined(THREADED_RTS)
|
| 242 | 121 | |
| 243 | 122 | #define N_PENDING_HANDLERS 16
|
| ... | ... | @@ -260,31 +139,9 @@ generic_handler(int sig USED_IF_THREADS, |
| 260 | 139 | void *p STG_UNUSED)
|
| 261 | 140 | {
|
| 262 | 141 | #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 | - |
|
| 142 | + //TODO: This calls MIO directly. We should go via IOManager API.
|
|
| 143 | + // The IOManager API should be extended to cover signals.
|
|
| 144 | + timerManagerNotifySignal(sig, info);
|
|
| 288 | 145 | #else /* not THREADED_RTS */
|
| 289 | 146 | |
| 290 | 147 | /* Can't call allocate from here. Probably can't call malloc
|
| ... | ... | @@ -25,18 +25,6 @@ extern siginfo_t *next_pending_handler; |
| 25 | 25 | void startSignalHandlers(Capability *cap);
|
| 26 | 26 | #endif
|
| 27 | 27 | |
| 28 | -/* Communicating with the IO manager thread (see GHC.Conc).
|
|
| 29 | - *
|
|
| 30 | - * TODO: these I/O manager things are not related to signals and ought to live
|
|
| 31 | - * elsewhere, e.g. in a module specifically for the I/O manager.
|
|
| 32 | - */
|
|
| 33 | -void ioManagerWakeup (void);
|
|
| 34 | -#if defined(THREADED_RTS)
|
|
| 35 | -void ioManagerDie (void);
|
|
| 36 | -void ioManagerStart (void);
|
|
| 37 | -void ioManagerStartCap (/* inout */ Capability **cap);
|
|
| 38 | -#endif
|
|
| 39 | - |
|
| 40 | 28 | extern StgInt *signal_handlers;
|
| 41 | 29 | |
| 42 | 30 | #include "EndPrivate.h" |
| ... | ... | @@ -583,6 +583,7 @@ library |
| 583 | 583 | posix/OSMem.c
|
| 584 | 584 | posix/OSThreads.c
|
| 585 | 585 | posix/FdWakeup.c
|
| 586 | + posix/MIO.c
|
|
| 586 | 587 | posix/Poll.c
|
| 587 | 588 | posix/Select.c
|
| 588 | 589 | posix/Signals.c
|
| ... | ... | @@ -12,6 +12,8 @@ |
| 12 | 12 | |
| 13 | 13 | #include "Rts.h"
|
| 14 | 14 | |
| 15 | +#include "BeginPrivate.h"
|
|
| 16 | + |
|
| 15 | 17 | extern unsigned int
|
| 16 | 18 | addIORequest(int fd,
|
| 17 | 19 | bool forWriting,
|
| ... | ... | @@ -28,3 +30,5 @@ extern int awaitRequests(bool wait); |
| 28 | 30 | extern void abandonRequestWait(void);
|
| 29 | 31 | extern void resetAbandonRequestWait(void);
|
| 30 | 32 | extern HsInt rts_EINTR(void);
|
| 33 | + |
|
| 34 | +#include "EndPrivate.h" |
| ... | ... | @@ -14,12 +14,16 @@ |
| 14 | 14 | #include <stdbool.h>
|
| 15 | 15 | #include <windows.h>
|
| 16 | 16 | |
| 17 | -extern bool startupAsyncWinIO(void);
|
|
| 18 | -extern void shutdownAsyncWinIO(bool wait_threads);
|
|
| 19 | -extern void awaitAsyncRequests(bool wait);
|
|
| 20 | 17 | extern void registerIOCPHandle (HANDLE port);
|
| 21 | 18 | extern void registerAlertableWait (bool has_timeout, DWORD mssec);
|
| 22 | - |
|
| 23 | 19 | extern OVERLAPPED_ENTRY* getOverlappedEntries (uint32_t *num);
|
| 24 | 20 | extern void completeSynchronousRequest (void);
|
| 21 | + |
|
| 22 | +#include "BeginPrivate.h"
|
|
| 23 | + |
|
| 24 | +extern bool startupAsyncWinIO(void);
|
|
| 25 | +extern void shutdownAsyncWinIO(bool wait_threads);
|
|
| 26 | +extern void awaitAsyncRequests(bool wait);
|
|
| 25 | 27 | extern bool queueIOThread(void);
|
| 28 | + |
|
| 29 | +#include "EndPrivate.h" |
| 1 | 1 | #pragma once
|
| 2 | 2 | |
| 3 | +#include "BeginPrivate.h"
|
|
| 4 | + |
|
| 3 | 5 | void awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait);
|
| 4 | 6 | |
| 7 | +#include "EndPrivate.h" |
| ... | ... | @@ -7,6 +7,8 @@ |
| 7 | 7 | |
| 8 | 8 | #pragma once
|
| 9 | 9 | |
| 10 | +#include "BeginPrivate.h"
|
|
| 11 | + |
|
| 10 | 12 | /*
|
| 11 | 13 | * Console control handlers lets an application handle Ctrl+C, Ctrl+Break etc.
|
| 12 | 14 | * in Haskell under Win32. Akin to the Unix signal SIGINT.
|
| ... | ... | @@ -69,3 +71,5 @@ extern int rts_waitConsoleHandlerCompletion(void); |
| 69 | 71 | * Tear down and shut down user signal processing.
|
| 70 | 72 | */
|
| 71 | 73 | extern void finiUserSignals(void);
|
| 74 | + |
|
| 75 | +#include "EndPrivate.h" |
| ... | ... | @@ -12,6 +12,8 @@ |
| 12 | 12 | |
| 13 | 13 | #include <windows.h>
|
| 14 | 14 | |
| 15 | +#include "BeginPrivate.h"
|
|
| 16 | + |
|
| 15 | 17 | /*
|
| 16 | 18 | The IOManager subsystem provides a non-blocking view
|
| 17 | 19 | of I/O operations. It lets one (or more) OS thread(s)
|
| ... | ... | @@ -107,3 +109,5 @@ extern int AddProcRequest ( void* proc, |
| 107 | 109 | extern void abandonWorkRequest ( int reqID );
|
| 108 | 110 | |
| 109 | 111 | extern void interruptIOManagerEvent ( void );
|
| 112 | + |
|
| 113 | +#include "EndPrivate.h" |
| ... | ... | @@ -12,7 +12,12 @@ |
| 12 | 12 | |
| 13 | 13 | /* Communicating with the IO manager thread (see GHC.Conc).
|
| 14 | 14 | */
|
| 15 | +void ioManagerFinished (void);
|
|
| 16 | + |
|
| 17 | +#include "BeginPrivate.h"
|
|
| 18 | + |
|
| 15 | 19 | void ioManagerWakeup (void);
|
| 16 | 20 | void ioManagerDie (void);
|
| 17 | 21 | void ioManagerStart (void);
|
| 18 | -void ioManagerFinished (void); |
|
| 22 | + |
|
| 23 | +#include "EndPrivate.h" |
| ... | ... | @@ -10,6 +10,8 @@ |
| 10 | 10 | |
| 11 | 11 | #include <windows.h>
|
| 12 | 12 | |
| 13 | +#include "BeginPrivate.h"
|
|
| 14 | + |
|
| 13 | 15 | /* This is a fixed-size queue. */
|
| 14 | 16 | #define WORKQUEUE_SIZE 16
|
| 15 | 17 | |
| ... | ... | @@ -34,3 +36,5 @@ extern HANDLE GetWorkQueueHandle ( WorkQueue* pq ); |
| 34 | 36 | extern BOOL GetWork ( WorkQueue* pq, void** ppw );
|
| 35 | 37 | extern BOOL FetchWork ( WorkQueue* pq, void** ppw );
|
| 36 | 38 | extern int SubmitWork ( WorkQueue* pq, void* pw );
|
| 39 | + |
|
| 40 | +#include "EndPrivate.h" |
| ... | ... | @@ -59,6 +59,8 @@ |
| 59 | 59 | // simplified quite a bit.
|
| 60 | 60 | // Using VEH also means we don't have to worry about the dynamic code generated by GHCi.
|
| 61 | 61 | |
| 62 | +#include "BeginPrivate.h"
|
|
| 63 | + |
|
| 62 | 64 | // Prototype of the VEH callback function.
|
| 63 | 65 | // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms681419(v=vs.85).aspx
|
| 64 | 66 | //
|
| ... | ... | @@ -72,3 +74,5 @@ void __unregister_hs_exception_handler( void ); |
| 72 | 74 | // prototypes for dump methods.
|
| 73 | 75 | void generateDump(EXCEPTION_POINTERS* pExceptionPointers);
|
| 74 | 76 | void generateStack (EXCEPTION_POINTERS* pExceptionPointers);
|
| 77 | + |
|
| 78 | +#include "EndPrivate.h" |