In a standalone GHC program, only the main thread is required to terminate in order for the process to terminate. Thus all other forked threads will simply terminate at the same time as the main thread (the terminology for this kind of behaviour is ``daemonic threads'').
If you want the program to wait for child threads to finish before exiting, you need to program this yourself. A simple mechanism is to have each child thread write to an MVar when it completes, and have the main thread wait on all the MVars before exiting:
myForkIO :: IO () -> IO (MVar ()) myForkIO io = do mvar <- newEmptyMVar forkIO (io `finally` putMVar mvar ()) return mvar
Note that we use finally from the Exception module to make sure that the MVar is written to even if the thread dies or is killed for some reason.
A better method is to keep a global list of all child threads which we should wait for at the end of the program:
children :: MVar [MVar ()] children = unsafePerformIO (newMVar ) waitForChildren :: IO () waitForChildren = do (mvar:mvars) <- takeMVar children putMVar children mvars takeMVar mvar waitForChildren forkChild :: IO () -> IO () forkChild io = do mvar <- newEmptyMVar forkIO (p `finally` putMVar mvar ()) childs <- takeMVar children putMVar children (mvar:childs) later = flip finally main = later waitForChildren $ ...