
#9285: IO manager startup procedure somewhat odd ------------------------------------+------------------------------------- Reporter: edsko | Owner: Type: bug | Status: new Priority: low | Milestone: Component: Compiler | Version: 7.8.2 Keywords: | Operating System: Unknown/Multiple Architecture: Unknown/Multiple | Type of failure: None/Unknown Difficulty: Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | ------------------------------------+------------------------------------- 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`. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/9285 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler