[GHC] #9285: IO manager startup procedure somewhat odd

GHC ghc-devs at haskell.org
Tue Jul 8 17:19:01 UTC 2014


#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


More information about the ghc-tickets mailing list