Opened 12 months ago

Last modified 12 months ago

#9030 new feature request

An async exception handler that blocks throwTo until handler finishes running

Reported by: jberryman Owned by:
Priority: normal Milestone:
Component: Compiler Version: 7.8.2
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case:
Blocked By: Blocking:
Related Tickets: Differential Revisions:

Description

I'd like to be able to handle an async exception in thread A such that if another thread B calls throwTo, that call blocks until my handler in A has a chance to finish. Currently throwTo only blocks until the exception is "received" (which I don't understand precisely).

My application is a concurrent FIFO queue library, in which a blocked reader must perform a cleanup action on async exceptions, else the next write will be lost:

testBlockedRecovery = do
    c <- newChan
    started <- newEmptyMVar
    rid <- forkIO (putMVar started () >> readChan c)
    takeMVar started >> threadDelay 1000
    throwTo rid ThreadKilled
    -- we race the exception-handler in `readChan` here...
    writeChan c ()
    -- If this write won, then the write was lost and we block indefinitely below:
    readChan c
    putStrLn "OK"

If in my library I could catch the exception in such a way that throwTo blocked until I could handle it, then the test above would pass and the behavior of my Chan would match the standard library.

This might seem like a lame example, but I think this functionality would be very generally useful. It would let you recover to a state using an exception handler such that you can reason in terms of linearizability about the pre- and post- exception states; I assume that's sort of the thinking behind making throwTo synchronous in the first place.

Thanks and please let me know if there's actually a way to do what I'm asking currently, or if any of that wasn't clear.

Change History (5)

comment:1 Changed 12 months ago by ezyang

Why can't you pass an MVar in the thrown exception (which the throwing thread blocks on), which the handler writes to when it is done?

comment:2 Changed 12 months ago by jberryman

Sure, but the throwTo is something the users of my library are doing. Or maybe they're using the async lib which is doing the throwTo ThreadKilled.

In this particular case I think the user will have a mental model of what should happen when a blocked reader thread is killed (which matches the behavior of standard Chan), and I'd rather just be able to implement that rather than document weird corner-cases or force users to use a particular function or Exception type (although thanks for that idea; I'll probably warm up to that as a workaround).

Maybe users never in practice think or care much about what happens when they do a throwTo and I just have to worry about not leaving my data structure broken; I'm not really sure.

comment:3 Changed 12 months ago by ezyang

OK, I looked at your example more carefully, I think this alternate design is more appropriate: you should just mask exceptions before reading from the channel. (Now readChan never services an exception once it has successfully read from the MVar, it will finish processing before admitting the exception.) Mask is exactly about setting up the linearizability properly. This won't help if a blocking call inside the mask requires an exception handler to run, but it feels like this should be rare (i.e. you shouldn't hold onto resources if you're going to block).

comment:4 Changed 12 months ago by jberryman

I don't really follow you. I'm certainly already masking exceptions in my readChan to avoid leaving the structure in a broken state, but an exception can (and must be permitted to) be raised when readChan is blocking on an empty queue (blocking on an MVar internally).

comment:5 Changed 12 months ago by ezyang

Oh sorry, my mistake, readChan is using modifyMVarMasked, not modifyMVar.

(Tongue in cheek (but only slightly): You should use STM, not MVars.)

Last edited 12 months ago by ezyang (previous) (diff)
Note: See TracTickets for help on using tickets.