Thursday, July 21, 2011

Life after 2.1: Exceptions in Parallel.Future

The main focus in the next OmniThreadLibrary release is on exception handling in high-level constructs (i.e. the OtlParallel unit). The first one to get this support is Parallel.Future. Why? Simple reason – it has a well-defined point of interaction with the owner (the Value function) and only one background task, which makes exception handling easy to implement.
IOmniFuture<T> was extended with three functions.
  IOmniFuture<T> = interface
    procedure Cancel;
    function  DetachException: Exception;
    function  FatalException: Exception;
    function  IsCancelled: boolean;
    function  IsDone: boolean;
    function  TryValue(timeout_ms: cardinal; var value: T): boolean;
    function  Value: T;
    function  WaitFor(timeout_ms: cardinal): boolean;
  end; { IOmniFuture<T> }
Any exception thrown in the background task and not caught in the future-calculating code will be caught by the IOmniFuture<T> implementation. When a Value is accessed, this exception will be raised in the owner thread.
You can explicitly check if the calculation raised an exception by examining the result of the FatalException function. (Nil, of course, represents normal execution without any exception.) Be aware that the background task must terminate before exception can be examined! If you call FatalException while the background task is still running, the code will block until the task terminates.
This blocking will be undesirable in most circumstances. To wait for the task to complete you can periodically check IsDone function or use the new WaitFor function which waits up to the specified number of milliseconds and returns True if the task has completed its execution.
There’s also the DetachException function which returns task exception object (if any) and detaches it from the IOmniFuture<T> implementor. The calling code is now the sole owner of the exception object and should ultimately destroy it.
New demo 48_OtlParallelExceptions demonstrates three approaches to exception handling in IOmniFuture<T>.
First example catches the exception by wrapping the call to the Value function in try..except.
procedure TForm34.btnFuture1Click(Sender: TObject);
var
  future: IOmniFuture<integer>;
begin
  future := Parallel.Future<integer>(
    function: integer
    begin
      raise ETestException.Create('Exception in Parallel.Future');
    end
  );
  Log('Future is executing ...');
  Sleep(1000); // another long task
  try
    Log('Future retured: %d', [future.Value]);
  except
    on E: Exception do
      Log('Future raised exception %s:%s', [E.ClassName, E.Message]);
  end;
end;
Second example uses WaitFor to wait on task completion and then checks the result of the FatalException function.
procedure TForm34.btnFuture2Click(Sender: TObject);
var
  future: IOmniFuture<integer>;
begin
  future := Parallel.Future<integer>(
    function: integer
    begin
      raise ETestException.Create('Exception in Parallel.Future');
    end
  );
  Log('Future is executing ...');
  future.WaitFor(INFINITE);
  if assigned(future.FatalException) then
    Log('Future raised exception %s:%s', 
      [future.FatalException.ClassName, future.FatalException.Message])
  else
    Log('Future retured: %d', [future.Value]);
end;
Third example again uses WaitFor but then detaches the exception from the future, logs the status and destroys the exception object.
procedure TForm34.btnFuture3Click(Sender: TObject);
var
  excFuture: Exception;
  future   : IOmniFuture<integer>;
begin
  future := Parallel.Future<integer>(
    function: integer
    begin
      raise ETestException.Create('Exception in Parallel.Future');
    end
  );
  Log('Future is executing ...');
  future.WaitFor(INFINITE);
  excFuture := future.DetachException;
  try
    if assigned(excFuture) then
      Log('Future raised exception %s:%s', 
        [excFuture.ClassName, excFuture.Message])
    else
      Log('Future retured: %d', [future.Value]);
  finally FreeAndNil(excFuture); end;
end;

3 comments:

  1. Oh, You're still using FreeAndNil !
    It's so out of fashion these days !

    :-D

    ReplyDelete
  2. I am and I will and everybody that opposes FreeAndNil is just stupid!

    ReplyDelete
  3. And, of course, :-D

    ReplyDelete