Why do remaining HECs busy-wait during unsafe-FFI calls?

Herbert Valerio Riedel hvr at gnu.org
Mon May 6 13:29:29 CEST 2013


Recently, I stumbled over E.Z.Yang's "Safety first: FFI and
threading"[1] post and then while experimenting with unsafe-imported FFI
functions I've noticed a somewhat surprising behaviour:

Consider the following contrived program:

--8<---------------cut here---------------start------------->8---
import Foreign.C
import Control.Concurrent
import Control.Monad
import Data.Time.Clock.POSIX (getPOSIXTime)

foreign import ccall unsafe "unistd.h sleep" c_sleep_unsafe :: CUInt -> IO CUInt

main :: IO ()
main = do
    putStrLnTime "main started"
    _ <- forkIO (sleepLoop 10 >> putStrLnTime "sleepLoop finished")
    putStrLnTime "after forkIO"
    threadDelay (11*1000*1000) -- 11 seconds
    putStrLnTime "end of main"
    putStrLnTime s = do
	t <- getPOSIXTime
        putStrLn $ init (show t) ++ "\t" ++ s

    sleepLoop n = do
        n' <- c_sleep_unsafe n
        unless (n' == 0) $ do
            putStrLnTime "c_sleep_unsafe got interrupted"
            sleepLoop n'

--8<---------------cut here---------------end--------------->8---

When compiled with GHC-7.6.3/linux/amd64 with "-O2 -threaded" and
executed with "+RTS -N4", the following output is emitted:

 1367838802.137419	main started
 1367838812.137727	after forkIO
 1367838812.137783	sleepLoop finished
 1367838823.148733	end of main

which shows that the forkIO of the unsafe ccall effectively blocks the
main thread;

Moreover, when looking at the process table, I saw that 3 threads were
occupying 100% CPU time each for 10 seconds until the 'after forkIO' was

So what is happening here exactly, why do the 3 remaining HECs busy-wait
during that FFI call instead of continuing the execution of the main

Do *all* foreign unsafe ccalls (even short ones) cause N-1 HECs to spend
time in some kind of busy looping?

 [1]: http://blog.ezyang.com/2010/07/safety-first-ffi-and-threading/


More information about the Glasgow-haskell-users mailing list