delphidelphi-xe5omnithreadlibrary

How can I check exceptions when using IOmniTaskControl/TOmniWorker?


I'm using an IOmniTaskControl/TOmniWorker to funnel code execution onto a specific thread. I'll reuse this IOmniTaskControl repeatedly for multiple Invoke calls. How do I check for exceptions that may have occurred during the Invoke? This is a follow on question to the question/answer: Waiting for Invoke to finish when using IOmniTaskControl/TOmniWorker.

I've checked the ExitCode and FatalException of the IOmniTaskControl but they are not set. It seems that the IOmniTaskControl will create a new task automatically for each Invoke call, and the exception gets placed onto that task, if it occurs. However, I have no reference to that task following the completion of the Invoke. I'm using a TOmniWaitableValue to flag when the invoke completes, but not clear on what I will need to do to make any exception that occurred available to me on return from the WaitFor(...).

Here is a snippet of the structure I have:

interface

type
  TMyTaskProc = reference to procedure;

  TMyTask = class
  private
    FWorker:      IOmniWorker;
    FTaskControl: IOmniTaskControl;
    FWaitable:    IOmniWaitableValue;
  public
    constructor Create;
    destructor Destroy; override;

    procedure AwaitInvoke(Proc: TMyTaskProc); overload;
  end;

implementation

type
  TMyTaskWorker = class(TOmniWorker);

constructor TMyTask.Create;
begin
  inherited;

  FWorker := TMyTaskWorker.Create;
  FTaskControl := CreateTask(FWorker).Run;
  FWaitable := TOmniWaitableValue.Create;
end;

destructor TMyTask.Destroy;
begin
  FTaskControl.Terminate;
  FTaskControl := nil;

  FWaitable := nil;

  inherited;
end;

procedure TMyTask.AwaitInvoke(Proc: TMyTaskProc);
begin
  FWaitable.Reset;

  FTaskControl.Invoke(
    procedure
    begin
      try
        Proc();
      finally
        FWaitable.Signal;
      end;
    end
  );

  FWaitable.WaitFor(INFINITE);
end;

So in the above setup, how can I check after FWaitable.WaitFor(INFINITE) for any exception that might have occurred during the Proc() call. I'd like to just raise it again at that point in the calling thread.


Solution

  • You have to catch the exception at the point when you call Proc and then somehow notify the caller of the situation. For example:

    FTaskControl.Invoke(
      procedure
      var
        return: TOmniValue;
      begin
        return := TOmniValue.Null;
        try
          try
            Proc();
          except
            return.AsException := AcquireExceptionObject;
          end;
        finally
          FWaitable.Signal(return);
        end;
      end
    );
    
    FWaitable.WaitFor(INFINITE);
    if FWaitable.Value.IsException then begin
      Writeln('Caught exception ' + FWaitable.Value.AsException.ClassName);
      FWaitable.Value.AsException.Free;
    end;