GHC uses 300% CPU when calling into blocking C call
I'm trying to write a program that modifies /etc/passwd safely, and so I wrote a function that looks like this:
lockPwd f = bracket main recover (\_ -> f) where mode = unionFileModes ownerReadMode ownerWriteMode main = do fd <- openFd "/etc/.pwd.lock" WriteOnly (Just mode) defaultFileFlags putStrLn "waiting to set lock" waitToSetLock fd (WriteLock, AbsoluteSeek, 0, 0) putStrLn "got lock" return fd recover = flip setLock (Unlock, AbsoluteSeek, 0, 0)
When I run it, my fans go wild, and CPU usage hits 300%.
/u/gelisam did some more in depth investigation here: https://www.reddit.com/r/haskell/comments/94wbfc/systemunixiowaittosetlock_call_results_in_300_cpu/e3p7sks/
Googling confirms that the parallel gc is using spin locks. So I think what is happening is that the waitToSetLock call makes the current thread unavailable for garbage collection (I don't know if that means the thread is "descheduled" as in the linked issue), which then causes the other threads to spin-lock while waiting for that thread at the next parallel GC. The problem still occurs with the latest release, GHC 8.4.3, and I could not find an existing issue describing the problem, so please file a ticket.
Maybe an argument could be made that this waitToSetLock call should be converted to be interruptible, but it also seems like I should be able to make my haskell program block patiently if I want it to.