-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MVar deadlock exceptions cause waitCatch to throw an exception #14
Comments
I reduced this to a bug reproducible with just https://ghc.haskell.org/trac/ghc/ticket/9401 Feel free to close this issue if you feel it makes more sense to just follow this in GHC trac. |
See the comment on https://ghc.haskell.org/trac/ghc/ticket/9401, this isn't really a bug, just a consequence of the way that deadlock detection works. If you want a particular thread to be immune to deadlock detection, you can use this trick:
|
Pinging @simonmar. Thank you for your response in the tickets. However, the StablePtr technique simply caused this test suite to hang indefinitely. Instead, I'm trying to catch the BlockedIndefinitelyOnSTM exception sent by the RTS, which seems to work reliably. Pinging @sol. This change needs to be included in hspec as well. Without a patch, for example, the enclosed-exceptions test suite still fails, since hspec ends up letting the test die due to the BlockedIndefinitelyOnSTM exception being sent to it as well. I can send a pull request after this is reviewed/accepted into enclosed-exceptions.
Thanks for the clarification. While the StablePtr trick didn't work for me, catching the BlockedIndefinitelyOnSTM exception does seem to have solved the problem. I've created pull request jcristovao/enclosed-exceptions#5. |
Making a StablePtr will prevent the main thread from receiving deadlock exceptions, so if you need that exception to recover from a main thread deadlock then it will just hang instead. The StablePtr trick works for me on the example in https://ghc.haskell.org/trac/ghc/ticket/9401. |
This uses the same technique I described in my enclosed-exceptions pull request. I think it would be better to fix this in async itself, so that other packages trying to use async for reliable exception handling get the semantics they're looking for.
`waitCatch` is used to have the fix for <simonmar#14> also in `cancel`
Here is the fix implemented in wait :: Async a -> IO a
wait = tryAgain . atomically . waitSTM
where
-- See: https://github.com/simonmar/async/issues/14
tryAgain f = f `catch` \BlockedIndefinitelyOnSTM -> f But my colleague @tstat pointed out that
For that reason, do we want to use wait :: Async a -> IO a
wait = tryAgain . atomically . waitSTM
where
-- See: https://github.com/simonmar/async/issues/14
tryAgain f = try f >>= \case
Left BlockedIndefinitelyOnSTM -> f
Right x -> pure x |
I don't think it actually matters, because the only thing that |
@simonmar Thanks for the clarification. I'm still a little bit puzzled - could it not be the case that on the second invocation of atomically (waitSTM x) (with async exceptions masked due to A related question - if the transaction gets rolled back and retried due to an inconsistent view of memory, does that also count as an "interruptible" moment for the runtime to poll for an async exception? Overall I agree there is not a very significant distinction here, but I am curious about the nitty-gritty timings :) Thanks! |
That's true, but you never have any guarantees about when an asynchronous exception will be delivered anyway. I mean, strictly speaking you're probably right. I'll accept a pull request to fix it.
I don't believe so. |
I originally discovered this problem when working around an issue with hlint (so pinging @ndmitchell, who may be interested). Consider the following program:
There are three logical blocks in this program, each doing essentially the same thing: creating an MVar deadlock exception and trying to catch it. The first approach does it in the main program thread, and the second in a child thread. Both of them work as expected: the exception is caught and printed, and the program continues executing.
However, the third block behaves differently. The MVar exception seems to not be got by the call to
try
inasyncUsing
, and therefore theTMVar
is never filled. This means that a later call towaitCatch
throws an exception, and therefore the program exits prematurely. I've seen the same behavior withasyncBound
as well, so I do not believe this is related torawForkIO
.Here's an example output from my system:
I'm on Ubuntu 12.04 64-bit, using GHC 7.8.3, stm-2.4.3 and async-2.0.1.5.
The text was updated successfully, but these errors were encountered: