
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