[Haskell-cafe] child thread control design patterns (was: GHC.Conc.threadStatus - documentation of ThreadDied :: ThreadStatus)

Olaf Klinke olf at aatal-apotheke.de
Wed Mar 9 19:45:26 UTC 2022


> Another good option for eternally long running things is to use race. I actually think race and concurrently are the much better go-to functions from the async library. I will often write code like:
> 
> race_ daemonThread mainThread >>= \case
>   Left _ -> fail "Daemon terminated"
>   Right a -> return a
> 
> This forks a thread for a daemon, a thread for the main thread, and then waits for either of them to finish. Assuming the daemon should never terminate, if it terminates the main thread will be terminated and the calling thread (e.g., main) will crash. If the other thread terminates early, that's fine, and we assume that's just the program gracefully terminating. In this case, the daemon will be cancelled.
> 
> Ollie

That is indeed a neat pattern. What are its advantages/disadvantages? 
The main thread in my case is the (Yesod) webserver. Perhaps Yesod does
exactly what you propose, internally. Currently I use a bracket to kill
all child threads when the webserver shuts down, like:

main = bracket forkWorkers (traverse_ cancel) (const (warp port site))

Your idiom would be something like this?

main = foldr race_ (warp port site) workers

I also do want to include the possibility of a child (daemon) thread to
crash without crashing the webserver (hence my original question),
which your race solution does not cater for. In fact I will have a mix
of perpetually running actions and actions that are fired on command of
the user and then exit gracefully. 

Therefore I must save the original IO action somewhere, to be re-
executed. And since operations on the child thread are initiated by the
Yesod event handler, I can not pass them to the continuation of
withAsync. 

Olaf



More information about the Haskell-Cafe mailing list