Pop quiz time! What would the user see if this code is executed in your application?
raise Exception.Create('Exception in Async'); end);
The answer may surprise you: Nothing! At least if you’re not using the latest SVN version.
For a long time exceptions were not something I thought about while developing OmniThreadLibrary.
[I don’t really like to use exceptions (although I will admit that they are useful) and even when I use them I never allow them to “escape” to the global context and become “visible” at the VCL level. In other words, if my application causes a message box with an exception text to pop up, this will indicate an error in my design, not an intention.]
During the OTL history there were quite some changes on how exceptions are handled, but at the end I’ve settled down with the Delphi model – if your TThread descendant raises an exception that is not handled in your code, this exception will be silently ignored. No notification, no log, no popup – it will just disappear.
[Of course, I’m only talking about the end-user experience here. If you run the program in the RAD Studio with debugging enabled, debugger will catch the exception and display a message box.]
While this is good for low-level programming, high-level constructs (i.e. the OtlParallel unit) are not really focused on threads and tasks but on parallel execution of frequently-used parallel architectures. In such context, exceptions can sometimes be used to simplify the program flow. That’s why some of the high-level wrappers got a proper exception handling after the 2.1 release (, , ). Async, however was not one of those. Until few SVN revisions ago, the code above would therefore generate an exception which will later disappear.
This has changed few days ago and again yesterday when I committed few simple fixes for that behavior. Async will now install its own OnTerminated handler and re-raise task exception in it. If you try the same code with the latest revision, you’ll see a message box provided by the VCL exception handler.
What about your code – can it get access to the unhandled exception before the VCL sees it? Sure, just write your own termination handler and handle exception there. Following code explains this approach.
raise Exception.Create('Exception in Async'); end, Parallel.TaskConfig.OnTerminated(
procedure (const task: IOmniTaskControl)
begin if assigned(task.FatalException) then begin
excp := task.DetachException;
Log('Caught async exception %s:%s', [excp.ClassName, excp.Message]);
FreeAndNil(excp); end; end));
All this is also demoed in the updated 48_OtlParallelExceptions demo.