
* Bertram Felgenhauer
Roman Cheplyaka wrote:
Can withAsync guarantee that its child will be terminated if the thread executing withAsync gets an exception?
To remind, here's an implementation of withAsync:
withAsyncUsing :: (IO () -> IO ThreadId) -> IO a -> (Async a -> IO b) -> IO b -- The bracket version works, but is slow. We can do better by -- hand-coding it: withAsyncUsing doFork = \action inner -> do var <- newEmptyTMVarIO mask $ \restore -> do t <- doFork $ try (restore action) >>= atomically . putTMVar var let a = Async t (readTMVar var) r <- restore (inner a) `catchAll` \e -> do cancel a; throwIO e cancel a return r
I am interested in the case when an exception arrives which transfers control to 'cancel', and then another exception arrives to the same thread. Even though 'catchAll' (which is a type-restricted synonym for catch) masks the exception handler, 'throwTo' inside 'cancel' is interruptible (as stated by the documentation).
Will this scenario lead to a thread leakage?
Yes. I guess that 'cancel' should use 'uninterruptibleMask_', but it's a hard call to make (if an async action becomes unresponsive, do we want to risk not being able to deliver any exceptions to the controlling thread just because it wants to terminate the async action?)
Fair point. What if we fork a new thread, shield it from exceptions using uninterruptibleMask_, and let it to kill every other thread (however long that may take)? It will change the semantics a bit (the cleanup will be asynchronous), but I'm not sure if it can be a problem. A hybrid (but complicated) approach should also be possible. Roman