I've got a program which uses several threads. As I understand it, when thread 0 exits, the entire program exits, regardless of any other threads which might still be running.
The thing is, these other threads may have files open. Naturally, this is wrapped in exception-handling code which cleanly closes the files in case of a problem. That also means that if I use killThread
(which is implemented via throwTo
), the file should also be closed before the thread exits.
My question is, if I just let thread 0 exit, without attempting to stop the other threads, will all the various file handles be closed nicely? Does any buffered output get flushed?
In short, can I just exit, or do I need to manually kill threads first?
Our Haskell process uses exitWith to exit the process with exit code 42 . Then echo $? prints the last exit code. All relatively straightforward (if you're familiar with the shell).
This represents the fact that Haskell programs are not guaranteed to terminate and can have exceptions. I.e., all Agda programs will terminate, and a Bool in Agda corresponds to exactly {True, False} .
You can use Control.Concurrent.MVar
to achieve this. An MVar
is essentially a flag which is either ''empty'' or "full". A thread can try to read an MVar
and if it is empty it blocks the thread. Wherever you have a thread which performs file IO, create an MVar
for it, and pass it that MVar
as an argument. Put all the MVar
s you create into a list:
main = do let mvars = sequence (replicate num_of_child_threads newEmptyMVar) returnVals <- sequence (zipWith (\m f -> f m) mvars (list_of_child_threads :: [MVar -> IO a]))
Once a child thread has finished all file operations that you are worried about, write to the MVar
. Instead of writing killThread
you can do
mapM_ takeMVar mvars >> killThread
and where-ever your thread would exit otherwise, just take all the MVar
s.
See the documentation on GHC concurrency for more details.
From my testing, I have discovered a few things:
exitFailure
and friends only work in thread 0. (The documentation actually says so, if you go to the trouble of reading it. These functions just throw exceptions, which are silently ignored in other threads.)
If an exception kills your thread, or your whole program, any open handles are not flushed. This is excruciatingly annoying when you're desperately trying to figure out exactly where your program crashed!
So it appears it if you want your stuff flushed before the program exits, then you have to implement this. Just letting thread 0 die doesn't flush stuff, doesn't throw any exception, just silently terminates all threads without running exception handlers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With