Moved signal interfaces inside records

This commit is contained in:
Michael Schimmel
2025-06-05 23:27:48 +02:00
parent 7ba42b0c7c
commit a57b2d50e4
15 changed files with 316 additions and 313 deletions
+5 -5
View File
@@ -9,7 +9,7 @@ uses
Myc.Futures;
type
TMycFuture<T> = class abstract(TInterfacedObject, IMycFuture<T>)
TMycFuture<T> = class abstract(TInterfacedObject, TFuture<T>.IFuture)
protected
function GetResult: T; virtual; abstract;
function GetDone: TState; virtual; abstract;
@@ -23,14 +23,14 @@ type
TMycGateFuncFuture<T> = class(TMycFuture<T>)
private
FInit: TSubscription;
FDone: IMycLatch;
FInit: TSignal.TSubscription;
FDone: TLatch.ILatch;
FResult: T;
protected
function GetResult: T; override;
function GetDone: TState; override;
public
constructor Create(const ATaskManager: IMycTaskManager; const AGate: IMycState; AProc: TFunc<T>);
constructor Create(const ATaskManager: IMycTaskManager; const AGate: TState.IState; AProc: TFunc<T>);
destructor Destroy; override;
end;
@@ -50,7 +50,7 @@ end;
{ TMycGateFuncFuture<T> }
constructor TMycGateFuncFuture<T>.Create(const ATaskManager: IMycTaskManager; const AGate: IMycState; AProc: TFunc<T>);
constructor TMycGateFuncFuture<T>.Create(const ATaskManager: IMycTaskManager; const AGate: TState.IState; AProc: TFunc<T>);
begin
inherited Create;
+3 -3
View File
@@ -18,7 +18,7 @@ type
TMycLazyBase<T> = class(TInterfacedObject, IMycLazy<T>)
strict private
FChanged: TFlag;
FChangeState: TSubscription;
FChangeState: TSignal.TSubscription;
function GetChanged: TState;
protected
function GetValue: T; virtual; abstract;
@@ -97,12 +97,12 @@ constructor TMycFuncLazy<T>.Create(const AChanged: TState; const AProc: TFunc<T>
begin
inherited Create(AChanged);
FProc := AProc;
Assert( Assigned(FProc) );
Assert(Assigned(FProc));
end;
function TMycFuncLazy<T>.GetValue: T;
begin
Result := FProc();
Result := FProc();
end;
{ TMycChainedLazy<T> }
+16 -16
View File
@@ -10,7 +10,7 @@ uses
type
// TMycNullSignal implements a "null object" pattern for IMycState.
// This state is always considered "set".
TMycNullSignal = class(TInterfacedObject, IMycSignal)
TMycNullSignal = class(TInterfacedObject, TSignal.ISignal)
public
function Subscribe(Subscriber: IMycSubscriber): Pointer;
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
@@ -18,14 +18,14 @@ type
// TMycNullState implements a "null object" pattern for IMycState.
// This state is always considered "set".
TMycNullState = class(TMycNullSignal, IMycState)
TMycNullState = class(TMycNullSignal, TState.IState)
protected
// IMycState
// TState.IState
function GetIsSet: Boolean;
end;
// A state that acts as an event. It's state ist always set and it will always Trigger it's subscribers, if it gets notified by itself.
TMycEvent = class(TInterfacedObject, IMycSignal, IMycEvent)
TMycEvent = class(TInterfacedObject, TSignal.ISignal, TEvent.IEvent)
strict private
FSubscribers: TMycNotifyList<IMycSubscriber>;
protected
@@ -38,7 +38,7 @@ type
procedure Trigger;
end;
TMycNullEvent = class(TMycNullSignal, IMycEvent)
TMycNullEvent = class(TMycNullSignal, TEvent.IEvent)
private
procedure Trigger;
end;
@@ -47,13 +47,13 @@ type
// It is initialized with a count. Calls to its Notify method (as an IMycSubscriber)
// decrement this count. When the count reaches zero, the latch transitions to the "set"
// state (IsSet becomes true), notifies its current subscribers once, and then remains set.
TMycLatch = class(TInterfacedObject, IMycState, IMycLatch)
TMycLatch = class(TInterfacedObject, TState.IState, TLatch.ILatch)
strict private
FSubscribers: TMycNotifyList<IMycSubscriber>; // List of subscribers waiting for this latch to be set.
private
[volatile]
FCount: Integer; // The internal countdown value for the latch.
function GetState: TState; // Implementation for IMycLatch.GetState and IMycFlag.GetState.
function GetState: TState; // Implementation for TLatch.ILatch.GetState and IFlag.GetState.
protected
function GetIsSet: Boolean;
public
@@ -62,7 +62,7 @@ type
constructor Create(ACount: Integer);
destructor Destroy; override;
// IMycSignal implementation
// ISignal implementation
function Subscribe(Subscriber: IMycSubscriber): Pointer;
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
@@ -72,7 +72,7 @@ type
function Notify: Boolean;
end;
TMycNullLatch = class(TInterfacedObject, IMycLatch)
TMycNullLatch = class(TInterfacedObject, TLatch.ILatch)
private
function GetState: TState;
function Notify: Boolean;
@@ -81,7 +81,7 @@ type
// TMycFlag implements a resettable "dirty flag".
// It can be explicitly set to dirty (via Notify) or reset to clean (via Reset).
// Subscribers are notified of relevant state changes.
TMycFlag = class(TInterfacedObject, IMycState, IMycFlag)
TMycFlag = class(TInterfacedObject, TState.IState, TFlag.IFlag)
strict private
FSubscribers: TMycNotifyList<IMycSubscriber>; // List of subscribers interested in state changes of this dirty flag.
private
@@ -96,7 +96,7 @@ type
destructor Destroy; override;
// Factory method to create a new TMycFlag instance.
class function CreateDirty: IMycFlag; static;
class function CreateDirty: TFlag.IFlag; static;
function Subscribe(Subscriber: IMycSubscriber): Pointer;
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
@@ -105,11 +105,11 @@ type
// Sets this flag to dirty (true). Notifies subscribers if it transitioned from clean to dirty.
function Notify: Boolean;
// IMycFlag implementation. Resets the flag to clean (false).
// TFlag.IFlag implementation. Resets the flag to clean (false).
function Reset: Boolean;
end;
TMycNullFlag = class(TInterfacedObject, IMycFlag)
TMycNullFlag = class(TInterfacedObject, TFlag.IFlag)
private
function GetState: TState;
function Notify: Boolean;
@@ -250,7 +250,7 @@ end;
function TMycLatch.GetState: TState;
begin
// TMycLatch itself implements IMycState.
// TMycLatch itself implements TState.IState.
Result := Self;
end;
@@ -320,7 +320,7 @@ begin
FSubscribers.Create;
end;
class function TMycFlag.CreateDirty: IMycFlag;
class function TMycFlag.CreateDirty: TFlag.IFlag;
begin
// Factory method to create a new TMycFlag instance.
Result := TMycFlag.Create;
@@ -346,7 +346,7 @@ end;
function TMycFlag.GetState: TState;
begin
// TMycFlag itself implements IMycState.
// TMycFlag itself implements TState.IState.
Result := Self;
end;
+8 -8
View File
@@ -21,7 +21,7 @@ type
// Returns IMycState to await thread completion.
// Exceptions within Proc are caught; the first one is stored
// and can be re-raised in the main thread via WaitFor or Teardown.
function CreateThread(const Proc: TProc): IMycState;
function CreateThread(const Proc: TProc): TState.IState;
// Returns True if called from the main application thread.
function InMainThread: Boolean;
@@ -77,12 +77,12 @@ type
constructor Create;
destructor Destroy; override;
class function CreateAnonymousThread(const DbgName: String; const Proc: TProc): TThread;
function CreateThread(const Proc: TProc): IMycState;
function CreateThread(const Proc: TProc): TState.IState;
procedure HandleException;
procedure EnqueueJob(const Job: TProc);
function Run(Job: TProc): IMycSubscriber;
function CreateTask(const Gate: TState; const Proc: TProc): TSubscription;
procedure WaitFor(State: IMycState);
function CreateTask(const Gate: TState; const Proc: TProc): TSignal.TSubscription;
procedure WaitFor(State: TState.IState);
procedure Teardown;
function InMainThread: Boolean;
function InWorkerThread: Boolean;
@@ -188,7 +188,7 @@ begin
Result.NameThreadForDebugging(DbgName); // Set thread name for debugging
end;
function TMycTaskFactory.CreateTask(const Gate: TState; const Proc: TProc): TSubscription;
function TMycTaskFactory.CreateTask(const Gate: TState; const Proc: TProc): TSignal.TSubscription;
begin
if Gate.IsSet then
begin
@@ -201,9 +201,9 @@ begin
end;
end;
function TMycTaskFactory.CreateThread(const Proc: TProc): IMycState;
function TMycTaskFactory.CreateThread(const Proc: TProc): TState.IState;
var
res: IMycLatch;
res: TLatch.ILatch;
capturedProc: TProc;
begin
// Create a latch that will be signaled when the proc finishes
@@ -345,7 +345,7 @@ begin
end;
end;
procedure TMycTaskFactory.WaitFor(State: IMycState);
procedure TMycTaskFactory.WaitFor(State: TState.IState);
var
lock: TSemaphore; // This is System.SyncObjs.TSemaphore
begin
+26 -25
View File
@@ -9,45 +9,46 @@ uses
type
// Represents the eventual result of an asynchronous operation.
IMycFuture<T> = interface
{$REGION 'property access'}
function GetResult: T;
function GetDone: TState;
{$ENDREGION}
// Provides access to the computed result.
property Result: T read GetResult;
// Provides access to the completion state.
property Done: TState read GetDone;
end;
TFuture<T> = record
type
IFuture = interface
{$REGION 'property access'}
function GetResult: T;
function GetDone: TState;
{$ENDREGION}
// Provides access to the computed result.
property Result: T read GetResult;
// Provides access to the completion state.
property Done: TState read GetDone;
end;
private
FFuture: IMycFuture<T>;
function GetDone: IMycState; inline;
FFuture: IFuture;
function GetDone: TState.IState; inline;
function GetResult: T; inline;
class var
FNull: IMycFuture<T>;
FNull: IFuture;
class constructor CreateClass;
class destructor DestroyClass;
public
constructor Create(const AFuture: IMycFuture<T>);
constructor Create(const AFuture: IFuture);
class operator Implicit(const A: IMycFuture<T>): TFuture<T>; overload;
class operator Implicit(const A: TFuture<T>): IMycFuture<T>; overload;
class operator Implicit(const A: IFuture): TFuture<T>; overload;
class operator Implicit(const A: TFuture<T>): IFuture; overload;
class function Construct(const Proc: TFunc<T>): TFuture<T>; overload; static;
class function Construct(const Gate: IMycState; const Proc: TFunc<T>): TFuture<T>; overload; static;
class function Construct(const Gate: TState.IState; const Proc: TFunc<T>): TFuture<T>; overload; static;
class property Null: IMycFuture<T> read FNull;
class property Null: IFuture read FNull;
function Chain<S>(const Proc: TFunc<T, S>): TFuture<S>;
function WaitFor: T;
property Done: IMycState read GetDone;
property Done: TState.IState read GetDone;
property Result: T read GetResult;
end;
@@ -57,7 +58,7 @@ uses
Myc.Core.Futures,
Myc.Core.Tasks;
constructor TFuture<T>.Create(const AFuture: IMycFuture<T>);
constructor TFuture<T>.Create(const AFuture: IFuture);
begin
FFuture := AFuture;
if not Assigned(FFuture) then
@@ -87,12 +88,12 @@ begin
Result := TMycGateFuncFuture<T>.Create(TaskManager, nil, Proc);
end;
class function TFuture<T>.Construct(const Gate: IMycState; const Proc: TFunc<T>): TFuture<T>;
class function TFuture<T>.Construct(const Gate: TState.IState; const Proc: TFunc<T>): TFuture<T>;
begin
Result := TMycGateFuncFuture<T>.Create(TaskManager, Gate, Proc);
end;
function TFuture<T>.GetDone: IMycState;
function TFuture<T>.GetDone: TState.IState;
begin
Result := FFuture.Done;
end;
@@ -108,12 +109,12 @@ begin
Result := FFuture.Result;
end;
class operator TFuture<T>.Implicit(const A: IMycFuture<T>): TFuture<T>;
class operator TFuture<T>.Implicit(const A: IFuture): TFuture<T>;
begin
Result.Create(A);
end;
class operator TFuture<T>.Implicit(const A: TFuture<T>): IMycFuture<T>;
class operator TFuture<T>.Implicit(const A: TFuture<T>): IFuture;
begin
Result := A.FFuture;
end;
+5 -5
View File
@@ -25,7 +25,7 @@ type
TLazy<T> = record
private
FLazy: IMycLazy<T>;
function GetChanged: IMycState; inline;
function GetChanged: TState.IState; inline;
class var
FNull: IMycLazy<T>;
class constructor CreateClass;
@@ -33,7 +33,7 @@ type
public
constructor Create(const ALazy: IMycLazy<T>);
class function Construct(const Changing: IMycState; const Proc: TFunc<T>): IMycLazy<T>; overload; static;
class function Construct(const Changing: TState.IState; const Proc: TFunc<T>): IMycLazy<T>; overload; static;
class function Construct(const Value: IMycLazy<T>): IMycLazy<T>; overload; static;
class operator Implicit(const A: IMycLazy<T>): TLazy<T>; overload;
@@ -41,7 +41,7 @@ type
function Pop(out Res: T): Boolean; inline;
property Changed: IMycState read GetChanged;
property Changed: TState.IState read GetChanged;
end;
implementation
@@ -56,7 +56,7 @@ begin
FLazy := FNull;
end;
class function TLazy<T>.Construct(const Changing: IMycState; const Proc: TFunc<T>): IMycLazy<T>;
class function TLazy<T>.Construct(const Changing: TState.IState; const Proc: TFunc<T>): IMycLazy<T>;
begin
Result := TMycFuncLazy<T>.Create(Changing, Proc);
end;
@@ -71,7 +71,7 @@ begin
Result := TMycChainedLazy<T>.Create(Value);
end;
function TLazy<T>.GetChanged: IMycState;
function TLazy<T>.GetChanged: TState.IState;
begin
Result := FLazy.Changed;
end;
+121 -119
View File
@@ -10,160 +10,168 @@ type
function Notify: Boolean;
end;
TSubscriptionTag = Pointer;
IMycSignal = interface
function Subscribe(Subscriber: IMycSubscriber): TSubscriptionTag;
procedure Unsubscribe(Tag: TSubscriptionTag);
end;
TSubscription = record
private
FState: IMycSignal;
FTag: TSubscriptionTag;
constructor Create(const AState: IMycSignal; ATag: Pointer);
public
class operator Initialize(out Dest: TSubscription);
// Unsubscribes from the associated TState.
procedure Unsubscribe;
end;
TSignal = record
type
TSubscriptionTag = Pointer;
ISignal = interface
function Subscribe(Subscriber: IMycSubscriber): TSubscriptionTag;
procedure Unsubscribe(Tag: TSubscriptionTag);
end;
TSubscription = record
private
FSignal: ISignal;
FTag: TSubscriptionTag;
constructor Create(const ASignal: ISignal; ATag: TSubscriptionTag);
public
class operator Initialize(out Dest: TSubscription);
// Unsubscribes from the associated signal
procedure Unsubscribe;
end;
strict private
class var
FNull: IMycSignal;
class var
FNull: ISignal;
class constructor ClassCreate;
private
FSignal: IMycSignal;
FSignal: ISignal;
public
constructor Create(const ASignal: IMycSignal);
constructor Create(const ASignal: ISignal);
class operator Initialize(out Dest: TSignal);
class operator Implicit(const A: IMycSignal): TSignal; overload;
class operator Implicit(const A: TSignal): IMycSignal; overload;
class operator Implicit(const A: ISignal): TSignal; overload;
class operator Implicit(const A: TSignal): ISignal; overload;
function Subscribe(Subscriber: IMycSubscriber): TSubscription; inline;
procedure Unsubscribe(Tag: Pointer); inline;
class property Null: IMycSignal read FNull;
end;
IMycEvent = interface( IMycSignal )
procedure Trigger;
class property Null: ISignal read FNull;
end;
TEvent = record
type
IEvent = interface(TSignal.ISignal)
procedure Trigger;
end;
strict private
class var
FNull: IMycEvent;
FNull: IEvent;
class constructor ClassCreate;
private
FEvent: IMycEvent;
FEvent: IEvent;
public
constructor Create(const AEvent: IMycEvent);
constructor Create(const AEvent: IEvent);
class operator Initialize(out Dest: TEvent);
class operator Implicit(const A: IMycEvent): TEvent; overload;
class operator Implicit(const A: TEvent): IMycEvent; overload;
class operator Implicit(const A: IEvent): TEvent; overload;
class operator Implicit(const A: TEvent): IEvent; overload;
class property Null: IMycEvent read FNull;
class property Null: IEvent read FNull;
function Subscribe(Subscriber: IMycSubscriber): TSubscription; inline;
function Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription; inline;
procedure Unsubscribe(Tag: Pointer); inline;
end;
IMycState = interface(IMycSignal)
// Represents a subscribable TState that can be queried.
{$REGION 'property access'}
function GetIsSet: Boolean;
{$ENDREGION}
// IsSet is true if the TState has been reached or the condition is met.
property IsSet: Boolean read GetIsSet;
end;
TState = record
type
IState = interface(TSignal.ISignal)
{$REGION 'property access'}
function GetIsSet: Boolean;
{$ENDREGION}
// IsSet is true if the TState has been reached or the condition is met.
property IsSet: Boolean read GetIsSet;
end;
TState = record // helper for IMycState
strict private
class var
FNull: IMycState;
FNull: IState;
class constructor ClassCreate;
private
FState: IMycState;
FState: IState;
function GetIsSet: Boolean; inline;
public
constructor Create(const AState: IMycState);
constructor Create(const AState: IState);
class operator Initialize(out Dest: TState);
class operator Implicit(const A: IMycState): TState; overload;
class operator Implicit(const A: TState): IMycState; overload;
class operator Implicit(const A: IState): TState; overload;
class operator Implicit(const A: TState): IState; overload;
class function All(const States: TArray<TState>): TState; static;
class function Any(const States: TArray<TState>): TState; static;
class property Null: IMycState read FNull;
class property Null: IState read FNull;
function Subscribe(Subscriber: IMycSubscriber): TSubscription; inline;
function Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription; inline;
procedure Unsubscribe(Tag: Pointer); inline;
property IsSet: Boolean read GetIsSet;
end;
IMycLatch = interface(IMycSubscriber)
function GetState: TState;
property State: TState read GetState;
end;
// A latch is a specific type of event that, once set, remains set (non-resettable).
// It becomes set when the internal countdown reaches zero.
TLatch = record
type
ILatch = interface(IMycSubscriber)
function GetState: TState;
property State: TState read GetState;
end;
strict private
class var
FNull: IMycLatch;
FNull: ILatch;
class constructor ClassCreate;
private
FLatch: IMycLatch;
FLatch: ILatch;
function GetState: TState; inline;
public
constructor Create(const ALatch: IMycLatch);
constructor Create(const ALatch: ILatch);
class operator Initialize(out Dest: TLatch);
class operator Implicit(const A: IMycLatch): TLatch; overload;
class operator Implicit(const A: TLatch): IMycLatch; overload;
class operator Implicit(const A: ILatch): TLatch; overload;
class operator Implicit(const A: TLatch): ILatch; overload;
// A latch is a specific type of event that, once set, remains set (non-resettable).
// It becomes set when the internal countdown reaches zero.
class function CreateLatch(Count: Integer): TLatch; static;
class function Enqueue(var Gate: TLatch; Count: Integer = 1): TState; static;
class property Null: IMycLatch read FNull;
class property Null: ILatch read FNull;
function Notify: Boolean; inline;
property State: TState read GetState;
end;
// IMycFlag represents a resettable flag, typically indicating if a State is "dirty" (requiring attention) or "clean".
IMycFlag = interface(IMycSubscriber)
function GetState: TState;
// Resets the flag to its "clean" (not set / not dirty) State.
// Returns true if the flag was actually dirty before this reset, false otherwise.
function Reset: Boolean;
property State: TState read GetState;
end;
// TFlag represents a resettable flag, typically indicating if a State is "dirty" (requiring attention) or "clean".
TFlag = record
type
IFlag = interface(IMycSubscriber)
function GetState: TState;
function Reset: Boolean;
property State: TState read GetState;
end;
strict private
class var
FNull: IMycFlag;
FNull: IFlag;
class constructor ClassCreate;
private
FDirty: IMycFlag;
FDirty: IFlag;
function GetState: TState; inline;
public
constructor Create(const ADirty: IMycFlag);
constructor Create(const ADirty: IFlag);
class operator Initialize(out Dest: TFlag);
class operator Implicit(const A: IMycFlag): TFlag; overload;
class operator Implicit(const A: TFlag): IMycFlag; overload;
class operator Implicit(const A: IFlag): TFlag; overload;
class operator Implicit(const A: TFlag): IFlag; overload;
class function CreateFlag: IMycFlag; static;
class property Null: IMycFlag read FNull;
class function CreateFlag: IFlag; static;
class property Null: IFlag read FNull;
function Notify: Boolean; inline;
function Reset: Boolean;
@@ -177,31 +185,31 @@ uses
Myc.Core.Notifier,
Myc.Core.Signals;
{ TState.TSubscription }
{ TSignal.TSubscription }
constructor TSubscription.Create(const AState: IMycSignal; ATag: Pointer);
constructor TSignal.TSubscription.Create(const ASignal: ISignal; ATag: TSubscriptionTag);
begin
Assert(Assigned(AState));
FState := AState;
FSignal := ASignal;
FTag := ATag;
Assert(Assigned(FSignal));
end;
procedure TSubscription.Unsubscribe;
procedure TSignal.TSubscription.Unsubscribe;
begin
FState.Unsubscribe(FTag);
FState := TState.Null;
FSignal.Unsubscribe(FTag);
FSignal := TSignal.Null;
FTag := nil;
end;
class operator TSubscription.Initialize(out Dest: TSubscription);
class operator TSignal.TSubscription.Initialize(out Dest: TSubscription);
begin
Dest.FState := TState.Null;
Dest.FSignal := TSignal.Null;
Dest.FTag := nil;
end;
{ TState }
constructor TState.Create(const AState: IMycState);
constructor TState.Create(const AState: IState);
begin
FState := AState;
if not Assigned(FState) then
@@ -216,7 +224,7 @@ end;
class function TState.All(const States: TArray<TState>): TState;
var
Latch: IMycLatch;
Latch: TLatch.ILatch;
begin
Latch := TLatch.CreateLatch(Length(States));
for var i := 0 to High(States) do
@@ -226,7 +234,7 @@ end;
class function TState.Any(const States: TArray<TState>): TState;
var
Latch: IMycLatch;
Latch: TLatch.ILatch;
begin
if Length(States) = 0 then
begin
@@ -246,9 +254,9 @@ begin
Result := FState.IsSet;
end;
function TState.Subscribe(Subscriber: IMycSubscriber): TSubscription;
function TState.Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription;
begin
Result := TSubscription.Create(FState, FState.Subscribe(Subscriber));
Result := TSignal.TSubscription.Create(FState, FState.Subscribe(Subscriber));
end;
procedure TState.Unsubscribe(Tag: Pointer);
@@ -256,12 +264,12 @@ begin
FState.Unsubscribe(Tag);
end;
class operator TState.Implicit(const A: TState): IMycState;
class operator TState.Implicit(const A: TState): IState;
begin
Result := A.FState;
end;
class operator TState.Implicit(const A: IMycState): TState;
class operator TState.Implicit(const A: IState): TState;
begin
Result.Create(A);
end;
@@ -273,7 +281,7 @@ end;
{ TEvent }
constructor TEvent.Create(const AEvent: IMycEvent);
constructor TEvent.Create(const AEvent: IEvent);
begin
FEvent := AEvent;
if not Assigned(FEvent) then
@@ -285,9 +293,9 @@ begin
FNull := TMycNullEvent.Create();
end;
function TEvent.Subscribe(Subscriber: IMycSubscriber): TSubscription;
function TEvent.Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription;
begin
Result := TSubscription.Create(FEvent, FEvent.Subscribe(Subscriber));
Result := TSignal.TSubscription.Create(FEvent, FEvent.Subscribe(Subscriber));
end;
procedure TEvent.Unsubscribe(Tag: Pointer);
@@ -295,12 +303,12 @@ begin
FEvent.Unsubscribe(Tag);
end;
class operator TEvent.Implicit(const A: TEvent): IMycEvent;
class operator TEvent.Implicit(const A: TEvent): IEvent;
begin
Result := A.FEvent;
end;
class operator TEvent.Implicit(const A: IMycEvent): TEvent;
class operator TEvent.Implicit(const A: IEvent): TEvent;
begin
Result.Create(A);
end;
@@ -317,14 +325,14 @@ begin
FNull := TMycNullFlag.Create;
end;
constructor TFlag.Create(const ADirty: IMycFlag);
constructor TFlag.Create(const ADirty: IFlag);
begin
FDirty := ADirty;
if not Assigned(FDirty) then
FDirty := FNull;
end;
class function TFlag.CreateFlag: IMycFlag;
class function TFlag.CreateFlag: IFlag;
begin
Result := TMycFlag.Create;
end;
@@ -344,12 +352,12 @@ begin
Result := FDirty.Reset;
end;
class operator TFlag.Implicit(const A: IMycFlag): TFlag;
class operator TFlag.Implicit(const A: IFlag): TFlag;
begin
Result.Create(A);
end;
class operator TFlag.Implicit(const A: TFlag): IMycFlag;
class operator TFlag.Implicit(const A: TFlag): IFlag;
begin
Result := A.FDirty;
end;
@@ -367,8 +375,7 @@ begin
FNull := TMycNullLatch.Create;
end;
constructor TLatch.Create(const ALatch: IMycLatch);
constructor TLatch.Create(const ALatch: TLatch.ILatch);
begin
FLatch := ALatch;
if not Assigned(FLatch) then
@@ -401,12 +408,12 @@ begin
Result := FLatch.Notify;
end;
class operator TLatch.Implicit(const A: IMycLatch): TLatch;
class operator TLatch.Implicit(const A: TLatch.ILatch): TLatch;
begin
Result.Create(A);
end;
class operator TLatch.Implicit(const A: TLatch): IMycLatch;
class operator TLatch.Implicit(const A: TLatch): TLatch.ILatch;
begin
Result := A.FLatch;
end;
@@ -423,7 +430,7 @@ end;
{ TSignal }
constructor TSignal.Create(const ASignal: IMycSignal);
constructor TSignal.Create(const ASignal: ISignal);
begin
FSignal := ASignal;
if not Assigned(FSignal) then
@@ -435,17 +442,12 @@ begin
Result := TSubscription.Create(FSignal, FSignal.Subscribe(Subscriber));
end;
procedure TSignal.Unsubscribe(Tag: Pointer);
begin
FSignal.Unsubscribe(Tag);
end;
class operator TSignal.Implicit(const A: TSignal): IMycSignal;
class operator TSignal.Implicit(const A: TSignal): ISignal;
begin
Result := A.FSignal;
end;
class operator TSignal.Implicit(const A: IMycSignal): TSignal;
class operator TSignal.Implicit(const A: ISignal): TSignal;
begin
Result.Create(A);
end;
+6 -6
View File
@@ -9,14 +9,14 @@ uses
type
IMycTaskManager = interface
// TOD Dokumentation
function CreateTask(const Gate: TState; const Proc: TProc): TSubscription;
function CreateTask(const Gate: TState; const Proc: TProc): TSignal.TSubscription;
// Waits for the operation associated with State to complete.
// Must not be called from a worker thread of this factory.
// After waiting, or if the state is already set, any first stored exception
// from any worker thread of this factory will be re-raised in the calling (main) thread.
// Raises ETaskException if called from within a worker thread.
procedure WaitFor(State: IMycState);
procedure WaitFor(State: TState.IState);
end;
var
@@ -41,8 +41,8 @@ type
TMycTaskManagerMock = class(TInterfacedObject, IMycTaskManager)
public
// IMycTaskManager
function CreateTask(const Gate: TState; const Proc: TProc): TSubscription;
procedure WaitFor(State: IMycState);
function CreateTask(const Gate: TState; const Proc: TProc): TSignal.TSubscription;
procedure WaitFor(State: TState.IState);
constructor Create;
end;
@@ -72,12 +72,12 @@ begin
inherited Create;
end;
function TMycTaskManagerMock.CreateTask(const Gate: TState; const Proc: TProc): TSubscription;
function TMycTaskManagerMock.CreateTask(const Gate: TState; const Proc: TProc): TSignal.TSubscription;
begin
Result := Gate.Subscribe(TMycExecMock.Create(Proc));
end;
procedure TMycTaskManagerMock.WaitFor(State: IMycState);
procedure TMycTaskManagerMock.WaitFor(State: TState.IState);
begin
Assert(State.IsSet);
end;
+18 -18
View File
@@ -103,7 +103,7 @@ end;
procedure TTestMycCoreLazy.TestNullLazy_GetChanged_IsAlwaysNullState;
var
nullLazy: IMycLazy<Integer>;
changedState: IMycState;
changedState: TState.IState;
begin
nullLazy := TMycNullLazy<Integer>.Create as IMycLazy<Integer>;
Assert.IsNotNull(nullLazy, 'TMycNullLazy<Integer> instance should not be nil');
@@ -142,12 +142,12 @@ end;
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultInterfaceAndTrue;
var
nullLazy: IMycLazy<IMycState>;
value: IMycState;
nullLazy: IMycLazy<TState.IState>;
value: TState.IState;
result: Boolean;
begin
nullLazy := TMycNullLazy<IMycState>.Create as IMycLazy<IMycState>;
Assert.IsNotNull(nullLazy, 'TMycNullLazy<IMycState> instance should not be nil');
nullLazy := TMycNullLazy<TState.IState>.Create as IMycLazy<TState.IState>;
Assert.IsNotNull(nullLazy, 'TMycNullLazy<TState.IState> instance should not be nil');
value := TState.Null;
result := nullLazy.Pop(value);
Assert.IsTrue(result, 'TMycNullLazy.Pop should return true');
@@ -158,15 +158,15 @@ end;
procedure TTestMycCoreLazy.TestFuncLazy_GetChanged_IsAlwaysTrueAfterCreation;
var
funcLazy: IMycLazy<Integer>;
changedState: IMycState;
sourceDirty: IMycFlag;
changedState: TState.IState;
sourceDirty: TFlag.IFlag;
begin
sourceDirty := TFlag.CreateFlag;
sourceDirty.Reset;
funcLazy := TMycFuncLazy<Integer>.Create(sourceDirty.State, function: Integer begin Result := 10; end);
Assert.IsNotNull(funcLazy, 'TMycFuncLazy<Integer> instance should not be nil');
changedState := funcLazy.GetChanged;
Assert.IsNotNull(changedState, 'funcLazy.GetChanged should return a valid IMycState');
Assert.IsNotNull(changedState, 'funcLazy.GetChanged should return a valid TState.IState');
Assert.IsTrue(changedState.IsSet, 'By design, funcLazy.GetChanged.IsSet should be true immediately after creation');
end;
@@ -175,7 +175,7 @@ var
funcLazy: IMycLazy<Integer>;
value: Integer;
result: Boolean;
sourceDirty: IMycFlag;
sourceDirty: TFlag.IFlag;
procExecuted: Boolean;
expectedValue: Integer;
begin
@@ -207,7 +207,7 @@ end;
procedure TTestMycCoreLazy.TestFuncLazy_AfterFirstPop_IfNoSourceChange_GetChangedIsFalse;
var
funcLazy: IMycLazy<Integer>;
sourceDirty: IMycFlag;
sourceDirty: TFlag.IFlag;
initialProcValue: Integer;
begin
sourceDirty := TFlag.CreateFlag;
@@ -223,7 +223,7 @@ var
funcLazy: IMycLazy<Integer>;
value: Integer;
result: Boolean;
sourceDirty: IMycFlag;
sourceDirty: TFlag.IFlag;
initialProcValue: Integer;
procExecutedCount: Integer;
begin
@@ -250,8 +250,8 @@ end;
procedure TTestMycCoreLazy.TestFuncLazy_AfterSourceChange_GetChanged_IsSet;
var
funcLazy: IMycLazy<Integer>;
changedState: IMycState;
sourceDirty: IMycFlag;
changedState: TState.IState;
sourceDirty: TFlag.IFlag;
initialProcValue: Integer;
begin
sourceDirty := TFlag.CreateFlag;
@@ -269,7 +269,7 @@ var
funcLazy: IMycLazy<Integer>;
value: Integer;
result: Boolean;
sourceDirty: IMycFlag;
sourceDirty: TFlag.IFlag;
procCallCount: Integer;
currentExpectedValue: Integer;
begin
@@ -307,7 +307,7 @@ var
funcLazy: IMycLazy<Integer>;
value: Integer;
resultPop1, resultPop2: Boolean;
sourceDirty: IMycFlag;
sourceDirty: TFlag.IFlag;
procCallCount: Integer;
begin
sourceDirty := TFlag.CreateFlag;
@@ -339,7 +339,7 @@ var
funcLazy: IMycLazy<Integer>;
value: Integer;
result: Boolean;
sourceDirty: IMycFlag;
sourceDirty: TFlag.IFlag;
procCallCount: Integer;
initialValue, firstSourceChangeValue: Integer;
begin
@@ -399,7 +399,7 @@ end;
procedure TTestMycCoreLazy.TestFuncLazy_Destroy_UnsubscribesFromSource;
var
funcLazyObj: TMycFuncLazy<Integer>;
sourceDirty: IMycFlag;
sourceDirty: TFlag.IFlag;
tempVal: Integer;
begin
sourceDirty := TFlag.CreateFlag;
@@ -420,7 +420,7 @@ var
funcLazy: IMycLazy<Integer>;
value: Integer;
result: Boolean;
sourceDirty: IMycFlag;
sourceDirty: TFlag.IFlag;
expectedValue: Integer;
procExecuted: Boolean;
begin
+2 -2
View File
@@ -12,7 +12,7 @@ type
[TestFixture]
TTestMyLazy = class(TObject)
private
FChangingSignal: IMycFlag; // Used as the 'Changing' state for functional lazy objects
FChangingSignal: TFlag.IFlag; // Used as the 'Changing' state for functional lazy objects
// Helper to consume the initial pop, which is always expected to succeed
// for a TLazy wrapping a functional lazy object due to "Changed.IsSet initially true" design.
@@ -281,7 +281,7 @@ end;
procedure TTestMyLazy.TestConstruct_Destruction_UnsubscribesAndNoCrashOnSourceNotify;
var
lazyIntf: IMycLazy<Integer>;
localChangingSignal: IMycFlag; // Use a local signal for this test to control its lifetime
localChangingSignal: TFlag.IFlag; // Use a local signal for this test to control its lifetime
begin
localChangingSignal := TFlag.CreateFlag;
localChangingSignal.Reset;
+35 -35
View File
@@ -24,7 +24,7 @@ type
TTestMycDirtyFlag = class(TObject)
private
// Helper to create a dirty flag and optionally reset it for a clean initial state
function CreateAndPrepareDirtyFlag(StartDirty: Boolean = True): IMycFlag;
function CreateAndPrepareDirtyFlag(StartDirty: Boolean = True): TFlag.IFlag;
public
[Setup]
procedure Setup;
@@ -103,7 +103,7 @@ end;
{ TTestMycDirtyFlag }
function TTestMycDirtyFlag.CreateAndPrepareDirtyFlag(StartDirty: Boolean = True): IMycFlag;
function TTestMycDirtyFlag.CreateAndPrepareDirtyFlag(StartDirty: Boolean = True): TFlag.IFlag;
begin
Result := TFlag.CreateFlag; // Initially dirty
if not StartDirty then
@@ -125,7 +125,7 @@ end;
procedure TTestMycDirtyFlag.TestCreate_InitialStateIsDirty;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
begin
dirtyFlag := TFlag.CreateFlag;
Assert.IsTrue(dirtyFlag.State.IsSet, 'Newly created dirty flag should be IsSet (dirty).');
@@ -133,7 +133,7 @@ end;
procedure TTestMycDirtyFlag.TestReset_FromDirtyState_BecomesCleanAndReturnsTrue;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
resetResult: Boolean;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(True); // Start dirty
@@ -146,7 +146,7 @@ end;
procedure TTestMycDirtyFlag.TestReset_FromCleanState_StaysCleanAndReturnsFalse;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
resetResult: Boolean;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(False); // Start clean
@@ -159,7 +159,7 @@ end;
procedure TTestMycDirtyFlag.TestNotify_OnCleanFlag_SetsDirtyAndReturnsTrue;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
notifyResult: Boolean;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(False); // Start clean
@@ -172,7 +172,7 @@ end;
procedure TTestMycDirtyFlag.TestNotify_OnDirtyFlag_StaysDirtyAndReturnsFalse;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
notifyResult: Boolean;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(True); // Start dirty
@@ -187,9 +187,9 @@ end;
procedure TTestMycDirtyFlag.TestSubscribe_ToAlreadyDirtyFlag_NotifiesSubscriberImmediately;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
subscriber: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(True); // Start dirty
subscriber := TMockSubscriber.Create;
@@ -202,9 +202,9 @@ end;
procedure TTestMycDirtyFlag.TestSubscribe_ToCleanFlag_DoesNotNotifySubscriberImmediately;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
subscriber: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(False); // Start clean
subscriber := TMockSubscriber.Create;
@@ -217,9 +217,9 @@ end;
procedure TTestMycDirtyFlag.TestSubscriber_Notified_WhenFlagTransitionsToDirty;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
subscriber: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(False); // Start clean
subscriber := TMockSubscriber.Create;
@@ -234,9 +234,9 @@ end;
procedure TTestMycDirtyFlag.TestSubscriber_NotNotified_WhenSettingAlreadyDirtyFlagViaNotify;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
subscriber: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(True); // Start dirty
subscriber := TMockSubscriber.Create;
@@ -251,9 +251,9 @@ end;
procedure TTestMycDirtyFlag.TestSubscriber_NotNotified_ByResetAction;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
subscriber: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(True); // Start dirty
subscriber := TMockSubscriber.Create;
@@ -269,9 +269,9 @@ end;
procedure TTestMycDirtyFlag.TestMultipleSubscribers_Notified_WhenFlagTransitionsToDirty;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
sub1, sub2: TMockSubscriber;
subscription1, subscription2: TSubscription;
subscription1, subscription2: TSignal.TSubscription;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(False); // Start clean
sub1 := TMockSubscriber.Create;
@@ -287,9 +287,9 @@ end;
procedure TTestMycDirtyFlag.TestUnsubscribe_SubscriberNotNotified;
var
dirtyFlag: IMycFlag;
dirtyFlag: TFlag.IFlag;
subscriber: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
dirtyFlag := CreateAndPrepareDirtyFlag(False); // Start clean
subscriber := TMockSubscriber.Create;
@@ -308,9 +308,9 @@ end;
procedure TTestMycDirtyFlag.TestChain_A_Notifies_B_SimplePropagation;
var
flagA, flagB: IMycFlag;
flagA, flagB: TFlag.IFlag;
subToB: TMockSubscriber;
subscriptionA_B, subscriptionMock_B: TSubscription;
subscriptionA_B, subscriptionMock_B: TSignal.TSubscription;
begin
flagA := CreateAndPrepareDirtyFlag(False); // A starts clean
flagB := CreateAndPrepareDirtyFlag(False); // B starts clean
@@ -328,9 +328,9 @@ end;
procedure TTestMycDirtyFlag.TestChain_A_Notifies_B_Notifies_C_SimplePropagation;
var
flagA, flagB, flagC: IMycFlag;
flagA, flagB, flagC: TFlag.IFlag;
subToC: TMockSubscriber;
subscriptionB_A, subscriptionC_B, subscriptionMock_C: TSubscription;
subscriptionB_A, subscriptionC_B, subscriptionMock_C: TSignal.TSubscription;
begin
flagA := CreateAndPrepareDirtyFlag(False);
flagB := CreateAndPrepareDirtyFlag(False);
@@ -353,9 +353,9 @@ end;
procedure TTestMycDirtyFlag.TestChain_A_B_ResetB_RetriggerA_BStaysCleanIfANotChanged;
var
flagA, flagB: IMycFlag;
flagA, flagB: TFlag.IFlag;
subToB: TMockSubscriber;
subscriptionA_B, subscriptionMock_B: TSubscription;
subscriptionA_B, subscriptionMock_B: TSignal.TSubscription;
begin
flagA := CreateAndPrepareDirtyFlag(False);
flagB := CreateAndPrepareDirtyFlag(False);
@@ -382,9 +382,9 @@ end;
procedure TTestMycDirtyFlag.TestChain_A_B_ResetA_ThenTriggerA_FullPropagationToB;
var
flagA, flagB: IMycFlag;
flagA, flagB: TFlag.IFlag;
subToB: TMockSubscriber;
subscriptionA_B, subscriptionMock_B: TSubscription;
subscriptionA_B, subscriptionMock_B: TSignal.TSubscription;
begin
flagA := CreateAndPrepareDirtyFlag(False);
flagB := CreateAndPrepareDirtyFlag(False);
@@ -411,9 +411,9 @@ end;
procedure TTestMycDirtyFlag.TestChain_A_B_C_IntermediateBReset_RetriggerA_PropagatesToC;
var
flagA, flagB, flagC: IMycFlag;
flagA, flagB, flagC: TFlag.IFlag;
subToC: TMockSubscriber;
subscriptionB_A, subscriptionC_B, subscriptionMock_C: TSubscription;
subscriptionB_A, subscriptionC_B, subscriptionMock_C: TSignal.TSubscription;
begin
flagA := CreateAndPrepareDirtyFlag(False);
flagB := CreateAndPrepareDirtyFlag(False);
@@ -450,9 +450,9 @@ end;
procedure TTestMycDirtyFlag.TestChain_A_B_C_AllReset_RetriggerA_FullPropagation;
var
flagA, flagB, flagC: IMycFlag;
flagA, flagB, flagC: TFlag.IFlag;
subToC: TMockSubscriber;
subscriptionB_A, subscriptionC_B, subscriptionMock_C: TSubscription;
subscriptionB_A, subscriptionC_B, subscriptionMock_C: TSignal.TSubscription;
begin
flagA := CreateAndPrepareDirtyFlag(False);
flagB := CreateAndPrepareDirtyFlag(False);
@@ -490,9 +490,9 @@ end;
procedure TTestMycDirtyFlag.TestChain_A_B_C_TriggerA_ResetA_TriggerA_EnsureCNotifiedCorrectly;
var
flagA, flagB, flagC: IMycFlag;
flagA, flagB, flagC: TFlag.IFlag;
subToC: TMockSubscriber;
subscriptionB_A, subscriptionC_B, subscriptionMock_C: TSubscription;
subscriptionB_A, subscriptionC_B, subscriptionMock_C: TSignal.TSubscription;
begin
flagA := CreateAndPrepareDirtyFlag(False);
flagB := CreateAndPrepareDirtyFlag(False);
+33 -33
View File
@@ -120,7 +120,7 @@ end;
procedure TTestMycLatch.TestCreate_InitialState(InitialCount: Integer; ExpectedIsSet: Boolean);
var
latch: IMycLatch;
latch: TLatch.ILatch;
begin
// TMycLatch.CreateLatch returns TMycLatch.Null if InitialCount <= 0
latch := TLatch.CreateLatch(InitialCount);
@@ -133,11 +133,11 @@ procedure TTestMycLatch.TestNotify_ReturnValueAndState_AfterOneNotify(
ExpectedIsSet: Boolean
);
var
latch: IMycLatch;
latch: TLatch.ILatch;
returnedValueFromNotify: Boolean;
begin
latch := TLatch.CreateLatch(InitialCount);
returnedValueFromNotify := latch.Notify; // This is IMycLatch (as IMycSubscriber).Notify
returnedValueFromNotify := latch.Notify; // This is TLatch.ILatch (as IMycSubscriber).Notify
Assert.AreEqual(
ExpectedReturn,
@@ -153,7 +153,7 @@ end;
procedure TTestMycLatch.TestNotify_DecrementsCounter_StateChanges;
var
latch: IMycLatch;
latch: TLatch.ILatch;
begin
latch := TLatch.CreateLatch(1);
Assert.IsFalse(latch.State.IsSet, 'Latch should initially not be set (Count=1).');
@@ -163,7 +163,7 @@ end;
procedure TTestMycLatch.TestNotify_MultipleNotifies_CounterBecomesNegativeAndStaysSet;
var
latch: IMycLatch;
latch: TLatch.ILatch;
begin
latch := TLatch.CreateLatch(1);
latch.Notify; // Count becomes 0, IsSet = True
@@ -176,9 +176,9 @@ end;
procedure TTestMycLatch.TestSubscriber_Single_IsNotifiedWhenLatchSet;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSub: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
latch := TLatch.CreateLatch(1);
mockSub := TMockSubscriber.Create;
@@ -195,9 +195,9 @@ end;
procedure TTestMycLatch.TestSubscriber_Multiple_AreNotifiedWhenLatchSet;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSub1, mockSub2, mockSub3: TMockSubscriber;
sub1, sub2, sub3: TSubscription; // Keep subscriptions in scope
sub1, sub2, sub3: TSignal.TSubscription; // Keep subscriptions in scope
begin
latch := TLatch.CreateLatch(1);
mockSub1 := TMockSubscriber.Create;
@@ -217,9 +217,9 @@ end;
procedure TTestMycLatch.TestSubscriber_NotNotified_BeforeLatchSet;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSub: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
latch := TLatch.CreateLatch(2); // Count = 2
mockSub := TMockSubscriber.Create;
@@ -232,9 +232,9 @@ end;
procedure TTestMycLatch.TestSubscriber_NotifiedOnlyOnce_AfterLatchSetAndUnadvised;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSub: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
latch := TLatch.CreateLatch(1);
mockSub := TMockSubscriber.Create;
@@ -249,9 +249,9 @@ end;
procedure TTestMycLatch.TestSubscribe_ToAlreadySetLatch_NotifiesImmediately;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSub1, mockSub2: TMockSubscriber;
subscription1, subscription2: TSubscription;
subscription1, subscription2: TSignal.TSubscription;
begin
latch := TLatch.CreateLatch(1); // Create with count 1
mockSub1 := TMockSubscriber.Create;
@@ -277,9 +277,9 @@ end;
procedure TTestMycLatch.TestChaining_A_Notifies_B;
var
latchA, latchB: IMycLatch;
latchA, latchB: TLatch.ILatch;
mockSubForB: TMockSubscriber;
subHandle_B_listens_A, subHandle_Mock_listens_B: TSubscription;
subHandle_B_listens_A, subHandle_Mock_listens_B: TSignal.TSubscription;
begin
latchA := TLatch.CreateLatch(1);
latchB := TLatch.CreateLatch(1); // latchB is an IMycSubscriber
@@ -302,9 +302,9 @@ end;
procedure TTestMycLatch.TestCascade_A_Notifies_B_Notifies_C;
var
latchA, latchB, latchC: IMycLatch;
latchA, latchB, latchC: TLatch.ILatch;
mockSubForC: TMockSubscriber;
sub_Mock_C, sub_C_B, sub_B_A: TSubscription;
sub_Mock_C, sub_C_B, sub_B_A: TSignal.TSubscription;
begin
latchA := TLatch.CreateLatch(1);
latchB := TLatch.CreateLatch(1);
@@ -325,9 +325,9 @@ end;
procedure TTestMycLatch.TestChaining_B_NeedsMultipleNotifiesFromA_WithSubscriberOnB;
var
latchA, latchB: IMycLatch;
latchA, latchB: TLatch.ILatch;
mockSubForB: TMockSubscriber;
sub_Mock_B, sub_B_A: TSubscription;
sub_Mock_B, sub_B_A: TSignal.TSubscription;
begin
// LatchA needs 2 notifies to become set. LatchB needs 1 notify to become set.
// LatchB subscribes to LatchA. So LatchB will be notified once LatchA becomes set.
@@ -354,9 +354,9 @@ end;
procedure TTestMycLatch.TestInitiallySetLatch_NotifiesNewSubscriberImmediately;
var
latch: IMycLatch; // Will be TMycLatch.Null
latch: TLatch.ILatch; // Will be TMycLatch.Null
mockSub: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
latch := TLatch.CreateLatch(0); // Returns TMycLatch.Null which is set.
mockSub := TMockSubscriber.Create;
@@ -369,9 +369,9 @@ end;
procedure TTestMycLatch.TestUnsubscribe_SubscriberNotNotified;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSub: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
latch := TLatch.CreateLatch(1);
mockSub := TMockSubscriber.Create;
@@ -388,9 +388,9 @@ end;
procedure TTestMycLatch.TestMultipleTriggersNoSubscriber_StateCorrectForLaterSubscriber;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSub: TMockSubscriber;
subscription: TSubscription;
subscription: TSignal.TSubscription;
begin
latch := TLatch.CreateLatch(3);
@@ -411,9 +411,9 @@ end;
procedure TTestMycLatch.TestLatchSet_ClearsSubscribers_NoFurtherNotification;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSub1, mockSub2: TMockSubscriber;
subscription1, subscription2: TSubscription;
subscription1, subscription2: TSignal.TSubscription;
begin
latch := TLatch.CreateLatch(1);
mockSub1 := TMockSubscriber.Create;
@@ -439,7 +439,7 @@ end;
procedure TTestMycLatch.TestLatchSet_SubscriptionFinalize_IsSafePostUnadviseAll;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSub: TMockSubscriber;
// 'subscription' will be declared in an inner scope to control its finalization
begin
@@ -449,7 +449,7 @@ begin
var noException: Boolean := True;
try
begin // Inner block to control lifetime of 'subscription'
var subscription: TSubscription; // Local to this block
var subscription: TSignal.TSubscription; // Local to this block
subscription := latch.State.Subscribe(mockSub);
latch.Notify; // Sets the latch, mockSub is notified, UnadviseAll is called internally.
@@ -475,9 +475,9 @@ end;
procedure TTestMycLatch.TestLatchSet_NewSubscriberToSetLatch_NotifiedImmediatelyButNotPersistedForLatchNotify;
var
latch: IMycLatch;
latch: TLatch.ILatch;
mockSubInitial, mockSubNew: TMockSubscriber;
subscriptionInitial, subscriptionNew: TSubscription;
subscriptionInitial, subscriptionNew: TSignal.TSubscription;
begin
latch := TLatch.CreateLatch(1);
mockSubInitial := TMockSubscriber.Create;
+21 -21
View File
@@ -16,9 +16,9 @@ type
// Helper class to notify a latch when its Notify method is called.
TLatchNotifierSubscriber = class(TInterfacedObject, IMycSubscriber)
private
FGateLatch: IMycLatch;
FGateLatch: TLatch.ILatch;
public
constructor Create(AGateLatch: IMycLatch);
constructor Create(AGateLatch: TLatch.ILatch);
// IMycSubscriber
function Notify: Boolean; // Implements IMycSubscriber.Notify [cite: 46, 181, 299]
end;
@@ -54,7 +54,7 @@ implementation
{ TLatchNotifierSubscriber }
constructor TLatchNotifierSubscriber.Create(AGateLatch: IMycLatch);
constructor TLatchNotifierSubscriber.Create(AGateLatch: TLatch.ILatch);
begin
inherited Create;
FGateLatch := AGateLatch;
@@ -89,8 +89,8 @@ end;
procedure TTestMycGateFuncFuture.Test_BasicSuccess_WithImmediateInitState;
var
LFuture: IMycFuture<Integer>;
LInitStateAsState: IMycState; // Parameter for Create
LFuture: TFuture<Integer>.IFuture;
LInitStateAsState: TState.IState; // Parameter for Create
LResultValue: Integer;
const
CExpectedResult = 42;
@@ -109,19 +109,19 @@ begin
end
);
FTaskFactory.WaitFor(LFuture.Done); // Accesses IMycFuture.GetDone [cite: 110, 234, 352]
FTaskFactory.WaitFor(LFuture.Done); // Accesses IFuture.GetDone [cite: 110, 234, 352]
Assert.IsTrue(LFuture.Done.IsSet, 'Future should be marked as done.'); // Accesses IMycState.IsSet [cite: 57, 194, 312]
Assert.IsTrue(LFuture.Done.IsSet, 'Future should be marked as done.'); // Accesses TState.IState.IsSet [cite: 57, 194, 312]
Assert.AreEqual(1, FProcExecutionCount, 'AProc should have been executed once.');
LResultValue := LFuture.GetResult; // Accesses IMycFuture.GetResult
LResultValue := LFuture.GetResult; // Accesses IFuture.GetResult
Assert.AreEqual(CExpectedResult, LResultValue, 'GetResult returned an unexpected value.');
end;
procedure TTestMycGateFuncFuture.Test_ChainedExecution_WithDelayedInitState;
var
LFuture: IMycFuture<string>;
LInitLatch: IMycLatch;
LFuture: tFuture<string>.IFuture;
LInitLatch: TLatch.ILatch;
LResultValue: string;
const
CExpectedResult = 'ChainCompleted';
@@ -155,9 +155,9 @@ end;
procedure TTestMycGateFuncFuture.Test_ExceptionInProc_HandledAsPlanned;
var
LFuture: IMycFuture<Integer>;
LFuture: TFuture<Integer>.IFuture;
LLocalTaskFactory: IMycTaskFactory;
LInitStateAsState: IMycState;
LInitStateAsState: TState.IState;
LResultValue: Integer;
LExpectedExceptionRaisedByFactory: Boolean;
const
@@ -219,8 +219,8 @@ end;
procedure TTestMycGateFuncFuture.Test_GetResult_BeforeDone_RaisesException;
var
LFuture: IMycFuture<Integer>;
LInitLatch: IMycLatch;
LFuture: TFuture<Integer>.IFuture;
LInitLatch: TLatch.ILatch;
begin
LInitLatch := TLatch.CreateLatch(1);
@@ -248,12 +248,12 @@ end;
procedure TTestMycGateFuncFuture.Test_FanIn_OneFutureWaitsForMultipleOthers;
var
LPrerequisiteFuture1, LPrerequisiteFuture2: IMycFuture<Integer>;
LMainFuture: IMycFuture<string>;
LGateLatch: IMycLatch;
LPrerequisiteFuture1, LPrerequisiteFuture2: TFuture<Integer>.IFuture;
LMainFuture: TFuture<string>.IFuture;
LGateLatch: TLatch.ILatch;
LSub1, LSub2: IMycSubscriber; // To hold the TLatchNotifierSubscriber instances
LInitStateP1, LInitStateP2: IMycLatch;
Subscriptions: array[1..2] of TSubscription; // To manage subscriptions
LInitStateP1, LInitStateP2: TLatch.ILatch;
Subscriptions: array[1..2] of TSignal.TSubscription; // To manage subscriptions
const
CMainFutureResult = 'AllPrerequisitesCompleted';
begin
@@ -335,8 +335,8 @@ end;
procedure TTestMycGateFuncFuture.Test_FanOut_MultipleFuturesWaitForOneTrigger;
var
LTriggerLatch: IMycLatch;
LFutureA, LFutureB: IMycFuture<Integer>;
LTriggerLatch: TLatch.ILatch;
LFutureA, LFutureB: TFuture<Integer>.IFuture;
LFlagFutureARan, LFlagFutureBRan: Boolean;
begin
LFlagFutureARan := False;
+9 -9
View File
@@ -120,10 +120,10 @@ end;
procedure TTestFuture.TestConstructWithPresetGate;
var
fut: TFuture<Integer>;
presetGate: IMycState;
presetGate: TState.IState;
resultValue: Integer;
begin
presetGate := TState.Null; // State.Null is an IMycState that is always set [cite: 68]
presetGate := TState.Null; // State.Null is an TState.IState that is always set [cite: 68]
Assert.IsTrue(presetGate.IsSet, 'The preset gate (State.Null) should be initially set.'); // Static string [cite: 55]
// Construct a future with a gate that is already set
@@ -148,17 +148,17 @@ end;
procedure TTestFuture.TestConstructWithDelayedGate;
var
fut: TFuture<Integer>;
delayedGate: IMycLatch; // IMycLatch implements IMycState [cite: 58]
delayedGate: TLatch.ILatch; // TLatch.ILatch implements TState.IState [cite: 58]
resultValue: Integer;
begin
delayedGate := TLatch.CreateLatch(1); // Create a latch that requires one notification to be set [cite: 66]
Assert.IsNotNull(delayedGate, 'The delayed gate (IMycLatch) should not be nil.'); // Static string
Assert.IsNotNull(delayedGate, 'The delayed gate (TLatch.ILatch) should not be nil.'); // Static string
Assert.IsFalse(delayedGate.State.IsSet, 'The delayed gate should not be initially set.'); // Static string [cite: 59, 55]
// Construct a future with a gate that is not yet set
fut :=
TFuture<Integer>.Construct(
delayedGate.State, // Get the IMycState interface from the latch [cite: 147, 59]
delayedGate.State, // Get the TState.IState interface from the latch [cite: 147, 59]
function: Integer begin Result := 45; end
);
@@ -220,7 +220,7 @@ procedure TTestFuture.TestChainWithGate;
var
fut1: TFuture<Integer>;
fut2: TFuture<string>;
delayedGate: IMycLatch;
delayedGate: TLatch.ILatch;
resultValue: string;
begin
delayedGate := TLatch.CreateLatch(1); // [cite: 66]
@@ -341,7 +341,7 @@ const
var
Futures: TArray<TFuture<Integer>>;
doneStates: TArray<TState>;
combinedStateAll: IMycState;
combinedStateAll: TState.IState;
masterFuture: TFuture<Boolean>;
i: Integer;
begin
@@ -374,7 +374,7 @@ begin
end;
combinedStateAll := TState.All(doneStates); // [cite: 67]
Assert.IsNotNull(combinedStateAll, 'State.All should return a valid IMycState.'); // Static string
Assert.IsNotNull(combinedStateAll, 'State.All should return a valid TState.IState.'); // Static string
// Create a master future that waits on the combined State.All state
masterFuture :=
@@ -447,7 +447,7 @@ begin
end;
combinedStateAny := TState.Any(doneStates); // [cite: 68]
Assert.IsNotNull(combinedStateAny, 'State.Any should return a valid IMycState.'); // Static string
Assert.IsNotNull(combinedStateAny, 'State.Any should return a valid TState.IState.'); // Static string
// Create a master future that waits on the combined State.Any state
masterFuture :=
+8 -8
View File
@@ -70,7 +70,7 @@ end;
procedure TMycTaskFactoryTests.TestCreateThreadExecutesAndSignals;
var
executed: Boolean;
threadState: IMycState;
threadState: TState.IState;
procWrapper: TProc;
begin
executed := False;
@@ -86,7 +86,7 @@ end;
procedure TMycTaskFactoryTests.TestRunImmediateJob;
var
jobExecuted: Boolean;
jobCompletedLatch: IMycLatch;
jobCompletedLatch: TLatch.ILatch;
begin
jobExecuted := False;
jobCompletedLatch := TLatch.CreateLatch(1); // Changed from Signals.CreateLatch
@@ -107,7 +107,7 @@ end;
procedure TMycTaskFactoryTests.TestRunDelayedJobExecutesAfterAllNotifies;
var
jobExecuted: Boolean;
jobCompletedLatch: IMycLatch;
jobCompletedLatch: TLatch.ILatch;
subscriber: IMycSubscriber;
begin
jobExecuted := False;
@@ -134,7 +134,7 @@ end;
procedure TMycTaskFactoryTests.TestWaitForAlreadySetState;
var
alreadySetLatch: IMycLatch;
alreadySetLatch: TLatch.ILatch;
startTime, endTime: Cardinal;
begin
// TMycLatch.CreateLatch(0) will return TMycLatch.Null
@@ -152,7 +152,7 @@ procedure TMycTaskFactoryTests.TestThreadInfoMethods;
var
inMainThreadInJob: Boolean;
inWorkerThreadInJob: Boolean;
jobDoneLatch: IMycLatch;
jobDoneLatch: TLatch.ILatch;
begin
inMainThreadInJob := True;
inWorkerThreadInJob := False;
@@ -178,7 +178,7 @@ end;
procedure TMycTaskFactoryTests.TestExceptionPropagationFromJob;
var
jobDoneLatch: IMycLatch;
jobDoneLatch: TLatch.ILatch;
begin
jobDoneLatch := TLatch.CreateLatch(1);
@@ -222,8 +222,8 @@ end;
procedure TMycTaskFactoryTests.TestWaitForInWorkerThreadRaisesException;
var
exceptionCaughtInJob: Boolean;
jobDoneLatch: IMycLatch;
dummyStateToWaitFor: IMycState;
jobDoneLatch: TLatch.ILatch;
dummyStateToWaitFor: TState.IState;
begin
exceptionCaughtInJob := False;
jobDoneLatch := TLatch.CreateLatch(1);