Refactoring

This commit is contained in:
Michael Schimmel
2025-06-02 12:27:52 +02:00
parent 86e2729a52
commit 762e7f83e2
8 changed files with 294 additions and 182 deletions
+36 -69
View File
@@ -4,7 +4,7 @@ interface
uses
System.SysUtils,
Myc.Signals, Myc.Core.Tasks;
Myc.Signals, Myc.TaskManager;
type
// Represents the eventual result of an asynchronous operation.
@@ -19,91 +19,58 @@ type
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;
Future<T> = record
strict private
FFuture: IMycFuture<T>;
class constructor CreateClass;
class destructor DestroyClass;
constructor Create(const AFuture: IMycFuture<T>);
public
class function Construct(const Proc: TFunc<T>): Future<T>; overload; static;
class function Construct(const Gate: IMycState; const Proc: TFunc<T>): Future<T>; overload; static;
// 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;
function Chain<S>(const Proc: TFunc<T, S>): Future<S>;
end;
implementation
{ TMycInitStateFuncFuture<T> }
uses
Myc.Core.Futures, Myc.Core.Tasks;
constructor TMycInitStateFuncFuture<T>.Create(const ATaskFactory:
IMycTaskFactory; const AInitState: IMycState; AProc: TFunc<T>);
constructor Future<T>.Create(const AFuture: IMycFuture<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
)
);
FFuture := AFuture;
end;
destructor TMycInitStateFuncFuture<T>.Destroy;
class constructor Future<T>.CreateClass;
begin
Assert( FInit.State.IsSet );
// Explicitly unsubscribe from the initial state to ensure cleanup.
FInit.Unsubscribe;
inherited Destroy;
TMycTaskFactory.AquireTaskManager;
end;
function TMycInitStateFuncFuture<T>.GetDone: IMycState;
class destructor Future<T>.DestroyClass;
begin
Result := FDone.State;
TMycTaskFactory.ReleaseTaskManager;
end;
function TMycInitStateFuncFuture<T>.GetResult: T;
function Future<T>.Chain<S>(const Proc: TFunc<T, S>): Future<S>;
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;
var Cap := FFuture;
// Returns FResult. If AProc failed, FResult is Default(T) and the exception
// was raised to be handled by the TaskFactory.
Result := FResult;
Result := Future<S>.Construct( FFuture.Done,
function: S
begin
Result := Proc( Cap.Result );
end );
end;
class function Future<T>.Construct(const Proc: TFunc<T>): Future<T>;
begin
Result.Create( TMycInitStateFuncFuture<T>.Create( TaskManager, nil, Proc ) );
end;
class function Future<T>.Construct(const Gate: IMycState; const Proc: TFunc<T>): Future<T>;
begin
Result.Create( TMycInitStateFuncFuture<T>.Create( TaskManager, Gate, Proc ) );
end;
end.