hspec / silently

Prevent or capture output to stdout or other handles in Haskell
Other
21 stars 5 forks source link

is capture_ supposed to work with forkIO? #27

Closed pakanek closed 4 months ago

pakanek commented 4 months ago

Hello, is this expected output?

module Main (main) where

import System.IO.Silently
import Control.Concurrent

f :: IO ()
f = putStrLn "output"

main :: IO ()
main = do
  out1 <- capture_ $ forkIO f
  out2 <- capture_ f
  threadDelay (3 * 1000000)
  putStrLn $ "forkIO: '" <> out1 <> "'"
  putStrLn $ "direct: '" <> out2 <> "'"

$ stack run
output
forkIO: ''
direct: 'output
'
sol commented 4 months ago
  1. When you have a shared resource such as a file descriptor[^1], and you access that shared resource concurrently without synchronization, then you can run into race conditions. That's true in general, independent of any specific libraries you use, independent of the type of resource, and independent of the used programming language.
  2. Your specific example program behaves as expected. You are not observing any race condition here. Did you mean forkIO $ capture_ f instead of capture_ $ forkIO f? Note that the former captures the output of f while it's running in a separate thread; the later only captures stdout while the thread is started. Also note that the former is prone to race conditions as per (1).

[^1]: Note that stdout is a file descriptor and that capture_ captures stdout.

pakanek commented 4 months ago

That makes sense, thanks a lot. Should be something like

v <- newEmptyMVar
forkIO $ capture_ f >>= putMVar v
out1 <- readMVar v