Skip to content

The documentation for performGC should mention its behaviour during unsafe FFI calls

Summary

System.Mem.performGC (and friends) don't currently mention their behaviour when the GC is unable to run because of an unsafe FFI call. At the moment between their documentation saying that a GC happens immediately and the unsafe FFI documentation saying that GHC guarantees that no GC takes place it's a little ambiguous.

This small program (using inline-c for convenience) demonstrates that performGC blocks during unsafe FFI calls. Switch Unsafe.exp for C.exp to observe no such blocking behaviour.

import           Control.Concurrent
import           GHC.Clock
import qualified Language.C.Inline             as C
import qualified Language.C.Inline.Unsafe      as Unsafe
import           System.Mem

C.include "<unistd.h>"

main :: IO ()
main = do
  let report s = putStrLn . (<> s) . show =<< getMonotonicTime
  finished <- newEmptyMVar
  report " forking sleeping thread"
  _ <- forkFinally [Unsafe.exp| void { sleep(3); } |]
                   (\_ -> putMVar finished ())
  report " starting GC"
  performGC
  report " finished GC"
  report " waiting for sleeping thread"
  _ <- takeMVar finished
  report " finished"

Proposed improvements or changes

In the documentation for performGC and friends add

... unless an @unsafe@ FFI call is in progress, in which case the call to performGC will block until all @unsafe@ calls have returned

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information