110 lines
3.4 KiB
ObjectPascal
110 lines
3.4 KiB
ObjectPascal
unit Myc.Futures;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
Myc.Signals, Myc.Core.Tasks;
|
|
|
|
type
|
|
// Represents the eventual result of an asynchronous operation.
|
|
IMycFuture<T> = interface
|
|
{$REGION 'property access'}
|
|
function GetResult: T;
|
|
function GetDone: IMycState;
|
|
{$ENDREGION}
|
|
// Provides access to the computed result.
|
|
property Result: T read GetResult;
|
|
// Provides access to the completion state.
|
|
property Done: IMycState read GetDone;
|
|
end;
|
|
|
|
// Abstract base class for IMycFuture<T> implementations.
|
|
TMycFuture<T> = class abstract(TInterfacedObject, IMycFuture<T>)
|
|
protected
|
|
function GetResult: T; virtual; abstract;
|
|
function GetDone: IMycState; virtual; abstract;
|
|
end;
|
|
|
|
// Concrete future implementation triggered by an initial state, executing a TFunc<T>.
|
|
TMycInitStateFuncFuture<T> = class(TMycFuture<T>)
|
|
private
|
|
FInit: TMycSubscription;
|
|
FDone: IMycLatch;
|
|
FResult: T;
|
|
protected
|
|
function GetResult: T; override;
|
|
function GetDone: IMycState; override;
|
|
public
|
|
// Creates a future that executes AProc after AInitState is set, using ATaskFactory.
|
|
constructor Create(const ATaskFactory: IMycTaskFactory; const AInitState:
|
|
IMycState; AProc: TFunc<T>);
|
|
// Cleans up resources, primarily by unsubscribing from the initial state.
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ TMycInitStateFuncFuture<T> }
|
|
|
|
constructor TMycInitStateFuncFuture<T>.Create(const ATaskFactory:
|
|
IMycTaskFactory; const AInitState: IMycState; AProc: TFunc<T>);
|
|
begin
|
|
inherited Create;
|
|
Assert(Assigned(AInitState), 'AInitState must be assigned.');
|
|
|
|
FDone := TMycLatch.CreateLatch(1);
|
|
|
|
// Subscribe the job execution to AInitState.
|
|
// The job will run when AInitState notifies the subscriber returned by Run.
|
|
// AStartCount for Run is 1, meaning it waits for one Notify from AInitState's subscription.
|
|
FInit := AInitState.Subscribe(
|
|
ATaskFactory.Run(
|
|
|
|
procedure
|
|
begin
|
|
try
|
|
try
|
|
Self.FResult := AProc();
|
|
except
|
|
Self.FResult := Default(T); // Set result to Default(T) on error
|
|
raise; // Re-raise for TaskFactory to handle
|
|
end;
|
|
finally
|
|
Self.FDone.Notify; // Signal that this future is done (successfully or with error)
|
|
end;
|
|
end
|
|
)
|
|
);
|
|
end;
|
|
|
|
destructor TMycInitStateFuncFuture<T>.Destroy;
|
|
begin
|
|
Assert( FInit.State.IsSet );
|
|
|
|
// Explicitly unsubscribe from the initial state to ensure cleanup.
|
|
FInit.Unsubscribe;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
|
|
function TMycInitStateFuncFuture<T>.GetDone: IMycState;
|
|
begin
|
|
Result := FDone.State;
|
|
end;
|
|
|
|
function TMycInitStateFuncFuture<T>.GetResult: T;
|
|
begin
|
|
if not FDone.State.IsSet then
|
|
begin
|
|
// Design decision: Caller must ensure future is done.
|
|
raise Exception.Create('Result is not yet available for the future.');
|
|
end;
|
|
|
|
// Returns FResult. If AProc failed, FResult is Default(T) and the exception
|
|
// was raised to be handled by the TaskFactory.
|
|
Result := FResult;
|
|
end;
|
|
|
|
end.
|