Duncan Coutts pushed to branch wip/io-manager-deadlock-detection at Glasgow Haskell Compiler / GHC Commits: f340f464 by Duncan Coutts at 2026-02-11T22:48:35+00:00 Make the IOManager API use CapIOManager rather than Capability This makes the API somewhat more self-contained and more consistent. Now the IOManager API and each of the backends takes just the I/O manager structure. Previously we had a bit of a mixture, depending on whether the function needed access to the Capability or just the CapIOManager. We still need access to the cap, so we introduce a back reference to reach the capability, via iomgr->cap. Convert all uses in select and poll backends, but not win32 ones. Convert callers in the scheduler and elsewhere. Also convert the three CMM primops that call IOManager APIs. They just need to use Capability_iomgr(MyCapability()). - - - - - 13 changed files: - rts/Capability.c - rts/IOManager.c - rts/IOManager.h - rts/IOManagerInternals.h - rts/PrimOps.cmm - rts/RaiseAsync.c - rts/Schedule.c - rts/posix/Poll.c - rts/posix/Poll.h - rts/posix/Select.c - rts/posix/Select.h - rts/posix/Timeout.c - rts/posix/Timeout.h Changes: ===================================== rts/Capability.c ===================================== @@ -286,7 +286,8 @@ initCapability (Capability *cap, uint32_t i) #endif cap->total_allocated = 0; - initCapabilityIOManager(cap); /* initialises cap->iomgr */ + cap->iomgr = allocCapabilityIOManager(cap); + initCapabilityIOManager(cap->iomgr); cap->f.stgEagerBlackholeInfo = (W_)&__stg_EAGER_BLACKHOLE_info; cap->f.stgGCEnter1 = (StgFunPtr)__stg_gc_enter_1; @@ -1344,7 +1345,7 @@ markCapability (evac_fn evac, void *user, Capability *cap, } #endif - markCapabilityIOManager(evac, user, cap); + markCapabilityIOManager(evac, user, cap->iomgr); // Free STM structures for this Capability stmPreGCHook(cap); ===================================== rts/IOManager.c ===================================== @@ -316,22 +316,29 @@ char * showIOManager(void) } } +/* Allocate a CapIOManager for a given Capability. Having this helps us keep + * struct CapIOManager opaque from most of the rest of the RTS. + */ +CapIOManager *allocCapabilityIOManager(Capability *cap) +{ + CapIOManager *iomgr = stgMallocBytes(sizeof(CapIOManager), + "allocCapabilityIOManager"); + iomgr->cap = cap; /* link back */ + return iomgr; +} + -/* Allocate and initialise the per-capability CapIOManager that lives in each - * Capability. Called from initCapability(), which is done in the RTS startup - * in initCapabilities(), and later at runtime via setNumCapabilities(). +/* Initialise the per-capability CapIOManager that lives in each Capability. + * Called from initCapability(), which is done in the RTS startup in + * initCapabilities(), and later at runtime via setNumCapabilities(). * * Note that during RTS startup this is called _before_ the storage manager * is initialised, so this is not allowed to allocate on the GC heap. */ -void initCapabilityIOManager(Capability *cap) +void initCapabilityIOManager(CapIOManager *iomgr) { debugTrace(DEBUG_iomanager, "initialising I/O manager %s for cap %d", - showIOManager(), cap->no); - - CapIOManager *iomgr = - (CapIOManager *) stgMallocBytes(sizeof(CapIOManager), - "initCapabilityIOManager"); + showIOManager(), iomgr->cap->no); switch (iomgr_type) { #if defined(IOMGR_ENABLED_SELECT) @@ -363,8 +370,6 @@ void initCapabilityIOManager(Capability *cap) default: break; } - - cap->iomgr = iomgr; } @@ -436,7 +441,7 @@ void initIOManager(void) /* Called from forkProcess in the child process on the surviving capability. */ void -initIOManagerAfterFork(Capability **pcap) +initIOManagerAfterFork(CapIOManager *iomgr, Capability **pcap) { switch (iomgr_type) { @@ -467,7 +472,7 @@ initIOManagerAfterFork(Capability **pcap) /* Called from setNumCapabilities. */ -void notifyIOManagerCapabilitiesChanged(Capability **pcap) +void notifyIOManagerCapabilitiesChanged(CapIOManager *iomgr, Capability **pcap) { switch (iomgr_type) { #if defined(IOMGR_ENABLED_MIO_POSIX) @@ -572,38 +577,29 @@ void wakeupIOManager(void) } } -void markCapabilityIOManager(evac_fn evac, void *user, Capability *cap) +void markCapabilityIOManager(evac_fn evac, void *user, CapIOManager *iomgr) { switch (iomgr_type) { #if defined(IOMGR_ENABLED_SELECT) case IO_MANAGER_SELECT: - { - CapIOManager *iomgr = cap->iomgr; evac(user, (StgClosure **)(void *)&iomgr->blocked_queue_hd); evac(user, (StgClosure **)(void *)&iomgr->blocked_queue_tl); evac(user, (StgClosure **)(void *)&iomgr->sleeping_queue); break; - } #endif #if defined(IOMGR_ENABLED_POLL) case IO_MANAGER_POLL: - { - CapIOManager *iomgr = cap->iomgr; markClosureTable(evac, user, &iomgr->aiop_table); evac(user, (StgClosure **)(void *)&iomgr->timeout_queue); break; - } #endif #if defined(IOMGR_ENABLED_WIN32_LEGACY) case IO_MANAGER_WIN32_LEGACY: - { - CapIOManager *iomgr = cap->iomgr; evac(user, (StgClosure **)(void *)&iomgr->blocked_queue_hd); evac(user, (StgClosure **)(void *)&iomgr->blocked_queue_tl); break; - } #endif default: break; @@ -665,29 +661,23 @@ setIOManagerControlFd(uint32_t cap_no, int fd) { #endif -bool anyPendingTimeoutsOrIO(Capability *cap) +bool anyPendingTimeoutsOrIO(CapIOManager *iomgr) { switch (iomgr_type) { #if defined(IOMGR_ENABLED_SELECT) case IO_MANAGER_SELECT: - { - CapIOManager *iomgr = cap->iomgr; return (iomgr->blocked_queue_hd != END_TSO_QUEUE) || (iomgr->sleeping_queue != END_TSO_QUEUE); - } #endif #if defined(IOMGR_ENABLED_POLL) case IO_MANAGER_POLL: - return anyPendingTimeoutsOrIOPoll(cap->iomgr); + return anyPendingTimeoutsOrIOPoll(iomgr); #endif #if defined(IOMGR_ENABLED_WIN32_LEGACY) case IO_MANAGER_WIN32_LEGACY: - { - CapIOManager *iomgr = cap->iomgr; return (iomgr->blocked_queue_hd != END_TSO_QUEUE); - } #endif /* For the purpose of the scheduler, the threaded I/O managers never have @@ -729,19 +719,19 @@ bool anyPendingTimeoutsOrIO(Capability *cap) } -void pollCompletedTimeoutsOrIO(Capability *cap) +void pollCompletedTimeoutsOrIO(CapIOManager *iomgr) { debugTrace(DEBUG_iomanager, "polling for completed IO or timeouts"); switch (iomgr_type) { #if defined(IOMGR_ENABLED_SELECT) case IO_MANAGER_SELECT: - awaitCompletedTimeoutsOrIOSelect(cap, false); + awaitCompletedTimeoutsOrIOSelect(iomgr, false); break; #endif #if defined(IOMGR_ENABLED_POLL) case IO_MANAGER_POLL: - pollCompletedTimeoutsOrIOPoll(cap); + pollCompletedTimeoutsOrIOPoll(iomgr); break; #endif @@ -753,7 +743,7 @@ void pollCompletedTimeoutsOrIO(Capability *cap) #if defined(IOMGR_ENABLED_WINIO) case IO_MANAGER_WINIO: #endif - awaitCompletedTimeoutsOrIOWin32(cap, false); + awaitCompletedTimeoutsOrIOWin32(iomgr->cap, false); break; #endif default: @@ -762,19 +752,19 @@ void pollCompletedTimeoutsOrIO(Capability *cap) } -void awaitCompletedTimeoutsOrIO(Capability *cap) +void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr) { debugTrace(DEBUG_iomanager, "waiting for completed IO or timeouts"); switch (iomgr_type) { #if defined(IOMGR_ENABLED_SELECT) case IO_MANAGER_SELECT: - awaitCompletedTimeoutsOrIOSelect(cap, true); + awaitCompletedTimeoutsOrIOSelect(iomgr, true); break; #endif #if defined(IOMGR_ENABLED_POLL) case IO_MANAGER_POLL: - awaitCompletedTimeoutsOrIOPoll(cap); + awaitCompletedTimeoutsOrIOPoll(iomgr); break; #endif @@ -786,17 +776,18 @@ void awaitCompletedTimeoutsOrIO(Capability *cap) #if defined(IOMGR_ENABLED_WINIO) case IO_MANAGER_WINIO: #endif - awaitCompletedTimeoutsOrIOWin32(cap, true); + awaitCompletedTimeoutsOrIOWin32(iomgr->cap, true); break; #endif default: barf("pollCompletedTimeoutsOrIO not implemented"); } - ASSERT(!emptyRunQueue(cap) || getSchedState() != SCHED_RUNNING); + ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING); } -bool syncIOWaitReady(Capability *cap, +/* CMM primop. Result is true on success, or false on allocation failure. */ +bool syncIOWaitReady(CapIOManager *iomgr, StgTSO *tso, IOReadOrWrite rw, HsInt fd) @@ -812,14 +803,14 @@ bool syncIOWaitReady(Capability *cap, StgWord why_blocked = rw == IORead ? BlockedOnRead : BlockedOnWrite; tso->block_info.fd = fd; RELEASE_STORE(&tso->why_blocked, why_blocked); - appendToIOBlockedQueue(cap, tso); + appendToIOBlockedQueue(iomgr, tso); return true; } #endif #if defined(IOMGR_ENABLED_POLL) case IO_MANAGER_POLL: ASSERT(tso->why_blocked == NotBlocked); - return syncIOWaitReadyPoll(cap, tso, rw, fd); + return syncIOWaitReadyPoll(iomgr, tso, rw, fd); #endif default: barf("waitRead# / waitWrite# not available for current I/O manager"); @@ -827,25 +818,29 @@ bool syncIOWaitReady(Capability *cap, } -void syncIOCancel(Capability *cap, StgTSO *tso) +void syncIOCancel(CapIOManager *iomgr, StgTSO *tso) { debugTrace(DEBUG_iomanager, "cancelling I/O for thread %ld", (long) tso->id); switch (iomgr_type) { #if defined(IOMGR_ENABLED_SELECT) case IO_MANAGER_SELECT: - removeThreadFromDeQueue(cap, &cap->iomgr->blocked_queue_hd, - &cap->iomgr->blocked_queue_tl, tso); + removeThreadFromDeQueue(iomgr->cap, + &iomgr->blocked_queue_hd, + &iomgr->blocked_queue_tl, + tso); break; #endif #if defined(IOMGR_ENABLED_POLL) case IO_MANAGER_POLL: - syncIOCancelPoll(cap, tso); + syncIOCancelPoll(iomgr, tso); break; #endif #if defined(IOMGR_ENABLED_WIN32_LEGACY) case IO_MANAGER_WIN32_LEGACY: - removeThreadFromDeQueue(cap, &cap->iomgr->blocked_queue_hd, - &cap->iomgr->blocked_queue_tl, tso); + removeThreadFromDeQueue(iomgr->cap, + &iomgr->blocked_queue_hd, + &iomgr->blocked_queue_tl, + tso); abandonWorkRequest(tso->block_info.async_result->reqID); break; #endif @@ -856,11 +851,12 @@ void syncIOCancel(Capability *cap, StgTSO *tso) #if defined(IOMGR_ENABLED_SELECT) -static void insertIntoSleepingQueue(Capability *cap, StgTSO *tso, LowResTime target); +static void insertIntoSleepingQueue(CapIOManager *iomgr, StgTSO *tso, LowResTime target); #endif -bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay) +/* CMM primop. Result is true on success, or false on allocation failure. */ +bool syncDelay(CapIOManager *iomgr, StgTSO *tso, HsInt us_delay) { debugTrace(DEBUG_iomanager, "thread %ld waiting for %lld us", tso->id, us_delay); ASSERT(tso->why_blocked == NotBlocked); @@ -871,13 +867,13 @@ bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay) LowResTime target = getDelayTarget(us_delay); tso->block_info.target = target; RELEASE_STORE(&tso->why_blocked, BlockedOnDelay); - insertIntoSleepingQueue(cap, tso, target); + insertIntoSleepingQueue(iomgr, tso, target); return true; } #endif #if defined(IOMGR_ENABLED_POLL) case IO_MANAGER_POLL: - return syncDelayTimeout(cap, tso, us_delay); + return syncDelayTimeout(iomgr, tso, us_delay); #endif #if defined(IOMGR_ENABLED_WIN32_LEGACY) case IO_MANAGER_WIN32_LEGACY: @@ -897,7 +893,7 @@ bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay) * delayed thread on the blocked_queue. */ RELEASE_STORE(&tso->why_blocked, BlockedOnDoProc); - appendToIOBlockedQueue(cap, tso); + appendToIOBlockedQueue(iomgr, tso); return true; } #endif @@ -907,18 +903,18 @@ bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay) } -void syncDelayCancel(Capability *cap, StgTSO *tso) +void syncDelayCancel(CapIOManager *iomgr, StgTSO *tso) { debugTrace(DEBUG_iomanager, "cancelling delay for thread %ld", (long) tso->id); switch (iomgr_type) { #if defined(IOMGR_ENABLED_SELECT) case IO_MANAGER_SELECT: - removeThreadFromQueue(cap, &cap->iomgr->sleeping_queue, tso); + removeThreadFromQueue(iomgr->cap, &iomgr->sleeping_queue, tso); break; #endif #if defined(IOMGR_ENABLED_POLL) case IO_MANAGER_POLL: - syncDelayCancelTimeout(cap, tso); + syncDelayCancelTimeout(iomgr, tso); break; #endif /* Note: no case for IO_MANAGER_WIN32_LEGACY despite it having a case @@ -935,14 +931,13 @@ void syncDelayCancel(Capability *cap, StgTSO *tso) #if defined(IOMGR_ENABLED_SELECT) || defined(IOMGR_ENABLED_WIN32_LEGACY) -void appendToIOBlockedQueue(Capability *cap, StgTSO *tso) +void appendToIOBlockedQueue(CapIOManager *iomgr, StgTSO *tso) { - CapIOManager *iomgr = cap->iomgr; ASSERT(tso->_link == END_TSO_QUEUE); if (iomgr->blocked_queue_hd == END_TSO_QUEUE) { iomgr->blocked_queue_hd = tso; } else { - setTSOLink(cap, iomgr->blocked_queue_tl, tso); + setTSOLink(iomgr->cap, iomgr->blocked_queue_tl, tso); } iomgr->blocked_queue_tl = tso; } @@ -957,9 +952,8 @@ void appendToIOBlockedQueue(Capability *cap, StgTSO *tso) * used. This is a wart that should be excised. */ // TODO: move to Select.c and rename -static void insertIntoSleepingQueue(Capability *cap, StgTSO *tso, LowResTime target) +static void insertIntoSleepingQueue(CapIOManager *iomgr, StgTSO *tso, LowResTime target) { - CapIOManager *iomgr = cap->iomgr; StgTSO *prev = NULL; StgTSO *t = iomgr->sleeping_queue; while (t != END_TSO_QUEUE && t->block_info.target < target) { @@ -971,7 +965,7 @@ static void insertIntoSleepingQueue(Capability *cap, StgTSO *tso, LowResTime tar if (prev == NULL) { iomgr->sleeping_queue = tso; } else { - setTSOLink(cap, prev, tso); + setTSOLink(iomgr->cap, prev, tso); } } #endif ===================================== rts/IOManager.h ===================================== @@ -19,6 +19,7 @@ #pragma once +#include "Capability.h" #include "sm/GC.h" // for evac_fn #include "BeginPrivate.h" @@ -227,11 +228,19 @@ enum IOOpOutcome { void selectIOManager(void); -/* Allocate and initialise the per-capability CapIOManager that lives in each - * Capability. Called from initCapability(), which is done in the RTS startup - * in initCapabilities(), and later at runtime via setNumCapabilities(). +/* Allocate a CapIOManager for a given Capability. Having this helps us keep + * struct CapIOManager opaque from most of the rest of the RTS. */ -void initCapabilityIOManager(Capability *cap); +CapIOManager *allocCapabilityIOManager(Capability *cap); + +/* Initialise the per-capability CapIOManager that lives in each Capability. + * Called from initCapability(), which is done in the RTS startup in + * initCapabilities(), and later at runtime via setNumCapabilities(). + * + * This is separate from allocCapabilityIOManager so that we can re-initialise + * I/O managers after forkProcess. + */ +void initCapabilityIOManager(CapIOManager *iomgr); /* Init hook: called from hs_init_ghc, very late in the startup after almost @@ -243,10 +252,11 @@ void initIOManager(void); /* Init hook: called from forkProcess in the child process on the surviving * capability. * - * Note that this is synchronous and can run Haskell code, so can change the - * given cap. + * This is synchronous and can run Haskell code, so can change the given cap. + * TODO: it would make for a cleaner API here if this were made asynchronous. */ -void initIOManagerAfterFork(/* inout */ Capability **pcap); +void initIOManagerAfterFork(CapIOManager *iomgr, + /* inout */ Capability **pcap); /* TODO: rationalise initIOManager and initIOManagerAfterFork into a single per-capability init function. @@ -254,8 +264,12 @@ void initIOManagerAfterFork(/* inout */ Capability **pcap); /* Called from setNumCapabilities. + * + * This is synchronous and can run Haskell code, so can change the given cap. + * TODO: it would make for a cleaner API here if this were made asynchronous. */ -void notifyIOManagerCapabilitiesChanged(Capability **pcap); +void notifyIOManagerCapabilitiesChanged(CapIOManager *iomgr, + /* inout */ Capability **pcap); /* Shutdown hooks: called from hs_exit_ before and after the scheduler exits. @@ -288,7 +302,7 @@ void wakeupIOManager(void); /* GC hook: mark any per-capability GC roots the I/O manager uses. */ -void markCapabilityIOManager(evac_fn evac, void *user, Capability *cap); +void markCapabilityIOManager(evac_fn evac, void *user, CapIOManager *iomgr); /* GC hook: scavenge I/O related tso->block_info. Used by scavengeTSO. @@ -305,21 +319,20 @@ typedef enum { IORead, IOWrite } IOReadOrWrite; * necessarily operate on threads. The thread is suspended until the operation * completes. * - * These are called from CMM primops. The ones returing int can perform heap - * allocation, which might fail. They return 0 on success, or n > 0 on heap - * allocation failure, needing n words. The CMM primops should invoke the - * GC to free up at least n words and then retry the operation. + * Some of these are called from CMM primops. The primops returing bool can + * perform heap allocation, which might fail. They return true on success, or + * false on heap allocation failure. */ -/* Result is true on success, or false on allocation failure. */ -bool syncIOWaitReady(Capability *cap, StgTSO *tso, IOReadOrWrite rw, HsInt fd); +/* Called from CMM primop */ +bool syncIOWaitReady(CapIOManager *iomgr, StgTSO *tso, IOReadOrWrite rw, HsInt fd); -void syncIOCancel(Capability *cap, StgTSO *tso); +void syncIOCancel(CapIOManager *iomgr, StgTSO *tso); -/* Result is true on success, or false on allocation failure. */ -bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay); +/* Called from CMM primop */ +bool syncDelay(CapIOManager *iomgr, StgTSO *tso, HsInt us_delay); -void syncDelayCancel(Capability *cap, StgTSO *tso); +void syncDelayCancel(CapIOManager *iomgr, StgTSO *tso); #if defined(IOMGR_ENABLED_SELECT) || defined(IOMGR_ENABLED_WIN32_LEGACY) /* Add a thread to the end of the queue of threads blocked on I/O. @@ -327,7 +340,7 @@ void syncDelayCancel(Capability *cap, StgTSO *tso); * This is used by the select() and the Windows MIO non-threaded I/O manager * implementation. Called from CMM code. */ -void appendToIOBlockedQueue(Capability *cap, StgTSO *tso); +void appendToIOBlockedQueue(CapIOManager *iomgr, StgTSO *tso); #endif /* Check to see if there are any pending timeouts or I/O operations @@ -336,7 +349,7 @@ void appendToIOBlockedQueue(Capability *cap, StgTSO *tso); * This is used by the scheduler as part of deadlock-detection, and the * "context switch as often as possible" test. */ -bool anyPendingTimeoutsOrIO(Capability *cap); +bool anyPendingTimeoutsOrIO(CapIOManager *iomgr); /* If there are any completed I/O operations or expired timers, process the * completions as appropriate (which will typically unblock some waiting @@ -344,7 +357,7 @@ bool anyPendingTimeoutsOrIO(Capability *cap); * * Called from schedule() both *before* and *after* scheduleDetectDeadlock(). */ -void pollCompletedTimeoutsOrIO(Capability *cap); +void pollCompletedTimeoutsOrIO(CapIOManager *iomgr); /* If there are any completed I/O operations or expired timers, process the * completions as appropriate. If there are none, wait until I/O or a timer @@ -360,6 +373,6 @@ void pollCompletedTimeoutsOrIO(Capability *cap); * * Called from schedule() both *before* and *after* scheduleDetectDeadlock(). */ -void awaitCompletedTimeoutsOrIO(Capability *cap); +void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr); #include "EndPrivate.h" ===================================== rts/IOManagerInternals.h ===================================== @@ -24,7 +24,8 @@ /* The per-capability data structures belonging to the I/O manager. * - * It can be accessed as cap->iomgr. + * It can be accessed as cap->iomgr. Or given just the iomgr, you can access + * the owning cap as iomgr->cap. * * The content of the structure is defined conditionally so it is different for * each I/O manager implementation. @@ -33,6 +34,9 @@ */ struct _CapIOManager { + /* Back reference to the containing capability */ + Capability *cap; + #if defined(IOMGR_ENABLED_SELECT) /* Thread queue for threads blocked on I/O completion. */ StgTSO *blocked_queue_hd; ===================================== rts/PrimOps.cmm ===================================== @@ -2279,7 +2279,8 @@ stg_waitReadzh ( W_ fd ) { CBool ok; /* Ok, or heap alloc failure. */ - (ok) = ccall syncIOWaitReady(MyCapability() "ptr", CurrentTSO "ptr", + (ok) = ccall syncIOWaitReady(Capability_iomgr(MyCapability()) "ptr", + CurrentTSO "ptr", /* IORead */ 0::I32, fd); if (ok != 0::CBool) (likely: True) { jump stg_block_noregs(); @@ -2292,7 +2293,8 @@ stg_waitWritezh ( W_ fd ) { CBool ok; /* Ok, or heap alloc failure. */ - (ok) = ccall syncIOWaitReady(MyCapability() "ptr", CurrentTSO "ptr", + (ok) = ccall syncIOWaitReady(Capability_iomgr(MyCapability()) "ptr", + CurrentTSO "ptr", /* IOWrite */ 1::I32, fd); if (ok != 0::CBool) (likely: True) { jump stg_block_noregs(); @@ -2305,7 +2307,8 @@ stg_delayzh ( W_ us_delay ) { CBool ok; /* Ok, or heap alloc failure. */ - (ok) = ccall syncDelay(MyCapability() "ptr", CurrentTSO "ptr", us_delay); + (ok) = ccall syncDelay(Capability_iomgr(MyCapability()) "ptr", + CurrentTSO "ptr", us_delay); if (ok != 0::CBool) (likely: True) { /* Annoyingly, we cannot be consistent with how we wait and resume the ===================================== rts/RaiseAsync.c ===================================== @@ -708,12 +708,12 @@ removeFromQueues(Capability *cap, StgTSO *tso) case BlockedOnWrite: case BlockedOnDoProc: // These blocking reasons are only used by some I/O managers - syncIOCancel(cap, tso); + syncIOCancel(cap->iomgr, tso); goto done; case BlockedOnDelay: // This blocking reasons is only used by some I/O managers - syncDelayCancel(cap, tso); + syncDelayCancel(cap->iomgr, tso); goto done; default: ===================================== rts/Schedule.c ===================================== @@ -409,7 +409,7 @@ schedule (Capability *initialCapability, Task *task) */ if (RtsFlags.ConcFlags.ctxtSwitchTicks == 0 && (!emptyRunQueue(cap) || - anyPendingTimeoutsOrIO(cap))) { + anyPendingTimeoutsOrIO(cap->iomgr))) { RELAXED_STORE(&cap->context_switch, 1); } @@ -923,14 +923,14 @@ scheduleCheckBlockedThreads(Capability *cap USED_IF_NOT_THREADS) * awaitCompletedTimeoutsOrIO below for the case of !defined(THREADED_RTS) * && defined(mingw32_HOST_OS). */ - if (anyPendingTimeoutsOrIO(cap)) + if (anyPendingTimeoutsOrIO(cap->iomgr)) { if (emptyRunQueue(cap)) { // block and wait - awaitCompletedTimeoutsOrIO(cap); + awaitCompletedTimeoutsOrIO(cap->iomgr); } else { // poll but do not wait - pollCompletedTimeoutsOrIO(cap); + pollCompletedTimeoutsOrIO(cap->iomgr); } } #endif @@ -950,7 +950,7 @@ scheduleDetectDeadlock (Capability **pcap, Task *task) * other tasks are waiting for work, we must have a deadlock of * some description. */ - if ( emptyRunQueue(cap) && !anyPendingTimeoutsOrIO(cap) ) + if ( emptyRunQueue(cap) && !anyPendingTimeoutsOrIO(cap->iomgr) ) { #if defined(THREADED_RTS) /* @@ -2232,7 +2232,7 @@ forkProcess(HsStablePtr *entry // like startup event, capabilities, process info etc traceTaskCreate(task, cap); - initIOManagerAfterFork(&cap); + initIOManagerAfterFork(cap->iomgr, &cap); // start timer after the IOManager is initialized // (the idle GC may wake up the IOManager) @@ -2392,7 +2392,7 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS) } // Notify IO manager that the number of capabilities has changed. - notifyIOManagerCapabilitiesChanged(&cap); + notifyIOManagerCapabilitiesChanged(cap->iomgr, &cap); startTimer(); ===================================== rts/posix/Poll.c ===================================== @@ -120,9 +120,9 @@ also allows the signal mask to be adjusted, but we do not make use of this. ******************************************************************************/ /* Forward declarations */ -static bool enlargeTables(Capability *cap, CapIOManager *iomgr); -static void notifyIOCompletion(Capability *cap, StgAsyncIOOp *aiop); -static void ioCancel(Capability *cap, StgAsyncIOOp *aiop); +static bool enlargeTables(CapIOManager *iomgr); +static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop); +static void ioCancel(CapIOManager *iomgr, StgAsyncIOOp *aiop); static void reportPollError(int res, nfds_t nfds) STG_NORETURN; @@ -136,32 +136,31 @@ void initCapabilityIOManagerPoll(CapIOManager *iomgr) /* Used to implement syncIOWaitReady. * Result is true on success, or false on allocation failure. */ -bool syncIOWaitReadyPoll(Capability *cap, StgTSO *tso, +bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso, IOReadOrWrite rw, HsInt fd) { StgAsyncIOOp *aiop; - aiop = (StgAsyncIOOp *)allocateMightFail(cap, sizeofW(StgAsyncIOOp)); + aiop = (StgAsyncIOOp *)allocateMightFail(iomgr->cap, sizeofW(StgAsyncIOOp)); if (RTS_UNLIKELY(aiop == NULL)) return false; - SET_HDR(aiop, &stg_ASYNCIOOP_info, cap->r.rCCCS); + SET_HDR(aiop, &stg_ASYNCIOOP_info, iomgr->cap->r.rCCCS); aiop->notify.tso = tso; aiop->notify_type = NotifyTSO; aiop->live = &stg_ASYNCIO_LIVE0_closure; tso->why_blocked = rw == IORead ? BlockedOnRead : BlockedOnWrite; tso->block_info.aiop = aiop; - return asyncIOWaitReadyPoll(cap, aiop, rw, fd); + return asyncIOWaitReadyPoll(iomgr, aiop, rw, fd); } /* Result is true on success, or false on allocation failure. */ -bool asyncIOWaitReadyPoll(Capability *cap, StgAsyncIOOp *aiop, +bool asyncIOWaitReadyPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop, IOReadOrWrite rw, int fd) { - CapIOManager *iomgr = cap->iomgr; if (RTS_UNLIKELY(isFullClosureTable(&iomgr->aiop_table))) { - bool ok = enlargeTables(cap, iomgr); + bool ok = enlargeTables(iomgr); if (RTS_UNLIKELY(!ok)) return false; } - int ix = insertClosureTable(cap, &iomgr->aiop_table, aiop); + int ix = insertClosureTable(iomgr->cap, &iomgr->aiop_table, aiop); /* We use the aiop_table and aiop_poll_table densely. */ ASSERT(ix == sizeClosureTable(&iomgr->aiop_table) - 1); @@ -169,7 +168,7 @@ bool asyncIOWaitReadyPoll(Capability *cap, StgAsyncIOOp *aiop, /* The syncIO wrapper or CMM primop filled in the notify and live fields, * we fill the rest. */ - aiop->capno = cap->no; + aiop->capno = iomgr->cap->no; aiop->index = ix; aiop->outcome = IOOpOutcomeInFlight; @@ -183,12 +182,12 @@ bool asyncIOWaitReadyPoll(Capability *cap, StgAsyncIOOp *aiop, } -void syncIOCancelPoll(Capability *cap, StgTSO *tso) +void syncIOCancelPoll(CapIOManager *iomgr, StgTSO *tso) { StgAsyncIOOp *aiop = tso->block_info.aiop; ASSERT(aiop->notify_type == NotifyTSO); - ASSERT(indexClosureTable(&cap->iomgr->aiop_table, aiop->index) == aiop); - ioCancel(cap, aiop); + ASSERT(indexClosureTable(&iomgr->aiop_table, aiop->index) == aiop); + ioCancel(iomgr, aiop); /* We cannot use the normal notifyIOCompletion here. We are in the context * of throwTo, interrupting a thread blocked on IO via an async exception. * We don't put the TSO back on the run queue or change the why_blocked @@ -198,7 +197,7 @@ void syncIOCancelPoll(Capability *cap, StgTSO *tso) } -void asyncIOCancelPoll(Capability *cap, StgAsyncIOOp *aiop) +void asyncIOCancelPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop) { /* We can reliably determine if the aiop is still in progress by checking * if the aiop_table still points to this aiop object. This is reliable @@ -206,20 +205,18 @@ void asyncIOCancelPoll(Capability *cap, StgAsyncIOOp *aiop) * is no longer retained by the application. */ ASSERT(aiop->notify_type != NotifyTSO); - if (indexClosureTable(&cap->iomgr->aiop_table, aiop->index) == aiop) { - ioCancel(cap, aiop); - notifyIOCompletion(cap, aiop); + if (indexClosureTable(&iomgr->aiop_table, aiop->index) == aiop) { + ioCancel(iomgr, aiop); + notifyIOCompletion(iomgr, aiop); } } -static void ioCancel(Capability *cap, StgAsyncIOOp *aiop) +static void ioCancel(CapIOManager *iomgr, StgAsyncIOOp *aiop) { - CapIOManager *iomgr = cap->iomgr; - int ix = aiop->index; int ix_from; int ix_to; - removeCompactClosureTable(cap, &iomgr->aiop_table, ix, + removeCompactClosureTable(iomgr->cap, &iomgr->aiop_table, ix, &ix_from, &ix_to); if (ix_to != ix_from) { StgAsyncIOOp *aiop_to = indexClosureTable(&iomgr->aiop_table, ix_to); @@ -237,7 +234,7 @@ bool anyPendingTimeoutsOrIOPoll(CapIOManager *iomgr) } -static void notifyIOCompletion(Capability *cap, StgAsyncIOOp *aiop) +static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop) { ASSERT(aiop->outcome != IOOpOutcomeInFlight); switch (aiop->notify_type) { @@ -251,7 +248,8 @@ static void notifyIOCompletion(Capability *cap, StgAsyncIOOp *aiop) debugTrace(DEBUG_iomanager, "Raising exception in thread %" FMT_StgThreadID " blocked on an invalid fd", tso->id); - raiseAsync(cap, tso, (StgClosure *)blockedOnBadFD_closure, + raiseAsync(iomgr->cap, tso, + (StgClosure *)blockedOnBadFD_closure, false, NULL); break; } else { @@ -262,7 +260,7 @@ static void notifyIOCompletion(Capability *cap, StgAsyncIOOp *aiop) StgTSO *tso = aiop->notify.tso; tso->why_blocked = NotBlocked; tso->_link = END_TSO_QUEUE; - pushOnRunQueue(cap, tso); + pushOnRunQueue(iomgr->cap, tso); } break; } @@ -277,8 +275,7 @@ static void notifyIOCompletion(Capability *cap, StgAsyncIOOp *aiop) } -static void processIOCompletions(Capability *cap, CapIOManager *iomgr, - int ncompletions) +static void processIOCompletions(CapIOManager *iomgr, int ncompletions) { /* The scheme we use with poll is that we have a dense poll table, and a * corresponding table that maps to the closure table index. The poll @@ -320,7 +317,7 @@ static void processIOCompletions(Capability *cap, CapIOManager *iomgr, * apply the same compacting to the aiop_poll_table. */ int ix_from; int ix_to; - removeCompactClosureTable(cap, &iomgr->aiop_table, i, + removeCompactClosureTable(iomgr->cap, &iomgr->aiop_table, i, &ix_from, &ix_to); if (ix_to != ix_from) { StgAsyncIOOp *aiop_to; @@ -329,7 +326,7 @@ static void processIOCompletions(Capability *cap, CapIOManager *iomgr, aiop_poll_table[ix_to] = aiop_poll_table[ix_from]; } - notifyIOCompletion(cap, aiop); + notifyIOCompletion(iomgr, aiop); n--; } else { /* You'd expect incrementing the poll table index to be @@ -343,13 +340,11 @@ static void processIOCompletions(Capability *cap, CapIOManager *iomgr, } -void pollCompletedTimeoutsOrIOPoll(Capability *cap) +void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) { - CapIOManager *iomgr = cap->iomgr; - if (!isEmptyTimeoutQueue(iomgr->timeout_queue)) { Time now = getProcessElapsedTime(); - processTimeoutCompletions(cap, now); + processTimeoutCompletions(iomgr, now); } if (!isEmptyClosureTable(&iomgr->aiop_table)) { @@ -379,7 +374,7 @@ void pollCompletedTimeoutsOrIOPoll(Capability *cap) } else if (res > 0) { int ncompletions = res; ASSERT(ncompletions <= (int)nfds); - processIOCompletions(cap, iomgr, ncompletions); + processIOCompletions(iomgr, ncompletions); } else if (errno == EINTR) { /* We got interrupted by a signal. This is unlikely since we asked @@ -393,10 +388,8 @@ void pollCompletedTimeoutsOrIOPoll(Capability *cap) } -void awaitCompletedTimeoutsOrIOPoll(Capability *cap) +void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) { - CapIOManager *iomgr = cap->iomgr; - /* Loop until we've woken up some threads. This loop is needed because the * poll() timing isn't accurate, we sometimes sleep for a while but not * long enough to wake up a thread in a threadDelay. Or we may need to @@ -409,14 +402,14 @@ void awaitCompletedTimeoutsOrIOPoll(Capability *cap) !isEmptyClosureTable(&iomgr->aiop_table)); Time now = getProcessElapsedTime(); - processTimeoutCompletions(cap, now); + processTimeoutCompletions(iomgr, now); /* If we didn't wake any threads due to expiring timeouts, then we need * to wait on I/O. Or to put it another way, even if we did wake some * threads, we'll still poll (but not wait) for I/O. This is to ensure * we avoid starving threads blocked on I/O. */ - bool wait = emptyRunQueue(cap); + bool wait = emptyRunQueue(iomgr->cap); /* Decide if we are going to wait if no I/O is ready, either: * poll only, wait indefinitely, or wait until a timeout. @@ -461,7 +454,7 @@ void awaitCompletedTimeoutsOrIOPoll(Capability *cap) } else if (res > 0) { int ncompletions = res; ASSERT(ncompletions <= (int)nfds); - processIOCompletions(cap, iomgr, ncompletions); + processIOCompletions(iomgr, ncompletions); } else if (errno == EINTR) { /* We got interrupted by a signal. In the non-threaded RTS, if the @@ -471,7 +464,7 @@ void awaitCompletedTimeoutsOrIOPoll(Capability *cap) * signal is serviced. */ #if defined(RTS_USER_SIGNALS) - if (startPendingSignalHandlers(cap)) break; + if (startPendingSignalHandlers(iomgr->cap)) break; #endif /* We can also be interrupted by the shutdown signal handler, which @@ -485,7 +478,7 @@ void awaitCompletedTimeoutsOrIOPoll(Capability *cap) reportPollError(res, nfds); } - } while (emptyRunQueue(cap) + } while (emptyRunQueue(iomgr->cap) && (getSchedState() == SCHED_RUNNING)); } @@ -507,12 +500,12 @@ static void reportPollError(int res, nfds_t nfds) /* Helper function to double the size of the aiop_table and aiop_poll_table. */ -static bool enlargeTables(Capability *cap, CapIOManager *iomgr) +static bool enlargeTables(CapIOManager *iomgr) { int oldcapacity = capacityClosureTable(&iomgr->aiop_table); int newcapacity = (oldcapacity == 0) ? 1 : (oldcapacity * 2); - bool ok = enlargeClosureTable(cap, &iomgr->aiop_table, newcapacity); + bool ok = enlargeClosureTable(iomgr->cap, &iomgr->aiop_table, newcapacity); if (RTS_UNLIKELY(!ok)) return false; /* Update the auxiliary aiop_poll_table to match */ ===================================== rts/posix/Poll.h ===================================== @@ -19,19 +19,19 @@ void initCapabilityIOManagerPoll(CapIOManager *iomgr); /* Synchronous I/O and timer operations */ -bool syncIOWaitReadyPoll(Capability *cap, StgTSO *tso, +bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso, IOReadOrWrite rw, HsInt fd); -void syncIOCancelPoll(Capability *cap, StgTSO *tso); +void syncIOCancelPoll(CapIOManager *iomgr, StgTSO *tso); /* Asynchronous operations */ -bool asyncIOWaitReadyPoll(Capability *cap, StgAsyncIOOp *aiop, +bool asyncIOWaitReadyPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop, IOReadOrWrite rw, int fd); -void asyncIOCancelPoll(Capability *cap, StgAsyncIOOp *aiop); +void asyncIOCancelPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop); /* Scheduler operations */ bool anyPendingTimeoutsOrIOPoll(CapIOManager *iomgr); -void pollCompletedTimeoutsOrIOPoll(Capability *cap); -void awaitCompletedTimeoutsOrIOPoll(Capability *cap); +void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr); +void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr); #endif /* IOMGR_ENABLED_POLL */ ===================================== rts/posix/Select.c ===================================== @@ -93,9 +93,8 @@ LowResTime getDelayTarget (HsInt us) * if this is true, then our time has expired. * (idea due to Andy Gill). */ -static bool wakeUpSleepingThreads (Capability *cap, LowResTime now) +static bool wakeUpSleepingThreads (CapIOManager *iomgr, LowResTime now) { - CapIOManager *iomgr = cap->iomgr; StgTSO *tso; bool flag = false; @@ -109,7 +108,7 @@ static bool wakeUpSleepingThreads (Capability *cap, LowResTime now) tso->_link = END_TSO_QUEUE; IF_DEBUG(scheduler, debugBelch("Waking up sleeping thread %" FMT_StgThreadID "\n", tso->id)); - pushOnRunQueue(cap,tso); + pushOnRunQueue(iomgr->cap,tso); flag = true; } return flag; @@ -217,9 +216,8 @@ static enum FdState fdPollWriteState (int fd) * */ void -awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait) +awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) { - CapIOManager *iomgr = cap->iomgr; StgTSO *tso, *prev, *next; fd_set rfd,wfd; int numFound; @@ -244,7 +242,7 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait) do { now = getLowResTimeOfDay(); - if (wakeUpSleepingThreads(cap, now)) { + if (wakeUpSleepingThreads(iomgr, now)) { return; } @@ -355,7 +353,7 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait) */ #if defined(RTS_USER_SIGNALS) if (RtsFlags.MiscFlags.install_signal_handlers && signals_pending()) { - startSignalHandlers(cap); + startSignalHandlers(iomgr->cap); return; /* still hold the lock */ } #endif @@ -368,12 +366,12 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait) /* check for threads that need waking up */ - wakeUpSleepingThreads(cap, getLowResTimeOfDay()); + wakeUpSleepingThreads(iomgr, getLowResTimeOfDay()); /* If new runnable threads have arrived, stop waiting for * I/O and run them. */ - if (!emptyRunQueue(cap)) { + if (!emptyRunQueue(iomgr->cap)) { return; /* still hold the lock */ } } @@ -429,7 +427,7 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait) IF_DEBUG(scheduler, debugBelch("Killing blocked thread %" FMT_StgThreadID " on bad fd=%i\n", tso->id, fd)); - raiseAsync(cap, tso, + raiseAsync(iomgr->cap, tso, (StgClosure *)blockedOnBadFD_closure, false, NULL); break; case RTS_FD_IS_READY: @@ -438,13 +436,13 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait) tso->id)); tso->why_blocked = NotBlocked; tso->_link = END_TSO_QUEUE; - pushOnRunQueue(cap,tso); + pushOnRunQueue(iomgr->cap,tso); break; case RTS_FD_IS_BLOCKING: if (prev == NULL) iomgr->blocked_queue_hd = tso; else - setTSOLink(cap, prev, tso); + setTSOLink(iomgr->cap, prev, tso); prev = tso; break; } @@ -460,7 +458,7 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait) } } while (wait && getSchedState() == SCHED_RUNNING - && emptyRunQueue(cap)); + && emptyRunQueue(iomgr->cap)); } #endif /* IOMGR_ENABLED_SELECT */ ===================================== rts/posix/Select.h ===================================== @@ -15,7 +15,7 @@ typedef StgWord LowResTime; LowResTime getDelayTarget (HsInt us); -void awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait); +void awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait); #include "EndPrivate.h" ===================================== rts/posix/Timeout.c ===================================== @@ -26,7 +26,7 @@ */ #if defined(IOMGR_ENABLED_POLL) -bool syncDelayTimeout(Capability *cap, StgTSO *tso, HsInt us_delay) +bool syncDelayTimeout(CapIOManager *iomgr, StgTSO *tso, HsInt us_delay) { Time now = getProcessElapsedTime(); Time target; @@ -42,16 +42,16 @@ bool syncDelayTimeout(Capability *cap, StgTSO *tso, HsInt us_delay) /* fill in a new timeout queue entry */ StgTimeout *timeout; - timeout = (StgTimeout *)allocateMightFail(cap, sizeofW(StgTimeout)); + timeout = (StgTimeout *)allocateMightFail(iomgr->cap, sizeofW(StgTimeout)); if (RTS_UNLIKELY(timeout == NULL)) { return false; } union NotifyCompletion notify = { .tso = tso }; - initElemTimeoutQueue(timeout, notify, NotifyTSO, cap->r.rCCCS); + initElemTimeoutQueue(timeout, notify, NotifyTSO, iomgr->cap->r.rCCCS); ASSERT(tso->why_blocked == NotBlocked); tso->why_blocked = BlockedOnDelay; tso->block_info.timeout = timeout; - insertTimeoutQueue(&cap->iomgr->timeout_queue, timeout, target); + insertTimeoutQueue(&iomgr->timeout_queue, timeout, target); debugTrace(DEBUG_iomanager, "timer for delay of %lld usec installed at time %lld ns", @@ -60,18 +60,18 @@ bool syncDelayTimeout(Capability *cap, StgTSO *tso, HsInt us_delay) } -void syncDelayCancelTimeout(Capability *cap, StgTSO *tso) +void syncDelayCancelTimeout(CapIOManager *iomgr, StgTSO *tso) { ASSERT(tso->why_blocked == BlockedOnDelay); StgTimeoutQueue *timeout = tso->block_info.timeout; - deleteTimeoutQueue(&cap->iomgr->timeout_queue, timeout); + deleteTimeoutQueue(&iomgr->timeout_queue, timeout); tso->block_info.closure = (StgClosure *)END_TSO_QUEUE; /* the timeout is no longer accessible from anywhere (except here) */ IF_NONMOVING_WRITE_BARRIER_ENABLED { - updateRemembSetPushClosure(cap, (StgClosure *)timeout); + updateRemembSetPushClosure(iomgr->cap, (StgClosure *)timeout); } /* We don't put the TSO back on the run queue or change the why_blocked @@ -79,7 +79,7 @@ void syncDelayCancelTimeout(Capability *cap, StgTSO *tso) */ } -static void notifyTimeoutCompletion(Capability *cap, StgTimeout *timeout); +static void notifyTimeoutCompletion(CapIOManager *iomgr, StgTimeout *timeout); /* We use the 64bit Time type from rts/Time.h so our max time (in nanosecond * precision) is over 290 years from the epoch of the monotonic clock. @@ -90,10 +90,8 @@ static void notifyTimeoutCompletion(Capability *cap, StgTimeout *timeout); * With 64bit Time we do not need to worry about clock wraparound and can just * use the simple formula. */ -void processTimeoutCompletions(Capability *cap, Time now) +void processTimeoutCompletions(CapIOManager *iomgr, Time now) { - CapIOManager *iomgr = cap->iomgr; - /* Pop entries from the front of the sleeping queue that are past their * wake time, and unblock the corresponding MVars. */ @@ -105,17 +103,17 @@ void processTimeoutCompletions(Capability *cap, Time now) debugTrace(DEBUG_iomanager,"timer expired at %lld ns", waketime); StgTimeout *timeout; deleteMinTimeoutQueue(&iomgr->timeout_queue, &timeout); - notifyTimeoutCompletion(cap, timeout); + notifyTimeoutCompletion(iomgr, timeout); /* the timeout is no longer accessible from anywhere (except here) */ IF_NONMOVING_WRITE_BARRIER_ENABLED { - updateRemembSetPushClosure(cap, (StgClosure *)timeout); + updateRemembSetPushClosure(iomgr->cap, (StgClosure *)timeout); } } } -static void notifyTimeoutCompletion(Capability *cap, StgTimeout *timeout) +static void notifyTimeoutCompletion(CapIOManager *iomgr, StgTimeout *timeout) { switch (timeout->notify_type) { case NotifyTSO: @@ -123,11 +121,11 @@ static void notifyTimeoutCompletion(Capability *cap, StgTimeout *timeout) StgTSO *tso = timeout->notify.tso; tso->why_blocked = NotBlocked; tso->_link = END_TSO_QUEUE; - pushOnRunQueue(cap, tso); + pushOnRunQueue(iomgr->cap, tso); break; } case NotifyMVar: - performTryPutMVar(cap, timeout->notify.mvar, Unit_closure); + performTryPutMVar(iomgr->cap, timeout->notify.mvar, Unit_closure); break; case NotifyTVar: ===================================== rts/posix/Timeout.h ===================================== @@ -12,9 +12,9 @@ #include "BeginPrivate.h" -bool syncDelayTimeout(Capability *cap, StgTSO *tso, HsInt us_delay); +bool syncDelayTimeout(CapIOManager *iomgr, StgTSO *tso, HsInt us_delay); -void syncDelayCancelTimeout(Capability *cap, StgTSO *tso); +void syncDelayCancelTimeout(CapIOManager *iomgr, StgTSO *tso); /* Process the completion of any timeouts that have expired: this means * notifying whatever is waiting on the timeout, a thread, an MVar or TVar. @@ -24,7 +24,7 @@ void syncDelayCancelTimeout(Capability *cap, StgTSO *tso); * No result is returned: callers can check if there are now any runnable * threads by consulting the scheduler's run queue. */ -void processTimeoutCompletions(Capability *cap, Time now); +void processTimeoutCompletions(CapIOManager *iomgr, Time now); /* Utility to compute the timeout wait time (in milliseconds) between now and * the next timer expiry (if any), or no waiting (if !wait). View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f340f464242fda12596188be8ed2f3a7... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f340f464242fda12596188be8ed2f3a7... You're receiving this email because of your account on gitlab.haskell.org.