Skip to content

GHC uses 300% CPU when calling into blocking C call

Hello,

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.

Trac metadata
Trac field Value
Version 8.4.3
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Runtime System
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information