IO manager startup procedure somewhat odd
Currently the RTS startup procedure for the threaded runtime with a single capability looks something like this:
1. Capability gets initialized
hs_main
real_main
hs_init_ghc
initScheduler
initCapabilities
2. IO Manager is started
hs_main
real_main
hs_init_ghc
ioManagerStart
3. ensureIOManagerIsRunning
The IO manager of course is mostly written in Haskell, so ioManagerStart
does
cap = rts_lock();
rts_evalIO(&cap,&base_GHCziConcziIO_ensureIOManagerIsRunning_closure,NULL);
rts_unlock(cap);
4. Bound task is created and we start running Haskell code
Since this is the first call to rts_lock
, it creates a bound task to correspond to the current OS thread.
5. Haskell code does a safe FFI call: create worker task 1
The first safe FFI call that the IO manager makes (ignoring labelThread
) is to c_setIOManagerControlFd
. At this point, the bound task gets suspended and we create a worker task.
6. Worker task 1 starts executing GHC.Event.Manager.loop
The worker tasks gets suspended due to an explicit call to yield
in GHC.Event.Manager.step
, at which point the bound task continues execution. It continues and finishes (ensureIOManagerRunning
completes). The worker task takes over.
7. Worker task 1 does a safe FFI call to I.poll
, create new worker task 2
(This safe call is from GHC.Event.Manager.step
)
8. Worker task 2 executes GHC.Event.TimerManager.loop
, and does safe FFI call to I.poll
; create worker task 3
This worker task will remain spare for now.
9. Start executing the actual main function
The IO manager calls rts_unlock
, which verifies if there are any spare workers available; it finds that there are and doesn't start a new one.
Then when we start main
itself of course we go through this rts_lock, rts_evalIO, rts_unlock
sequence again. This is somewhat confusing, and makes tracing the startup of the code more difficult to interpret. It seems that this could be simpler: only a single rts_lock
should be necessary, in which we first call ensureIOManagerIsRunning
and then main
, followed by a single rts_unlock
. A similar simplification should be possible in forkProcess
.
Trac metadata
Trac field | Value |
---|---|
Version | 7.8.2 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | low |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |