[Haskell-cafe] ANN: asynchronous-exceptions

Roman Cheplyaka roma at ro-che.info
Thu Feb 6 06:17:07 UTC 2014


> Am I getting this right?

Not quite.

The enclosed computation may get asynchronous exceptions whatever
definition of 'asynchronous' you use — the exact one (by throw method)
or the approximate one (by type).

Just a couple of examples:

* The thread may catch an async exception from the RTS (such as
  BlockedIndefinitelyOnMVar or StackOverflow)
* The thread may spawn other threads, passing its pid to them, and one
  of its children may eventually kill it

So I'd say this method allows to catch any exceptions "related" to
the computation, synchronous or asynchronous. It is this "relatedness"
that is enforced by running it in a separate thread.

Roman

* João Cristóvão <jmacristovao at gmail.com> [2014-02-06 00:16:06+0000]
> Hi again,
> 
> > since the module doesn't make any attempt to differentiate between sync and async exceptions.
> 
> Well... If I am understanding this correctly, and I might not be:
> Michael code efectively distinguishes exceptions thrown asynchronously
> with throwTo from synchronous exceptions generated inside the
> catchAny. Since the code run by catchAny is run in a separate
> 'anonymous' thread created by withAsync, there's no way you can throw
> it an asynchronous exception (ThreadKilled) with throwTo - you don't
> know its thread id.
> 
> Any code using throwTo will just know the calling thread, and thus
> you've got a way to distinguish between received asynchronous
> exceptions (the timeout example) and synchronous exceptions generated
> inside the function your passing to catchAny.
> 
> Thus, in one hand I do not think "CatchAny" makes justice to the fact
> that async exceptions received by the thread using catchAny are _not_
> caught by catchAny.
> 
> In the other hand, I was just thinking: what if the computation runing
> in catchAny in the anonymous thread generates a Ctrl-C, if, for
> example, it interacts with stdin.
> However, Control.Exception states that:
> "UserInterrupt: This exception is raised by default in the __main__
> thread of the program when the user requests to terminate the program
> via the usual mechanism(s) (e.g. Control-C in the console). "
> (emphasis mine)
> 
> Thus, the problem does not pose it self. The remaining two
> asynchronous exceptions (StackOverflow and HeapOverflow) are again a
> result of the inner function execution, and could, from a certain
> point of view, be considered synchronous.
> 
> So, for all intents and purposes, this catchAny and such _do_ allow to
> distinguish between asynchronous exceptions (sent with throwTo) or
> synchronous exceptions (generated as a result of the execution of the
> computation passed to catchAny). Asynchronous exceptions do not occur
> inside the CatchAny associated computation, except perhaps the
> ThreadKilled signal forward by its calling thread/parent thread when
> it self is killed - an in that particular case, it doesn't really
> matter. That asynchronous exception would be caught by catchAny, but
> the result returned by the exception handler would not be used - the
> calling thread is also dying.
> 
> Am I getting this right?
> João
> 
> 2014-02-05 Roman Cheplyaka <roma at ro-che.info>:
> > Well, since you happened to ask me (although I am as good a bikeshedder
> > as the next person), I think that you don't need to mention Async
> > anywhere in the module name, since the module doesn't make any attempt
> > to differentiate between sync and async exceptions.
> >
> > And the fact that the module uses the 'async' library is just an
> > implementation detail.
> >
> > I'd go with something like Control.Exception.CatchAny.
> >
> > Roman
> >
> > * João Cristóvão <jmacristovao at gmail.com> [2014-02-05 22:13:19+0000]
> >> Roman,
> >>
> >> By mere chance today I was, about the same time you published your
> >> library, working on the suggestion made by Michael in the end of his
> >> original blog post: splitting the async exceptions part from
> >> classy-prelude (he is ok with this).
> >>
> >> https://github.com/jcristovao/async-exception
> >>
> >> I was not yet sure about the namespace, I had opted for:
> >> Control.Concurrent.Async.Exception
> >>
> >> But yours makes more sense,
> >> Control.Async.Exception
> >>
> >> I agree that the two solutions address different problems, and as you
> >> say, for controlled situations where performance is critical yours
> >> indeed adds less overhead. But for more general solutions, Michael's
> >> solution - split from Classy prelude, seems to be the way to go, and
> >> thus my 'split' makes sense if you don't need the remaining classy
> >> prelude.
> >>
> >> As such, I was considering the namespace:
> >> Control.Async.Exception.All
> >>
> >> To differentiate from yours, signaling that it handles _all_ exceptions.
> >> What do you think?
> >>
> >> Anyhow, I also think Joachim suggestion (of at least implementing the
> >> new exception classes in base-compat) makes sense, so I volunteer to
> >> add to the work I already done here:
> >> https://github.com/sol/base-compat/pull/2
> >>
> >> (If the patch gets accepted, of course).
> >>
> >> Cheers,
> >> João
> >>
> >> 2014-02-05 Roman Cheplyaka <roma at ro-che.info>:
> >> > Ok, this clears things up. I misinterpreted your approach thinking that
> >> > you're also solving the problem of distinguishing async vs sync
> >> > exceptions, only based on how they were thrown instead of their type.
> >> >
> >> > I now see that it isn't the case -- you're catching *all* exceptions.
> >> > (And run the timeout handler in a different thread.)
> >> >
> >> > So no wonder that asynchronous-exceptions (whose description says that
> >> > it lets differentiate between sync and async exceptions, in a certain
> >> > sense) doesn't help you -- you simply don't want any exceptions at all.
> >> >
> >> > My use case is simpler -- I write testing libraries. If a test throws an
> >> > exception, we have to decide whether we want to report it as a test's
> >> > failure or it's a bigger problem and we want to wrap up.
> >> >
> >> > I don't think there's a universally right way to make this decision. It
> >> > depends on what exceptions exist and what threads they can be thrown to.
> >> > E.g. if there existed something like UserInterrupt but which could be
> >> > thrown to any active thread, not only the main thread, then the approach
> >> > "run in a separate thread and log any exceptions from that thread"
> >> > simply wouldn't work.
> >> >
> >> > For tasty, based on the async exceptions I'm aware of, I think your
> >> > approach is overall better. It's almost as simple, doesn't require
> >> > patching 3rd-party timeout libraries, and catches StackOverflow (which
> >> > is desirable). So I'll switch to it instead.
> >> >
> >> > For smallcheck, the overhead of forkIO might be significant, because it
> >> > has to be performed for every single property check, and those can be
> >> > numerous and very quick. I put together a simple benchmark
> >> > (http://lpaste.net/99532 if anyone is interested) which shows that
> >> > overhead can be noticable (16% for async vs 4% for simple catch) but
> >> > tolerable, and it will be even less for more realistic properties.
> >> > So I'll probably use the async approach there, too, although I may
> >> > reconsider that in the future if I ever get to optimizing smallcheck and
> >> > squeezing out those percents.
> >> >
> >> > As for the package itself, let's see if others will find any good use
> >> > cases for it. I'll update the docs with some conclusions from this
> >> > thread.
> >> >
> >> > And thanks for your input.
> >> >
> >> > Roman
> >> >
> >> > * Michael Snoyman <michael at snoyman.com> [2014-02-05 18:48:22+0200]
> >> >> I can't think of any situation in which the semantics you're implying make
> >> >> sense. To me, catching synchronous exception is a simple concept: if an
> >> >> exception is generated internally to `userAction`, then it's a synchronous
> >> >> exception. If it was terminated by something external, then it's
> >> >> asynchronous. I'm not sure what you're getting at about my approach
> >> >> requiring knowledge of what's going on deep inside a library.
> >> >>
> >> >> The real question which is not explained in your package is what use case
> >> >> you're actually trying to address. Here's a prime example I've run into:
> >> >> you're writing a web application which uses a third-party library. If that
> >> >> library throws an exception of any type, you want to catch the exception
> >> >> and display an appropriate error message (or perhaps return some data from
> >> >> another source). However, we still want the web application to respect
> >> >> timeout messages from the server to avoid slowloris attacks. The handler
> >> >> code would look like:
> >> >>
> >> >> myHandler = do
> >> >>     eres <- tryAnyDeep someLibraryFunction
> >> >>     case eres of
> >> >>         Left e -> tellUser "I'm sorry, there was an issue making the query"
> >> >>         Right x -> displayData x
> >> >>
> >> >> The goal is that, under no circumstances, should someLibraryFunction be
> >> >> able to case the exception to escape tryAnyDeep. This includes rethrowing
> >> >> some async exception that it received from, e.g., a timeout. This would not
> >> >> be honored by trySync.
> >> >>
> >> >> Michael
> >> >
> >> > _______________________________________________
> >> > Haskell-Cafe mailing list
> >> > Haskell-Cafe at haskell.org
> >> > http://www.haskell.org/mailman/listinfo/haskell-cafe
> >> >
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20140206/20859a04/attachment.sig>


More information about the Haskell-Cafe mailing list