Asynchronous exception wormholes kill modularity
Bas van Dijk
v.dijk.bas at gmail.com
Thu Mar 25 13:16:07 EDT 2010
On Thu, Mar 25, 2010 at 5:36 PM, Simon Marlow <marlowsd at gmail.com> wrote:
> Nice, I hadn't noticed that you can now code this up in the library since we
> added 'blocked'. Unfortunately this isn't cheap: 'blocked' is currently an
> out-of-line call to the RTS, so if we want to start using it for important
> things like finally and bracket, then we should put some effort into
> optimising it.
> I'd also be amenable to having block/unblock count nesting levels instead, I
> don't think it would be too hard to implement and it wouldn't require any
> changes at the library level.
Yes counting the nesting level like Twan proposed will definitely
solve the modularity problem.
I do think we need to optimize the block and unblock operations in
such a way that they don't need to use IORefs to save the counting
level. The version Twan posted requires 2 reads and 2 writes for a
block and unblock. While I haven't profiled it I think it's not very
> Incedentally, I've been using the term "mask" rather than "block" in this
> context, as "block" is far too overloaded. It would be nice to change the
> terminology in the library too, leaving the old functions around for
> backwards compatibility of course.
Indeed "block" is to overloaded. I've been using block and unblock a
lot in concurrent-extra and because this package deals with threads
that can "block" it sometimes is confusing whether a block refers to
thread blocking or asynchronous exceptions blocking.
So I'm all for deprecating 'block' in favor of 'mask'. However what do
we call 'unblock'? 'unmask' maybe? However when we have:
mask $ mask $ unmask x
and these operations have the counting nesting levels semantics,
asynchronous exception will not be unmasked in 'x'. However I don't
currently know of a nicer alternative.
BTW I also use the 'blockedApply' function in the
Control.Concurrent.Thread.forkIO function of concurrent-extra:
forkIO ∷ IO α → IO (ThreadId α)
forkIO = fork Conc.forkIO
fork ∷ (IO () → IO Conc.ThreadId) → IO α → IO (ThreadId α)
fork doFork act = do
stop ← newEmptyMVar
fmap (ThreadId stop) $ blockedApply act $ \a → doFork $ try a >>= putMVar stop
Here I use it to ensure that the forked IO computation keeps the same
blocked status as the parent thread. So that this forkIO has the same
semantics as the standard forkIO.
More information about the Libraries