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;
Oh, You're still using FreeAndNil !
ReplyDeleteIt's so out of fashion these days !
:-D
I am and I will and everybody that opposes FreeAndNil is just stupid!
ReplyDeleteAnd, of course, :-D
ReplyDelete