Duncan Coutts pushed to branch wip/io-manager-deadlock-detection at Glasgow Haskell Compiler / GHC
Commits:
-
1136fd5b
by Duncan Coutts at 2026-02-16T00:30:48+00:00
-
ec170e68
by Duncan Coutts at 2026-02-16T00:30:50+00:00
-
c4312136
by Duncan Coutts at 2026-02-16T00:30:50+00:00
-
24a403ed
by Duncan Coutts at 2026-02-16T00:30:50+00:00
-
e9c4d16c
by Duncan Coutts at 2026-02-16T00:30:50+00:00
-
fcc1cdd3
by Duncan Coutts at 2026-02-16T00:30:50+00:00
-
4e4aa84f
by Duncan Coutts at 2026-02-16T00:30:50+00:00
-
6fe60774
by Duncan Coutts at 2026-02-16T00:30:50+00:00
-
24b6f621
by Duncan Coutts at 2026-02-16T00:30:50+00:00
-
bc97a19f
by Duncan Coutts at 2026-02-16T00:30:50+00:00
18 changed files:
- rts/Capability.c
- rts/IOManager.c
- rts/IOManager.h
- rts/IOManagerInternals.h
- rts/RtsStartup.c
- rts/Schedule.c
- rts/Schedule.h
- + rts/posix/FdWakeup.c
- + rts/posix/FdWakeup.h
- + rts/posix/MIO.c
- + rts/posix/MIO.h
- rts/posix/Poll.c
- rts/posix/Poll.h
- rts/posix/Select.c
- rts/posix/Select.h
- rts/posix/Signals.c
- rts/posix/Signals.h
- rts/rts.cabal
Changes:
| ... | ... | @@ -1280,6 +1280,7 @@ shutdownCapabilities(Task *task, bool safe) |
| 1280 | 1280 | static void
|
| 1281 | 1281 | freeCapability (Capability *cap)
|
| 1282 | 1282 | {
|
| 1283 | + freeCapabilityIOManager(cap->iomgr);
|
|
| 1283 | 1284 | stgFree(cap->mut_lists);
|
| 1284 | 1285 | stgFree(cap->saved_mut_lists);
|
| 1285 | 1286 | if (cap->current_segments) {
|
| ... | ... | @@ -39,7 +39,7 @@ |
| 39 | 39 | #endif
|
| 40 | 40 | |
| 41 | 41 | #if defined(IOMGR_ENABLED_MIO_POSIX)
|
| 42 | -#include "posix/Signals.h"
|
|
| 42 | +#include "posix/MIO.h"
|
|
| 43 | 43 | #include "Prelude.h"
|
| 44 | 44 | #endif
|
| 45 | 45 | |
| ... | ... | @@ -343,9 +343,7 @@ void initCapabilityIOManager(CapIOManager *iomgr) |
| 343 | 343 | switch (iomgr_type) {
|
| 344 | 344 | #if defined(IOMGR_ENABLED_SELECT)
|
| 345 | 345 | case IO_MANAGER_SELECT:
|
| 346 | - iomgr->blocked_queue_hd = END_TSO_QUEUE;
|
|
| 347 | - iomgr->blocked_queue_tl = END_TSO_QUEUE;
|
|
| 348 | - iomgr->sleeping_queue = END_TSO_QUEUE;
|
|
| 346 | + initCapabilityIOManagerSelect(iomgr);
|
|
| 349 | 347 | break;
|
| 350 | 348 | #endif
|
| 351 | 349 | |
| ... | ... | @@ -373,11 +371,31 @@ void initCapabilityIOManager(CapIOManager *iomgr) |
| 373 | 371 | }
|
| 374 | 372 | |
| 375 | 373 | |
| 374 | +void freeCapabilityIOManager(CapIOManager *iomgr)
|
|
| 375 | +{
|
|
| 376 | + switch (iomgr_type) {
|
|
| 377 | +#if defined(IOMGR_ENABLED_SELECT)
|
|
| 378 | + case IO_MANAGER_SELECT:
|
|
| 379 | + freeCapabilityIOManagerSelect(iomgr);
|
|
| 380 | + break;
|
|
| 381 | +#endif
|
|
| 382 | + |
|
| 383 | +#if defined(IOMGR_ENABLED_POLL)
|
|
| 384 | + case IO_MANAGER_POLL:
|
|
| 385 | + freeCapabilityIOManagerPoll(iomgr);
|
|
| 386 | + break;
|
|
| 387 | +#endif
|
|
| 388 | + default:
|
|
| 389 | + break;
|
|
| 390 | + }
|
|
| 391 | +}
|
|
| 392 | + |
|
| 393 | + |
|
| 376 | 394 | /* Called late in the RTS initialisation
|
| 377 | 395 | */
|
| 378 | -void initIOManager(void)
|
|
| 396 | +void startIOManager(void)
|
|
| 379 | 397 | {
|
| 380 | - debugTrace(DEBUG_iomanager, "initialising %s I/O manager", showIOManager());
|
|
| 398 | + debugTrace(DEBUG_iomanager, "starting %s I/O manager", showIOManager());
|
|
| 381 | 399 | |
| 382 | 400 | switch (iomgr_type) {
|
| 383 | 401 | |
| ... | ... | @@ -441,7 +459,7 @@ void initIOManager(void) |
| 441 | 459 | /* Called from forkProcess in the child process on the surviving capability.
|
| 442 | 460 | */
|
| 443 | 461 | void
|
| 444 | -initIOManagerAfterFork(CapIOManager *iomgr, Capability **pcap)
|
|
| 462 | +restartIOManager(CapIOManager *iomgr, Capability **pcap)
|
|
| 445 | 463 | {
|
| 446 | 464 | |
| 447 | 465 | switch (iomgr_type) {
|
| ... | ... | @@ -546,8 +564,21 @@ exitIOManager(bool wait_threads) |
| 546 | 564 | */
|
| 547 | 565 | void wakeupIOManager(void)
|
| 548 | 566 | {
|
| 567 | + debugTrace(DEBUG_iomanager, "Sending wakeup to I/O manager...");
|
|
| 549 | 568 | switch (iomgr_type) {
|
| 550 | 569 | |
| 570 | +#if defined(IOMGR_ENABLED_SELECT)
|
|
| 571 | + case IO_MANAGER_SELECT:
|
|
| 572 | + wakeupIOManagerSelect(MainCapability.iomgr);
|
|
| 573 | + break;
|
|
| 574 | +#endif
|
|
| 575 | + |
|
| 576 | +#if defined(IOMGR_ENABLED_POLL)
|
|
| 577 | + case IO_MANAGER_POLL:
|
|
| 578 | + wakeupIOManagerPoll(MainCapability.iomgr);
|
|
| 579 | + break;
|
|
| 580 | +#endif
|
|
| 581 | + |
|
| 551 | 582 | #if defined(IOMGR_ENABLED_MIO_POSIX)
|
| 552 | 583 | case IO_MANAGER_MIO_POSIX:
|
| 553 | 584 | /* MIO Posix implementation in posix/Signals.c */
|
| ... | ... | @@ -572,8 +603,13 @@ void wakeupIOManager(void) |
| 572 | 603 | #endif
|
| 573 | 604 | break;
|
| 574 | 605 | #endif
|
| 575 | - default:
|
|
| 606 | +#if defined(IOMGR_ENABLED_WIN32_LEGACY)
|
|
| 607 | + case IO_MANAGER_WIN32_LEGACY:
|
|
| 608 | + abandonRequestWait();
|
|
| 576 | 609 | break;
|
| 610 | +#endif
|
|
| 611 | + default:
|
|
| 612 | + barf("wakeupIOManager not implemented");
|
|
| 577 | 613 | }
|
| 578 | 614 | }
|
| 579 | 615 | |
| ... | ... | @@ -782,7 +818,9 @@ void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr) |
| 782 | 818 | default:
|
| 783 | 819 | barf("pollCompletedTimeoutsOrIO not implemented");
|
| 784 | 820 | }
|
| 785 | - ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING);
|
|
| 821 | + // FIXME: the post condition is now more complicated. Await can now simply
|
|
| 822 | + // be interrupted by wakeupIOManager.
|
|
| 823 | + // ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING);
|
|
| 786 | 824 | }
|
| 787 | 825 | |
| 788 | 826 |
| ... | ... | @@ -242,11 +242,33 @@ CapIOManager *allocCapabilityIOManager(Capability *cap); |
| 242 | 242 | */
|
| 243 | 243 | void initCapabilityIOManager(CapIOManager *iomgr);
|
| 244 | 244 | |
| 245 | +/* When shutting down a capability, or after forkProcess, free the resources
|
|
| 246 | + * held by a CapIOManager to put it back into a state in which either it can be
|
|
| 247 | + * re-initialised using initCapabilityIOManager, or the whole structure freed.
|
|
| 248 | + *
|
|
| 249 | + * Note that this does not free the CapIOManager structure itself, just the
|
|
| 250 | + * contents.
|
|
| 251 | + *
|
|
| 252 | + * This is used during capability shutdown, during RTS shutdown. It is not used
|
|
| 253 | + * when reducing the number of capabilities. Capabilities are disabled rather
|
|
| 254 | + * than freed entirely: the I/O manager keeps running but threads that become
|
|
| 255 | + * runnable are migrated away.
|
|
| 256 | + *
|
|
| 257 | + * It is also used after forkProcess.
|
|
| 258 | + */
|
|
| 259 | +void freeCapabilityIOManager(CapIOManager *iomgr);
|
|
| 260 | + |
|
| 261 | +/* CapIOManager life cycle:
|
|
| 262 | + *
|
|
| 263 | + * alloc -> init -> free -> free struct
|
|
| 264 | + * ^ |
|
|
| 265 | + * +--------+
|
|
| 266 | + */
|
|
| 245 | 267 | |
| 246 | 268 | /* Init hook: called from hs_init_ghc, very late in the startup after almost
|
| 247 | 269 | * everything else is done.
|
| 248 | 270 | */
|
| 249 | -void initIOManager(void);
|
|
| 271 | +void startIOManager(void);
|
|
| 250 | 272 | |
| 251 | 273 | |
| 252 | 274 | /* Init hook: called from forkProcess in the child process on the surviving
|
| ... | ... | @@ -255,8 +277,8 @@ void initIOManager(void); |
| 255 | 277 | * This is synchronous and can run Haskell code, so can change the given cap.
|
| 256 | 278 | * TODO: it would make for a cleaner API here if this were made asynchronous.
|
| 257 | 279 | */
|
| 258 | -void initIOManagerAfterFork(CapIOManager *iomgr,
|
|
| 259 | - /* inout */ Capability **pcap);
|
|
| 280 | +void restartIOManager(CapIOManager *iomgr,
|
|
| 281 | + /* inout */ Capability **pcap);
|
|
| 260 | 282 | |
| 261 | 283 | /* TODO: rationalise initIOManager and initIOManagerAfterFork into a single
|
| 262 | 284 | per-capability init function.
|
| ... | ... | @@ -283,19 +305,13 @@ void stopIOManager(void); |
| 283 | 305 | void exitIOManager(bool wait_threads);
|
| 284 | 306 | |
| 285 | 307 | |
| 286 | -/* Wakeup hook: called from the scheduler's wakeUpRts (currently only in
|
|
| 287 | - * threaded mode).
|
|
| 308 | +/* Wakeup hook: called from the scheduler's wakeUpRts().
|
|
| 288 | 309 | *
|
| 289 | 310 | * The I/O manager can be blocked waiting on I/O or timers. Sometimes there are
|
| 290 | 311 | * other external events where we need to wake up the I/O manager and return
|
| 291 | - * to the schedulr.
|
|
| 292 | - *
|
|
| 293 | - * At the moment, all the non-threaded I/O managers will do this automagically
|
|
| 294 | - * since a signal will interrupt any waiting system calls, so at the moment
|
|
| 295 | - * the implementation for the non-threaded I/O managers does nothing.
|
|
| 312 | + * to the scheduler.
|
|
| 296 | 313 | *
|
| 297 | - * For the I/O managers in threaded mode, this arranges to unblock the I/O
|
|
| 298 | - * manager if it waa blocked waiting.
|
|
| 314 | + * This arranges to unblock the I/O manager if it was blocked waiting.
|
|
| 299 | 315 | */
|
| 300 | 316 | void wakeupIOManager(void);
|
| 301 | 317 |
| ... | ... | @@ -46,6 +46,11 @@ struct _CapIOManager { |
| 46 | 46 | StgTSO *sleeping_queue;
|
| 47 | 47 | #endif
|
| 48 | 48 | |
| 49 | +#if defined(IOMGR_ENABLED_SELECT) || defined(IOMGR_ENABLED_POLL)
|
|
| 50 | + /* FDs for waking up the I/O manager when it is blocked waiting */
|
|
| 51 | + int wakeup_fd_r, wakeup_fd_w;
|
|
| 52 | +#endif
|
|
| 53 | + |
|
| 49 | 54 | #if defined(IOMGR_ENABLED_POLL)
|
| 50 | 55 | /* AIOP and timeout collections shared by several I/O manager impls */
|
| 51 | 56 | ClosureTable aiop_table;
|
| ... | ... | @@ -53,8 +58,11 @@ struct _CapIOManager { |
| 53 | 58 | #endif
|
| 54 | 59 | |
| 55 | 60 | #if defined(IOMGR_ENABLED_POLL)
|
| 56 | - /* Auxiliary table with size and indexes matching the aiop_table */
|
|
| 57 | - struct pollfd *aiop_poll_table;
|
|
| 61 | + /* Auxiliary table with size and indexes matching the aiop_table. This is
|
|
| 62 | + * aliased to the tail of the full poll table, which has a head entry for
|
|
| 63 | + * the wakeup_fd_r above, so we can also poll that fd.
|
|
| 64 | + */
|
|
| 65 | + struct pollfd *aiop_poll_table, *full_poll_table;
|
|
| 58 | 66 | #endif
|
| 59 | 67 | |
| 60 | 68 | #if defined(IOMGR_ENABLED_WIN32_LEGACY)
|
| ... | ... | @@ -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 |
| ... | ... | @@ -174,6 +174,11 @@ static void deleteAllThreads (void); |
| 174 | 174 | static void deleteThread_(StgTSO *tso);
|
| 175 | 175 | #endif
|
| 176 | 176 | |
| 177 | +#if defined(FORKPROCESS_PRIMOP_SUPPORTED)
|
|
| 178 | +static void truncateRunQueue(Capability *cap);
|
|
| 179 | +#endif
|
|
| 180 | +static StgTSO *popRunQueue (Capability *cap);
|
|
| 181 | + |
|
| 177 | 182 | /* ---------------------------------------------------------------------------
|
| 178 | 183 | Main scheduling loop.
|
| 179 | 184 | |
| ... | ... | @@ -591,38 +596,6 @@ run_thread: |
| 591 | 596 | } /* end of while() */
|
| 592 | 597 | }
|
| 593 | 598 | |
| 594 | -/* -----------------------------------------------------------------------------
|
|
| 595 | - * Run queue operations
|
|
| 596 | - * -------------------------------------------------------------------------- */
|
|
| 597 | - |
|
| 598 | -static void
|
|
| 599 | -removeFromRunQueue (Capability *cap, StgTSO *tso)
|
|
| 600 | -{
|
|
| 601 | - if (tso->block_info.prev == END_TSO_QUEUE) {
|
|
| 602 | - ASSERT(cap->run_queue_hd == tso);
|
|
| 603 | - cap->run_queue_hd = tso->_link;
|
|
| 604 | - } else {
|
|
| 605 | - setTSOLink(cap, tso->block_info.prev, tso->_link);
|
|
| 606 | - }
|
|
| 607 | - if (tso->_link == END_TSO_QUEUE) {
|
|
| 608 | - ASSERT(cap->run_queue_tl == tso);
|
|
| 609 | - cap->run_queue_tl = tso->block_info.prev;
|
|
| 610 | - } else {
|
|
| 611 | - setTSOPrev(cap, tso->_link, tso->block_info.prev);
|
|
| 612 | - }
|
|
| 613 | - tso->_link = tso->block_info.prev = END_TSO_QUEUE;
|
|
| 614 | - cap->n_run_queue--;
|
|
| 615 | - |
|
| 616 | - IF_DEBUG(sanity, checkRunQueue(cap));
|
|
| 617 | -}
|
|
| 618 | - |
|
| 619 | -void
|
|
| 620 | -promoteInRunQueue (Capability *cap, StgTSO *tso)
|
|
| 621 | -{
|
|
| 622 | - removeFromRunQueue(cap, tso);
|
|
| 623 | - pushOnRunQueue(cap, tso);
|
|
| 624 | -}
|
|
| 625 | - |
|
| 626 | 599 | /* -----------------------------------------------------------------------------
|
| 627 | 600 | * scheduleFindWork()
|
| 628 | 601 | *
|
| ... | ... | @@ -2191,7 +2164,15 @@ forkProcess(HsStablePtr *entry |
| 2191 | 2164 | // bound threads for which the corresponding Task does not
|
| 2192 | 2165 | // exist.
|
| 2193 | 2166 | truncateRunQueue(cap);
|
| 2194 | - cap->n_run_queue = 0;
|
|
| 2167 | + |
|
| 2168 | + // Reset and re-initialise the capability's I/O manager,
|
|
| 2169 | + // to get the I/O manager ready again.
|
|
| 2170 | + //
|
|
| 2171 | + // Any threads waiting on I/O or timers should have been
|
|
| 2172 | + // removed from I/O manager queues by deleteThread_ above.
|
|
| 2173 | + // TODO: but we could assert that here.
|
|
| 2174 | + freeCapabilityIOManager(cap->iomgr);
|
|
| 2175 | + initCapabilityIOManager(cap->iomgr);
|
|
| 2195 | 2176 | |
| 2196 | 2177 | // Any suspended C-calling Tasks are no more, their OS threads
|
| 2197 | 2178 | // don't exist now:
|
| ... | ... | @@ -2208,7 +2189,7 @@ forkProcess(HsStablePtr *entry |
| 2208 | 2189 | cap->n_returning_tasks = 0;
|
| 2209 | 2190 | #endif
|
| 2210 | 2191 | |
| 2211 | - // Release all caps except 0, we'll use that for starting
|
|
| 2192 | + // Release all caps except 0, we'll use that for restarting
|
|
| 2212 | 2193 | // the IO manager and running the client action below.
|
| 2213 | 2194 | if (cap->no != 0) {
|
| 2214 | 2195 | task->cap = cap;
|
| ... | ... | @@ -2232,7 +2213,7 @@ forkProcess(HsStablePtr *entry |
| 2232 | 2213 | // like startup event, capabilities, process info etc
|
| 2233 | 2214 | traceTaskCreate(task, cap);
|
| 2234 | 2215 | |
| 2235 | - initIOManagerAfterFork(cap->iomgr, &cap);
|
|
| 2216 | + restartIOManager(cap->iomgr, &cap);
|
|
| 2236 | 2217 | |
| 2237 | 2218 | // start timer after the IOManager is initialized
|
| 2238 | 2219 | // (the idle GC may wake up the IOManager)
|
| ... | ... | @@ -2337,6 +2318,10 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS) |
| 2337 | 2318 | // the capability; we don't have to worry about GC data
|
| 2338 | 2319 | // structures, the nursery, etc.
|
| 2339 | 2320 | //
|
| 2321 | + // This approach also handles threads blocked on I/O. Such threads
|
|
| 2322 | + // remain blocked, and when I/O completes and threads become runnable
|
|
| 2323 | + // then they are migrated away.
|
|
| 2324 | + //
|
|
| 2340 | 2325 | for (n = new_n_capabilities; n < enabled_capabilities; n++) {
|
| 2341 | 2326 | getCapability(n)->disabled = true;
|
| 2342 | 2327 | traceCapDisable(getCapability(n));
|
| ... | ... | @@ -2997,7 +2982,7 @@ pushOnRunQueue (Capability *cap, StgTSO *tso) |
| 2997 | 2982 | cap->n_run_queue++;
|
| 2998 | 2983 | }
|
| 2999 | 2984 | |
| 3000 | -StgTSO *popRunQueue (Capability *cap)
|
|
| 2985 | +static StgTSO *popRunQueue (Capability *cap)
|
|
| 3001 | 2986 | {
|
| 3002 | 2987 | ASSERT(cap->n_run_queue > 0);
|
| 3003 | 2988 | StgTSO *t = cap->run_queue_hd;
|
| ... | ... | @@ -3017,6 +3002,45 @@ StgTSO *popRunQueue (Capability *cap) |
| 3017 | 3002 | return t;
|
| 3018 | 3003 | }
|
| 3019 | 3004 | |
| 3005 | +#if defined(FORKPROCESS_PRIMOP_SUPPORTED)
|
|
| 3006 | +static void truncateRunQueue(Capability *cap)
|
|
| 3007 | +{
|
|
| 3008 | + // Can only be called by the task owning the capability.
|
|
| 3009 | + TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_hd, "truncateRunQueue");
|
|
| 3010 | + TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_tl, "truncateRunQueue");
|
|
| 3011 | + TSAN_ANNOTATE_BENIGN_RACE(&cap->n_run_queue, "truncateRunQueue");
|
|
| 3012 | + cap->run_queue_hd = END_TSO_QUEUE;
|
|
| 3013 | + cap->run_queue_tl = END_TSO_QUEUE;
|
|
| 3014 | + cap->n_run_queue = 0;
|
|
| 3015 | +}
|
|
| 3016 | +#endif
|
|
| 3017 | + |
|
| 3018 | +static void removeFromRunQueue (Capability *cap, StgTSO *tso)
|
|
| 3019 | +{
|
|
| 3020 | + if (tso->block_info.prev == END_TSO_QUEUE) {
|
|
| 3021 | + ASSERT(cap->run_queue_hd == tso);
|
|
| 3022 | + cap->run_queue_hd = tso->_link;
|
|
| 3023 | + } else {
|
|
| 3024 | + setTSOLink(cap, tso->block_info.prev, tso->_link);
|
|
| 3025 | + }
|
|
| 3026 | + if (tso->_link == END_TSO_QUEUE) {
|
|
| 3027 | + ASSERT(cap->run_queue_tl == tso);
|
|
| 3028 | + cap->run_queue_tl = tso->block_info.prev;
|
|
| 3029 | + } else {
|
|
| 3030 | + setTSOPrev(cap, tso->_link, tso->block_info.prev);
|
|
| 3031 | + }
|
|
| 3032 | + tso->_link = tso->block_info.prev = END_TSO_QUEUE;
|
|
| 3033 | + cap->n_run_queue--;
|
|
| 3034 | + |
|
| 3035 | + IF_DEBUG(sanity, checkRunQueue(cap));
|
|
| 3036 | +}
|
|
| 3037 | + |
|
| 3038 | +void promoteInRunQueue (Capability *cap, StgTSO *tso)
|
|
| 3039 | +{
|
|
| 3040 | + removeFromRunQueue(cap, tso);
|
|
| 3041 | + pushOnRunQueue(cap, tso);
|
|
| 3042 | +}
|
|
| 3043 | + |
|
| 3020 | 3044 | |
| 3021 | 3045 | /* -----------------------------------------------------------------------------
|
| 3022 | 3046 | 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" |
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team 2025
|
|
| 4 | + *
|
|
| 5 | + * Utilities for a simple fd-based cross-thread wakeup mechanism.
|
|
| 6 | + *
|
|
| 7 | + * This is used in I/O managers, to provide a mechanism to wake them when they
|
|
| 8 | + * are blocked waiting on fds and timeouts. The mechanism works by including
|
|
| 9 | + * the read end fd into the set of fds the I/O manager waits on, and when a
|
|
| 10 | + * wake up is needed, the write end fd is used.
|
|
| 11 | + *
|
|
| 12 | + * This is implemented using either eventfd() or pipe().
|
|
| 13 | + *
|
|
| 14 | + * Linux 2.6.22+ and FreeBSD 13+ support eventfd. It is a single fd with a
|
|
| 15 | + * 64bit counter. It uses less resources than a pipe, and is probably a tad
|
|
| 16 | + * faster. Using write() adds to the counter, while read() reads and resets
|
|
| 17 | + * it. This gives us event combining.
|
|
| 18 | + *
|
|
| 19 | + * Otherwise we use a classic unix pipe.
|
|
| 20 | + *
|
|
| 21 | + * -------------------------------------------------------------------------*/
|
|
| 22 | + |
|
| 23 | +#include "rts/PosixSource.h"
|
|
| 24 | +#include "Rts.h"
|
|
| 25 | + |
|
| 26 | +#include "FdWakeup.h"
|
|
| 27 | + |
|
| 28 | +#include <fcntl.h>
|
|
| 29 | +#include <unistd.h>
|
|
| 30 | + |
|
| 31 | +#ifdef HAVE_SYS_EVENTFD_H
|
|
| 32 | +#include <sys/eventfd.h>
|
|
| 33 | +#endif
|
|
| 34 | + |
|
| 35 | +#if !defined(HAVE_EVENTFD) \
|
|
| 36 | + || (defined(HAVE_EVENTFD) && !(defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)))
|
|
| 37 | +static void fcntl_CLOEXEC_NONBLOCK(int fd)
|
|
| 38 | +{
|
|
| 39 | + int res1 = fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
| 40 | + int res2 = fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
| 41 | + if (RTS_UNLIKELY(res1 < 0 || res2 < 0)) {
|
|
| 42 | + sysErrorBelch("newFdWakeup fcntl()");
|
|
| 43 | + stg_exit(EXIT_FAILURE);
|
|
| 44 | + }
|
|
| 45 | +}
|
|
| 46 | +#endif
|
|
| 47 | + |
|
| 48 | +void newFdWakeup(int *wakeup_fd_r, int *wakeup_fd_w)
|
|
| 49 | +{
|
|
| 50 | +#if defined(HAVE_EVENTFD)
|
|
| 51 | + int wakeup_fd;
|
|
| 52 | +#if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
|
|
| 53 | + wakeup_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
|
| 54 | +#else
|
|
| 55 | + wakeup_fd = eventfd(0, 0);
|
|
| 56 | + if (wakeup_fd >= 0) fcntl_CLOEXEC_NONBLOCK(wakeup_fd);
|
|
| 57 | +#endif
|
|
| 58 | + if (RTS_UNLIKELY(wakeup_fd < 0)) {
|
|
| 59 | + sysErrorBelch("newFdWakeup eventfd()");
|
|
| 60 | + stg_exit(EXIT_FAILURE);
|
|
| 61 | + }
|
|
| 62 | + /* eventfd uses the same fd for each end */
|
|
| 63 | + *wakeup_fd_r = wakeup_fd;
|
|
| 64 | + *wakeup_fd_w = wakeup_fd;
|
|
| 65 | +#else
|
|
| 66 | + int pipefd[2];
|
|
| 67 | + int res;
|
|
| 68 | + res = pipe(pipefd);
|
|
| 69 | + if (RTS_UNLIKELY(res < 0)) {
|
|
| 70 | + sysErrorBelch("newFdWakeup pipe");
|
|
| 71 | + stg_exit(EXIT_FAILURE);
|
|
| 72 | + }
|
|
| 73 | + fcntl_CLOEXEC_NONBLOCK(pipefd[0]);
|
|
| 74 | + fcntl_CLOEXEC_NONBLOCK(pipefd[1]);
|
|
| 75 | + *wakeup_fd_r = pipefd[0]; /* read end */
|
|
| 76 | + *wakeup_fd_w = pipefd[1]; /* write end */
|
|
| 77 | +#endif
|
|
| 78 | +}
|
|
| 79 | + |
|
| 80 | +void closeFdWakeup(int wakeup_fd_r, int wakeup_fd_w)
|
|
| 81 | +{
|
|
| 82 | +#if defined(HAVE_EVENTFD)
|
|
| 83 | + ASSERT(wakeup_fd_r == wakeup_fd_w);
|
|
| 84 | + close(wakeup_fd_r);
|
|
| 85 | +#else
|
|
| 86 | + ASSERT(wakeup_fd_r != wakeup_fd_w);
|
|
| 87 | + close(wakeup_fd_r);
|
|
| 88 | + close(wakeup_fd_w);
|
|
| 89 | +#endif
|
|
| 90 | +}
|
|
| 91 | + |
|
| 92 | +void sendFdWakeup(int wakeup_fd_w)
|
|
| 93 | +{
|
|
| 94 | + int res;
|
|
| 95 | +#if defined(HAVE_EVENTFD)
|
|
| 96 | + uint64_t val = 1;
|
|
| 97 | + res = write(wakeup_fd_w, &val, 8);
|
|
| 98 | +#else
|
|
| 99 | + unsigned char buf = 1;
|
|
| 100 | + res = write(wakeup_fd_w, &buf, 1);
|
|
| 101 | +#endif
|
|
| 102 | + if (RTS_UNLIKELY(res < 0)) {
|
|
| 103 | + /* Unlikely the pipe buffer will fill, but it would not be an error. */
|
|
| 104 | + if (errno == EAGAIN) return;
|
|
| 105 | + sysErrorBelch("sendFdWakeup write");
|
|
| 106 | + stg_exit(EXIT_FAILURE);
|
|
| 107 | + }
|
|
| 108 | +}
|
|
| 109 | + |
|
| 110 | +void collectFdWakeup(int wakeup_fd_r)
|
|
| 111 | +{
|
|
| 112 | + int res;
|
|
| 113 | +#if defined(HAVE_EVENTFD)
|
|
| 114 | + uint64_t buf;
|
|
| 115 | + /* eventfd combines events into one counter, so a single read is enough */
|
|
| 116 | + res = read(wakeup_fd_r, &buf, 8);
|
|
| 117 | +#else
|
|
| 118 | + /* Drain the pipe buffer. Multiple wakeup notifications could
|
|
| 119 | + * have been sent before we have a chance to collect them.
|
|
| 120 | + */
|
|
| 121 | + uint64_t buf;
|
|
| 122 | + do {
|
|
| 123 | + res = read(wakeup_fd_r, &buf, 8);
|
|
| 124 | + } while (res == 8);
|
|
| 125 | +#endif
|
|
| 126 | + if (RTS_UNLIKELY(res < 0)) {
|
|
| 127 | + /* After the first pipe read, it could block */
|
|
| 128 | + if (errno == EAGAIN) return;
|
|
| 129 | + sysErrorBelch("collectFdWakeup read");
|
|
| 130 | + stg_exit(EXIT_FAILURE);
|
|
| 131 | + }
|
|
| 132 | +} |
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team 2025
|
|
| 4 | + *
|
|
| 5 | + * Utilities for a simple fd-based cross-thread wakeup mechanism.
|
|
| 6 | + *
|
|
| 7 | + * This is used in I/O managers, to provide a mechanism to wake them when they
|
|
| 8 | + * are blocked waiting on fds and timeouts. The mechanism works by including
|
|
| 9 | + * the read end fd into the set of fds the I/O manager waits on, and when a
|
|
| 10 | + * wake up is needed, the write end fd is used.
|
|
| 11 | + *
|
|
| 12 | + * Prototypes for functions in FdWakeup.c
|
|
| 13 | + *
|
|
| 14 | + * -------------------------------------------------------------------------*/
|
|
| 15 | + |
|
| 16 | +#pragma once
|
|
| 17 | + |
|
| 18 | +#include "BeginPrivate.h"
|
|
| 19 | + |
|
| 20 | +void newFdWakeup(int *fd_r, int *fd_w);
|
|
| 21 | +void closeFdWakeup(int fd_r, int fd_w);
|
|
| 22 | + |
|
| 23 | +void sendFdWakeup(int fd_w);
|
|
| 24 | +void collectFdWakeup(int fd_r);
|
|
| 25 | + |
|
| 26 | +#include "EndPrivate.h"
|
|
| 27 | + |
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team, 1998-2005
|
|
| 4 | + *
|
|
| 5 | + * Signal processing / handling.
|
|
| 6 | + *
|
|
| 7 | + * ---------------------------------------------------------------------------*/
|
|
| 8 | + |
|
| 9 | +#include "rts/PosixSource.h"
|
|
| 10 | +#include "Rts.h"
|
|
| 11 | + |
|
| 12 | +#include "Schedule.h"
|
|
| 13 | +#include "RtsUtils.h"
|
|
| 14 | +#include "Prelude.h"
|
|
| 15 | +#include "ThreadLabels.h"
|
|
| 16 | + |
|
| 17 | +#include "MIO.h"
|
|
| 18 | +#include "IOManager.h"
|
|
| 19 | +#include "IOManagerInternals.h"
|
|
| 20 | + |
|
| 21 | +#if defined(HAVE_ERRNO_H)
|
|
| 22 | +# include <errno.h>
|
|
| 23 | +#endif
|
|
| 24 | + |
|
| 25 | +#include <stdlib.h>
|
|
| 26 | +#include <unistd.h>
|
|
| 27 | + |
|
| 28 | +// Here's the pipe into which we will send our signals
|
|
| 29 | +static int io_manager_wakeup_fd = -1;
|
|
| 30 | +static int timer_manager_control_wr_fd = -1;
|
|
| 31 | +// TODO: Eliminate these globals. Put then into the CapIOManager, but the
|
|
| 32 | +// problem is these are shared across all caps, not per cap.
|
|
| 33 | + |
|
| 34 | +#define IO_MANAGER_WAKEUP 0xff
|
|
| 35 | +#define IO_MANAGER_DIE 0xfe
|
|
| 36 | +#define IO_MANAGER_SYNC 0xfd
|
|
| 37 | + |
|
| 38 | +void setTimerManagerControlFd(int fd) {
|
|
| 39 | + RELAXED_STORE(&timer_manager_control_wr_fd, fd);
|
|
| 40 | +}
|
|
| 41 | + |
|
| 42 | +void
|
|
| 43 | +setIOManagerWakeupFd (int fd)
|
|
| 44 | +{
|
|
| 45 | + // only called when THREADED_RTS, but unconditionally
|
|
| 46 | + // compiled here because GHC.Event.Control depends on it.
|
|
| 47 | + SEQ_CST_STORE(&io_manager_wakeup_fd, fd);
|
|
| 48 | +}
|
|
| 49 | + |
|
| 50 | +#if defined(THREADED_RTS)
|
|
| 51 | +void timerManagerNotifySignal(int sig, siginfo_t *info)
|
|
| 52 | +{
|
|
| 53 | + StgWord8 buf[sizeof(siginfo_t) + 1];
|
|
| 54 | + int r;
|
|
| 55 | + |
|
| 56 | + buf[0] = sig;
|
|
| 57 | + if (info == NULL) {
|
|
| 58 | + // info may be NULL on Solaris (see #3790)
|
|
| 59 | + memset(buf+1, 0, sizeof(siginfo_t));
|
|
| 60 | + } else {
|
|
| 61 | + memcpy(buf+1, info, sizeof(siginfo_t));
|
|
| 62 | + }
|
|
| 63 | + |
|
| 64 | + int timer_control_fd = RELAXED_LOAD(&timer_manager_control_wr_fd);
|
|
| 65 | + if (0 <= timer_control_fd)
|
|
| 66 | + {
|
|
| 67 | + r = write(timer_control_fd, buf, sizeof(siginfo_t)+1);
|
|
| 68 | + if (r == -1 && errno == EAGAIN) {
|
|
| 69 | + errorBelch("lost signal due to full pipe: %d\n", sig);
|
|
| 70 | + }
|
|
| 71 | + }
|
|
| 72 | + |
|
| 73 | + // If the IO manager hasn't told us what the FD of the write end
|
|
| 74 | + // of its pipe is, there's not much we can do here, so just ignore
|
|
| 75 | + // the signal..
|
|
| 76 | +}
|
|
| 77 | +#endif
|
|
| 78 | + |
|
| 79 | + |
|
| 80 | +/* -----------------------------------------------------------------------------
|
|
| 81 | + * Wake up at least one IO or timer manager HS thread.
|
|
| 82 | + * -------------------------------------------------------------------------- */
|
|
| 83 | +void
|
|
| 84 | +ioManagerWakeup (void)
|
|
| 85 | +{
|
|
| 86 | + int r;
|
|
| 87 | + const int wakeup_fd = SEQ_CST_LOAD(&io_manager_wakeup_fd);
|
|
| 88 | + // Wake up the IO Manager thread by sending a byte down its pipe
|
|
| 89 | + if (wakeup_fd >= 0) {
|
|
| 90 | +#if defined(HAVE_EVENTFD)
|
|
| 91 | + StgWord64 n = (StgWord64)IO_MANAGER_WAKEUP;
|
|
| 92 | + r = write(wakeup_fd, (char *) &n, 8);
|
|
| 93 | +#else
|
|
| 94 | + StgWord8 byte = (StgWord8)IO_MANAGER_WAKEUP;
|
|
| 95 | + r = write(wakeup_fd, &byte, 1);
|
|
| 96 | +#endif
|
|
| 97 | + /* N.B. If the TimerManager is shutting down as we run this
|
|
| 98 | + * then there is a possibility that our first read of
|
|
| 99 | + * io_manager_wakeup_fd is non-negative, but before we get to the
|
|
| 100 | + * write the file is closed. If this occurs, io_manager_wakeup_fd
|
|
| 101 | + * will be written into with -1 (GHC.Event.Control does this prior
|
|
| 102 | + * to closing), so checking this allows us to distinguish this case.
|
|
| 103 | + * To ensure we observe the correct ordering, we declare the
|
|
| 104 | + * io_manager_wakeup_fd as volatile.
|
|
| 105 | + * Since this is not an error condition, we do not print the error
|
|
| 106 | + * message in this case.
|
|
| 107 | + */
|
|
| 108 | + if (r == -1 && SEQ_CST_LOAD(&io_manager_wakeup_fd) >= 0) {
|
|
| 109 | + sysErrorBelch("ioManagerWakeup: write");
|
|
| 110 | + }
|
|
| 111 | + }
|
|
| 112 | +}
|
|
| 113 | + |
|
| 114 | +#if defined(THREADED_RTS)
|
|
| 115 | +void
|
|
| 116 | +ioManagerDie (void)
|
|
| 117 | +{
|
|
| 118 | + StgWord8 byte = (StgWord8)IO_MANAGER_DIE;
|
|
| 119 | + uint32_t i;
|
|
| 120 | + int r;
|
|
| 121 | + |
|
| 122 | + {
|
|
| 123 | + // Shut down timer manager
|
|
| 124 | + const int fd = RELAXED_LOAD(&timer_manager_control_wr_fd);
|
|
| 125 | + if (0 <= fd) {
|
|
| 126 | + r = write(fd, &byte, 1);
|
|
| 127 | + if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
|
|
| 128 | + RELAXED_STORE(&timer_manager_control_wr_fd, -1);
|
|
| 129 | + }
|
|
| 130 | + }
|
|
| 131 | + |
|
| 132 | + {
|
|
| 133 | + // Shut down IO managers
|
|
| 134 | + for (i=0; i < getNumCapabilities(); i++) {
|
|
| 135 | + const int fd = RELAXED_LOAD(&getCapability(i)->iomgr->control_fd);
|
|
| 136 | + if (0 <= fd) {
|
|
| 137 | + r = write(fd, &byte, 1);
|
|
| 138 | + if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
|
|
| 139 | + RELAXED_STORE(&getCapability(i)->iomgr->control_fd, -1);
|
|
| 140 | + }
|
|
| 141 | + }
|
|
| 142 | + }
|
|
| 143 | +}
|
|
| 144 | + |
|
| 145 | +void
|
|
| 146 | +ioManagerStartCap (Capability **cap)
|
|
| 147 | +{
|
|
| 148 | + rts_evalIO(cap,ensureIOManagerIsRunning_closure,NULL);
|
|
| 149 | +}
|
|
| 150 | + |
|
| 151 | +void
|
|
| 152 | +ioManagerStart (void)
|
|
| 153 | +{
|
|
| 154 | + // Make sure the IO manager thread is running
|
|
| 155 | + Capability *cap;
|
|
| 156 | + if (SEQ_CST_LOAD(&timer_manager_control_wr_fd) < 0 || SEQ_CST_LOAD(&io_manager_wakeup_fd) < 0) {
|
|
| 157 | + cap = rts_lock();
|
|
| 158 | + ioManagerStartCap(&cap);
|
|
| 159 | + rts_unlock(cap);
|
|
| 160 | + }
|
|
| 161 | +}
|
|
| 162 | +#endif
|
|
| 163 | + |
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team, 1998-2005
|
|
| 4 | + *
|
|
| 5 | + * Signal processing / handling.
|
|
| 6 | + *
|
|
| 7 | + * ---------------------------------------------------------------------------*/
|
|
| 8 | + |
|
| 9 | +#pragma once
|
|
| 10 | + |
|
| 11 | +#include "IOManager.h"
|
|
| 12 | + |
|
| 13 | +#if defined(HAVE_SIGNAL_H)
|
|
| 14 | +# include <signal.h>
|
|
| 15 | +#endif
|
|
| 16 | + |
|
| 17 | +#include "BeginPrivate.h"
|
|
| 18 | + |
|
| 19 | +/* Communicating with the IO manager thread (see GHC.Conc).
|
|
| 20 | + */
|
|
| 21 | +void ioManagerWakeup (void);
|
|
| 22 | +#if defined(THREADED_RTS)
|
|
| 23 | +void ioManagerDie (void);
|
|
| 24 | +void ioManagerStart (void);
|
|
| 25 | +void ioManagerStartCap (/* inout */ Capability **cap);
|
|
| 26 | + |
|
| 27 | +void timerManagerNotifySignal(int sig, siginfo_t *info);
|
|
| 28 | +#endif
|
|
| 29 | + |
|
| 30 | +#include "EndPrivate.h" |
| ... | ... | @@ -41,6 +41,7 @@ |
| 41 | 41 | |
| 42 | 42 | #include "IOManagerInternals.h"
|
| 43 | 43 | #include "Timeout.h"
|
| 44 | +#include "FdWakeup.h"
|
|
| 44 | 45 | |
| 45 | 46 | /******************************************************************************
|
| 46 | 47 | |
| ... | ... | @@ -107,8 +108,9 @@ timeout (if any) as the poll() timeout parameter. |
| 107 | 108 | The CapIOManager structure for this I/O manager contains:
|
| 108 | 109 | |
| 109 | 110 | ClosureTable aiop_table;
|
| 110 | - struct pollfd *aiop_poll_table;
|
|
| 111 | + struct pollfd *aiop_poll_table, *full_poll_table;
|
|
| 111 | 112 | StgTimeoutQueue *timeout_queue;
|
| 113 | + int wakeup_fd_r, wakeup_fd_w;
|
|
| 112 | 114 | |
| 113 | 115 | We also support the Linux-specific ppoll API which supports higher resolution
|
| 114 | 116 | time delays -- nanoseconds rather than milliseconds as in classic poll(). It
|
| ... | ... | @@ -117,6 +119,15 @@ also allows the signal mask to be adjusted, but we do not make use of this. |
| 117 | 119 | int ppoll(struct pollfd *fds, nfds_t nfds,
|
| 118 | 120 | const struct timespec *tmo_p, const sigset_t *sigmask);
|
| 119 | 121 | |
| 122 | +We have both aiop_poll_table and full_poll_table. This is to cope with needing
|
|
| 123 | +to wait on the special extra file descriptor wakeup_fd_r. This fd is used to
|
|
| 124 | +support waking the I/O manager when we are blocked in a poll call. This
|
|
| 125 | +requires waiting on an extra fd that has no corresponding entry in the
|
|
| 126 | +aiop_table. To manage this quirk, we alias the aiop_poll_table to be the tail
|
|
| 127 | +of the full_poll_table and have the first entry of the full_poll_table be the
|
|
| 128 | +wakeup_fd_r. This means the aiop_poll_table indicies match up exactly with the
|
|
| 129 | +aiop_table, but still allows the full_poll_table to have an extra entry.
|
|
| 130 | + |
|
| 120 | 131 | ******************************************************************************/
|
| 121 | 132 | |
| 122 | 133 | /* Forward declarations */
|
| ... | ... | @@ -129,8 +140,31 @@ static void reportPollError(int res, nfds_t nfds) STG_NORETURN; |
| 129 | 140 | void initCapabilityIOManagerPoll(CapIOManager *iomgr)
|
| 130 | 141 | {
|
| 131 | 142 | initClosureTable(&iomgr->aiop_table, ClosureTableCompact);
|
| 132 | - iomgr->aiop_poll_table = NULL;
|
|
| 133 | 143 | iomgr->timeout_queue = emptyTimeoutQueue();
|
| 144 | + |
|
| 145 | + newFdWakeup(&iomgr->wakeup_fd_r, &iomgr->wakeup_fd_w);
|
|
| 146 | + |
|
| 147 | + iomgr->full_poll_table = stgMallocBytes(sizeof(struct pollfd) /* size 1 */,
|
|
| 148 | + "initCapabilityIOManagerPoll");
|
|
| 149 | + iomgr->full_poll_table[0] = (struct pollfd) {
|
|
| 150 | + .fd = iomgr->wakeup_fd_r,
|
|
| 151 | + .events = POLLIN,
|
|
| 152 | + .revents = 0
|
|
| 153 | + };
|
|
| 154 | + iomgr->aiop_poll_table = iomgr->full_poll_table+1; /* hence empty */
|
|
| 155 | +}
|
|
| 156 | + |
|
| 157 | + |
|
| 158 | +void freeCapabilityIOManagerPoll(CapIOManager *iomgr)
|
|
| 159 | +{
|
|
| 160 | + stgFree(iomgr->full_poll_table);
|
|
| 161 | + closeFdWakeup(iomgr->wakeup_fd_r, iomgr->wakeup_fd_w);
|
|
| 162 | +}
|
|
| 163 | + |
|
| 164 | + |
|
| 165 | +void wakeupIOManagerPoll(CapIOManager *iomgr)
|
|
| 166 | +{
|
|
| 167 | + sendFdWakeup(iomgr->wakeup_fd_w);
|
|
| 134 | 168 | }
|
| 135 | 169 | |
| 136 | 170 | |
| ... | ... | @@ -275,7 +309,7 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop) |
| 275 | 309 | }
|
| 276 | 310 | |
| 277 | 311 | |
| 278 | -static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
|
|
| 312 | +static bool processIOCompletions(CapIOManager *iomgr, int ncompletions)
|
|
| 279 | 313 | {
|
| 280 | 314 | /* The scheme we use with poll is that we have a dense poll table, and a
|
| 281 | 315 | * corresponding table that maps to the closure table index. The poll
|
| ... | ... | @@ -285,6 +319,19 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions) |
| 285 | 319 | */
|
| 286 | 320 | debugTrace(DEBUG_iomanager, "processIOCompletions(ncompletions = %d)",
|
| 287 | 321 | ncompletions);
|
| 322 | + |
|
| 323 | + bool wakeup;
|
|
| 324 | + /* If the wakeup_fd_r is ready, collect it */
|
|
| 325 | + if (iomgr->full_poll_table[0].revents) {
|
|
| 326 | + ASSERT(iomgr->full_poll_table[0].fd == iomgr->wakeup_fd_r);
|
|
| 327 | + collectFdWakeup(iomgr->wakeup_fd_r);
|
|
| 328 | + ncompletions--;
|
|
| 329 | + wakeup = true;
|
|
| 330 | + debugTrace(DEBUG_iomanager, "Received wakeup in poll I/O manager.");
|
|
| 331 | + } else {
|
|
| 332 | + wakeup = false;
|
|
| 333 | + }
|
|
| 334 | + |
|
| 288 | 335 | struct pollfd *aiop_poll_table = iomgr->aiop_poll_table;
|
| 289 | 336 | int n = ncompletions;
|
| 290 | 337 | int i = 0;
|
| ... | ... | @@ -337,11 +384,14 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions) |
| 337 | 384 | i++;
|
| 338 | 385 | }
|
| 339 | 386 | }
|
| 387 | + return wakeup;
|
|
| 340 | 388 | }
|
| 341 | 389 | |
| 342 | 390 | |
| 343 | 391 | void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
|
| 344 | 392 | {
|
| 393 | + ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1);
|
|
| 394 | + |
|
| 345 | 395 | if (!isEmptyTimeoutQueue(iomgr->timeout_queue)) {
|
| 346 | 396 | Time now = getProcessElapsedTime();
|
| 347 | 397 | processTimeoutCompletions(iomgr, now);
|
| ... | ... | @@ -349,20 +399,20 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) |
| 349 | 399 | |
| 350 | 400 | if (!isEmptyClosureTable(&iomgr->aiop_table)) {
|
| 351 | 401 | |
| 352 | - nfds_t nfds = sizeClosureTable(&iomgr->aiop_table);
|
|
| 402 | + nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1;
|
|
| 353 | 403 | |
| 354 | 404 | /* Poll for I/O readiness, without waiting. */
|
| 355 | 405 | #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
|
| 356 | 406 | /* We could use poll here, since we use no timeout, but for
|
| 357 | 407 | consistency we use the same syscall as at the other call site. */
|
| 358 | 408 | struct timespec tv = (struct timespec) { .tv_sec = 0, .tv_nsec = 0 };
|
| 359 | - int res = ppoll(iomgr->aiop_poll_table, nfds, &tv, NULL);
|
|
| 409 | + int res = ppoll(iomgr->full_poll_table, nfds, &tv, NULL);
|
|
| 360 | 410 | |
| 361 | 411 | debugTrace(DEBUG_iomanager,
|
| 362 | 412 | "ppoll(nfds = %d, timeout.sec = 0, timeout.nsec = 0) = %d",
|
| 363 | 413 | nfds, res);
|
| 364 | 414 | #else
|
| 365 | - int res = poll(iomgr->aiop_poll_table, nfds, 0);
|
|
| 415 | + int res = poll(iomgr->full_poll_table, nfds, 0);
|
|
| 366 | 416 | |
| 367 | 417 | debugTrace(DEBUG_iomanager,
|
| 368 | 418 | "poll(nfds = %d, timeout_ms = 0) = %d",
|
| ... | ... | @@ -390,6 +440,10 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) |
| 390 | 440 | |
| 391 | 441 | void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
|
| 392 | 442 | {
|
| 443 | + bool wakeup = false; /* got woken up via wakeupIOManager */
|
|
| 444 | + |
|
| 445 | + ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1);
|
|
| 446 | + |
|
| 393 | 447 | /* Loop until we've woken up some threads. This loop is needed because the
|
| 394 | 448 | * poll() timing isn't accurate, we sometimes sleep for a while but not
|
| 395 | 449 | * long enough to wake up a thread in a threadDelay. Or we may need to
|
| ... | ... | @@ -422,9 +476,9 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) |
| 422 | 476 | #endif
|
| 423 | 477 | |
| 424 | 478 | /* Check for I/O readiness, possibly waiting. */
|
| 425 | - nfds_t nfds = sizeClosureTable(&iomgr->aiop_table);
|
|
| 479 | + nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1;
|
|
| 426 | 480 | #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
|
| 427 | - int res = ppoll(iomgr->aiop_poll_table, nfds, timeout_ns, NULL);
|
|
| 481 | + int res = ppoll(iomgr->full_poll_table, nfds, timeout_ns, NULL);
|
|
| 428 | 482 | |
| 429 | 483 | debugTrace(DEBUG_iomanager,
|
| 430 | 484 | "ppoll(nfds = %d, timeout.sec = %d, timeout.nsec = %d) = %d",
|
| ... | ... | @@ -432,7 +486,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) |
| 432 | 486 | timeout_ns == NULL ? 0 : timeout_ns->tv_nsec,
|
| 433 | 487 | res);
|
| 434 | 488 | #else
|
| 435 | - int res = poll(iomgr->aiop_poll_table, nfds, timeout_ms);
|
|
| 489 | + int res = poll(iomgr->full_poll_table, nfds, timeout_ms);
|
|
| 436 | 490 | |
| 437 | 491 | debugTrace(DEBUG_iomanager,
|
| 438 | 492 | "poll(nfds = %d, timeout_ms = %d) = %d",
|
| ... | ... | @@ -454,7 +508,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) |
| 454 | 508 | } else if (res > 0) {
|
| 455 | 509 | int ncompletions = res;
|
| 456 | 510 | ASSERT(ncompletions <= (int)nfds);
|
| 457 | - processIOCompletions(iomgr, ncompletions);
|
|
| 511 | + wakeup = processIOCompletions(iomgr, ncompletions);
|
|
| 458 | 512 | |
| 459 | 513 | } else if (errno == EINTR) {
|
| 460 | 514 | /* We got interrupted by a signal. In the non-threaded RTS, if the
|
| ... | ... | @@ -479,6 +533,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) |
| 479 | 533 | }
|
| 480 | 534 | |
| 481 | 535 | } while (emptyRunQueue(iomgr->cap)
|
| 536 | + && !wakeup
|
|
| 482 | 537 | && (getSchedState() == SCHED_RUNNING));
|
| 483 | 538 | }
|
| 484 | 539 | |
| ... | ... | @@ -508,13 +563,17 @@ static bool enlargeTables(CapIOManager *iomgr) |
| 508 | 563 | bool ok = enlargeClosureTable(iomgr->cap, &iomgr->aiop_table, newcapacity);
|
| 509 | 564 | if (RTS_UNLIKELY(!ok)) return false;
|
| 510 | 565 | |
| 511 | - /* Update the auxiliary aiop_poll_table to match */
|
|
| 512 | - struct pollfd *aiop_poll_table;
|
|
| 513 | - aiop_poll_table = stgReallocBytes(iomgr->aiop_poll_table,
|
|
| 514 | - sizeof(struct pollfd) * newcapacity,
|
|
| 515 | - "Poll.c: enlargeTables");
|
|
| 516 | - iomgr->aiop_poll_table = aiop_poll_table;
|
|
| 566 | + /* Update the auxiliary aiop_poll_table to match. The full_poll_table is
|
|
| 567 | + * one bigger than the aiop_poll_table, since it has an extra entry at the
|
|
| 568 | + * front for wakeup_fd_r, with no corresponding aiop. */
|
|
| 569 | + iomgr->full_poll_table =
|
|
| 570 | + stgReallocBytes(iomgr->full_poll_table,
|
|
| 571 | + sizeof(struct pollfd) * (newcapacity+1),
|
|
| 572 | + "Poll.c: enlargeTables");
|
|
| 573 | + iomgr->aiop_poll_table = iomgr->full_poll_table+1;
|
|
| 574 | + |
|
| 517 | 575 | /* Initialise the new part of the aiop_poll_table */
|
| 576 | + struct pollfd *aiop_poll_table = iomgr->aiop_poll_table;
|
|
| 518 | 577 | for (int i = oldcapacity; i < newcapacity; i++) {
|
| 519 | 578 | aiop_poll_table[i] = (struct pollfd) {
|
| 520 | 579 | .fd = -1,
|
| ... | ... | @@ -17,6 +17,8 @@ |
| 17 | 17 | #if defined(IOMGR_ENABLED_POLL)
|
| 18 | 18 | |
| 19 | 19 | void initCapabilityIOManagerPoll(CapIOManager *iomgr);
|
| 20 | +void freeCapabilityIOManagerPoll(CapIOManager *iomgr);
|
|
| 21 | +void wakeupIOManagerPoll(CapIOManager *iomgr);
|
|
| 20 | 22 | |
| 21 | 23 | /* Synchronous I/O and timer operations */
|
| 22 | 24 | bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
|
| ... | ... | @@ -22,6 +22,7 @@ |
| 22 | 22 | #include "IOManagerInternals.h"
|
| 23 | 23 | #include "Stats.h"
|
| 24 | 24 | #include "GetTime.h"
|
| 25 | +#include "FdWakeup.h"
|
|
| 25 | 26 | |
| 26 | 27 | # if defined(HAVE_SYS_SELECT_H)
|
| 27 | 28 | # include <sys/select.h>
|
| ... | ... | @@ -54,6 +55,25 @@ |
| 54 | 55 | #define TimeToLowResTimeRoundUp(t) (t)
|
| 55 | 56 | #endif
|
| 56 | 57 | |
| 58 | +void initCapabilityIOManagerSelect(CapIOManager *iomgr)
|
|
| 59 | +{
|
|
| 60 | + iomgr->blocked_queue_hd = END_TSO_QUEUE;
|
|
| 61 | + iomgr->blocked_queue_tl = END_TSO_QUEUE;
|
|
| 62 | + iomgr->sleeping_queue = END_TSO_QUEUE;
|
|
| 63 | + |
|
| 64 | + newFdWakeup(&iomgr->wakeup_fd_r, &iomgr->wakeup_fd_w);
|
|
| 65 | +}
|
|
| 66 | + |
|
| 67 | +void freeCapabilityIOManagerSelect(CapIOManager *iomgr)
|
|
| 68 | +{
|
|
| 69 | + closeFdWakeup(iomgr->wakeup_fd_r, iomgr->wakeup_fd_w);
|
|
| 70 | +}
|
|
| 71 | + |
|
| 72 | +void wakeupIOManagerSelect(CapIOManager *iomgr)
|
|
| 73 | +{
|
|
| 74 | + sendFdWakeup(iomgr->wakeup_fd_w);
|
|
| 75 | +}
|
|
| 76 | + |
|
| 57 | 77 | /*
|
| 58 | 78 | * Return the time since the program started, in LowResTime,
|
| 59 | 79 | * rounded down.
|
| ... | ... | @@ -225,6 +245,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) |
| 225 | 245 | bool seen_bad_fd = false;
|
| 226 | 246 | struct timeval tv, *ptv;
|
| 227 | 247 | LowResTime now;
|
| 248 | + bool wakeup = false; /* got woken up via wakeupIOManager */
|
|
| 228 | 249 | |
| 229 | 250 | IF_DEBUG(scheduler,
|
| 230 | 251 | debugBelch("scheduler: checking for threads blocked on I/O");
|
| ... | ... | @@ -252,6 +273,13 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) |
| 252 | 273 | FD_ZERO(&rfd);
|
| 253 | 274 | FD_ZERO(&wfd);
|
| 254 | 275 | |
| 276 | + /* We're always interested in our wakeup fd */
|
|
| 277 | + {
|
|
| 278 | + int fd = iomgr->wakeup_fd_r;
|
|
| 279 | + maxfd = (fd > maxfd) ? fd : maxfd;
|
|
| 280 | + FD_SET(fd, &rfd);
|
|
| 281 | + }
|
|
| 282 | + |
|
| 255 | 283 | for(tso = iomgr->blocked_queue_hd;
|
| 256 | 284 | tso != END_TSO_QUEUE;
|
| 257 | 285 | tso = next) {
|
| ... | ... | @@ -376,6 +404,13 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) |
| 376 | 404 | }
|
| 377 | 405 | }
|
| 378 | 406 | |
| 407 | + /* If the wakeup_fd_r is ready, collect it */
|
|
| 408 | + if (FD_ISSET(iomgr->wakeup_fd_r, &rfd)) {
|
|
| 409 | + collectFdWakeup(iomgr->wakeup_fd_r);
|
|
| 410 | + wakeup = true;
|
|
| 411 | + debugTrace(DEBUG_iomanager, "Received wakeup 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,8 @@ 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 | + && !wakeup);
|
|
| 462 | 498 | }
|
| 463 | 499 | |
| 464 | 500 | #endif /* IOMGR_ENABLED_SELECT */ |
| ... | ... | @@ -15,6 +15,10 @@ typedef StgWord LowResTime; |
| 15 | 15 | |
| 16 | 16 | LowResTime getDelayTarget (HsInt us);
|
| 17 | 17 | |
| 18 | +void initCapabilityIOManagerSelect(CapIOManager *iomgr);
|
|
| 19 | +void freeCapabilityIOManagerSelect(CapIOManager *iomgr);
|
|
| 20 | +void wakeupIOManagerSelect(CapIOManager *iomgr);
|
|
| 21 | + |
|
| 18 | 22 | void awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait);
|
| 19 | 23 | |
| 20 | 24 | #include "EndPrivate.h"
|
| ... | ... | @@ -9,21 +9,12 @@ |
| 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 | -#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"
|
|
| 17 | +#include "MIO.h"
|
|
| 27 | 18 | |
| 28 | 19 | #if defined(alpha_HOST_ARCH)
|
| 29 | 20 | # if defined(linux_HOST_OS)
|
| ... | ... | @@ -45,10 +36,6 @@ |
| 45 | 36 | # include <errno.h>
|
| 46 | 37 | #endif
|
| 47 | 38 | |
| 48 | -#if defined(HAVE_EVENTFD_H)
|
|
| 49 | -# include <sys/eventfd.h>
|
|
| 50 | -#endif
|
|
| 51 | - |
|
| 52 | 39 | #if defined(HAVE_TERMIOS_H)
|
| 53 | 40 | #include <termios.h>
|
| 54 | 41 | #endif
|
| ... | ... | @@ -134,110 +121,6 @@ more_handlers(int sig) |
| 134 | 121 | nHandlers = sig + 1;
|
| 135 | 122 | }
|
| 136 | 123 | |
| 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 | 124 | #if !defined(THREADED_RTS)
|
| 242 | 125 | |
| 243 | 126 | #define N_PENDING_HANDLERS 16
|
| ... | ... | @@ -260,31 +143,9 @@ generic_handler(int sig USED_IF_THREADS, |
| 260 | 143 | void *p STG_UNUSED)
|
| 261 | 144 | {
|
| 262 | 145 | #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 | - |
|
| 146 | + //TODO: This calls MIO directly. We should go via IOManager API.
|
|
| 147 | + // The IOManager API should be extended to cover signals.
|
|
| 148 | + timerManagerNotifySignal(sig, info);
|
|
| 288 | 149 | #else /* not THREADED_RTS */
|
| 289 | 150 | |
| 290 | 151 | /* Can't call allocate from here. Probably can't call malloc
|
| ... | ... | @@ -27,18 +27,6 @@ void startSignalHandlers(Capability *cap); |
| 27 | 27 | |
| 28 | 28 | void install_vtalrm_handler(int sig, TickProc handle_tick);
|
| 29 | 29 | |
| 30 | -/* Communicating with the IO manager thread (see GHC.Conc).
|
|
| 31 | - *
|
|
| 32 | - * TODO: these I/O manager things are not related to signals and ought to live
|
|
| 33 | - * elsewhere, e.g. in a module specifically for the I/O manager.
|
|
| 34 | - */
|
|
| 35 | -void ioManagerWakeup (void);
|
|
| 36 | -#if defined(THREADED_RTS)
|
|
| 37 | -void ioManagerDie (void);
|
|
| 38 | -void ioManagerStart (void);
|
|
| 39 | -void ioManagerStartCap (/* inout */ Capability **cap);
|
|
| 40 | -#endif
|
|
| 41 | - |
|
| 42 | 30 | extern StgInt *signal_handlers;
|
| 43 | 31 | |
| 44 | 32 | #include "EndPrivate.h" |
| ... | ... | @@ -569,6 +569,7 @@ library |
| 569 | 569 | wasm/OSThreads.c
|
| 570 | 570 | wasm/JSFFI.c
|
| 571 | 571 | wasm/JSFFIGlobals.c
|
| 572 | + posix/FdWakeup.c
|
|
| 572 | 573 | posix/Select.c
|
| 573 | 574 | posix/Poll.c
|
| 574 | 575 | posix/Timeout.c
|
| ... | ... | @@ -581,6 +582,8 @@ library |
| 581 | 582 | posix/Ticker.c
|
| 582 | 583 | posix/OSMem.c
|
| 583 | 584 | posix/OSThreads.c
|
| 585 | + posix/FdWakeup.c
|
|
| 586 | + posix/MIO.c
|
|
| 584 | 587 | posix/Poll.c
|
| 585 | 588 | posix/Select.c
|
| 586 | 589 | posix/Signals.c
|