diff --git a/Src/Myc.Core.Futures.pas b/Src/Myc.Core.Futures.pas index 2839612..8afab35 100644 --- a/Src/Myc.Core.Futures.pas +++ b/Src/Myc.Core.Futures.pas @@ -7,15 +7,19 @@ uses Myc.Signals, Myc.TaskManager, Myc.Futures; type - // Abstract base class for IMycFuture implementations. TMycFuture = class abstract( TInterfacedObject, IMycFuture ) protected function GetResult: T; virtual; abstract; function GetDone: IMycState; virtual; abstract; end; - // Concrete future implementation triggered by an initial state, executing a TFunc. - TMycInitStateFuncFuture = class( TMycFuture ) + TMycNullFuture = class( TMycFuture ) + protected + function GetResult: T; override; + function GetDone: IMycState; override; + end; + + TMycGateFuncFuture = class(TMycFuture) private FInit: TMycSubscription; FDone: IMycLatch; @@ -24,20 +28,31 @@ type function GetResult: T; override; function GetDone: IMycState; override; public - // Creates a future that executes AProc after AGate is set, using ATaskFactory. constructor Create( const ATaskManager: IMycTaskManager; const AGate: IMycState; AProc: TFunc ); destructor Destroy; override; end; implementation -{ TMycInitStateFuncFuture } +{ TMycNullFuture } -constructor TMycInitStateFuncFuture.Create( const ATaskManager: IMycTaskManager; const AGate: IMycState; AProc: TFunc ); +function TMycNullFuture.GetDone: IMycState; +begin + Result := TState.Null; +end; + +function TMycNullFuture.GetResult: T; +begin + Result := Default(T); +end; + +{ TMycGateFuncFuture } + +constructor TMycGateFuncFuture.Create(const ATaskManager: IMycTaskManager; const AGate: IMycState; AProc: TFunc); begin inherited Create; - FDone := TState.CreateLatch( 1 ); + FDone := TLatch.Construct( 1 ); // Subscribe the job execution to AGate. // The job will run when AGate notifies the subscriber returned by Run. @@ -58,7 +73,7 @@ begin end ); end; -destructor TMycInitStateFuncFuture.Destroy; +destructor TMycGateFuncFuture.Destroy; begin Assert( FDone.State.IsSet ); @@ -66,12 +81,12 @@ begin inherited Destroy; end; -function TMycInitStateFuncFuture.GetDone: IMycState; +function TMycGateFuncFuture.GetDone: IMycState; begin Result := FDone.State; end; -function TMycInitStateFuncFuture.GetResult: T; +function TMycGateFuncFuture.GetResult: T; begin Assert( FDone.State.IsSet, 'Result is not yet available.' ); Result := FResult; diff --git a/Src/Myc.Core.Signals.pas b/Src/Myc.Core.Signals.pas index d7b4b23..ef70575 100644 --- a/Src/Myc.Core.Signals.pas +++ b/Src/Myc.Core.Signals.pas @@ -9,18 +9,12 @@ uses type TMycState = class abstract( TInterfacedObject, IMycState ) - strict private - class var - FNull: IMycState; - class constructor ClassCreate; protected function GetIsSet: Boolean; virtual; abstract; public function Subscribe(Subscriber: IMycSubscriber): TMycSubscription; virtual; abstract; procedure Unsubscribe(Tag: TMycNotifyList.TTag); virtual; abstract; property IsSet: Boolean read GetIsSet; - // Provides access to the singleton null latch instance (always set). - class property Null: IMycState read FNull; end; // TMycNullState implements a "null object" pattern for IMycState. @@ -42,9 +36,6 @@ type TMycLatch = class(TMycState, IMycLatch) strict private FSubscribers: TMycNotifyList; // List of subscribers waiting for this latch to be set. - class var - FNull: IMycLatch; // Singleton instance of a latch that is always set. - class constructor ClassCreate; // Class constructor to initialize FNull. private [volatile] FCount: Integer; // The internal countdown value for the latch. function GetState: IMycState; // Implementation for IMycLatch.GetState and IMycFlag.GetState. @@ -56,9 +47,6 @@ type constructor Create(ACount: Integer); destructor Destroy; override; - // Factory method: creates a new countdown latch or returns the Null latch if Count <= 0. - class function CreateLatch(Count: Integer): IMycLatch; static; - // IMycSignal implementation function Subscribe(Subscriber: IMycSubscriber): TMycSubscription; override; procedure Unsubscribe(Tag: TMycNotifyList.TTag); override; @@ -67,9 +55,12 @@ type // Decrements the internal count. If the count reaches zero, // registered subscribers are notified, and the latch becomes permanently set. function Notify: Boolean; + end; - // Provides access to the singleton null latch instance (always set). - class property Null: IMycLatch read FNull; + TMycNullLatch = class( TInterfacedObject, IMycLatch ) + private + function GetState: IMycState; + function Notify: Boolean; end; // TMycDirty implements a resettable "dirty flag". @@ -91,7 +82,6 @@ type // Factory method to create a new TMycDirty instance. class function CreateDirty: IMycDirty; static; - // IMycSignal implementation function Subscribe(Subscriber: IMycSubscriber): TMycSubscription; override; procedure Unsubscribe(Tag: TMycNotifyList.TTag); override; @@ -103,17 +93,18 @@ type function Reset: Boolean; end; + TMycNullDirty = class( TInterfacedObject, IMycDirty ) + private + function GetState: IMycState; + function Notify: Boolean; + function Reset: Boolean; + end; + implementation uses System.SyncObjs; // For TInterlocked -class constructor TMycState.ClassCreate; -begin - // Create a singleton null latch instance that is initially (and always) set. - FNull := TMycNullState.Create(); // Calls the instance constructor -end; - { TMycNullState } constructor TMycNullState.Create; @@ -142,6 +133,18 @@ begin // Unsubscribing from a null state has no effect. end; +{ TMycNullLatch } + +function TMycNullLatch.GetState: IMycState; +begin + Result := TState.Null; +end; + +function TMycNullLatch.Notify: Boolean; +begin + Result := false; +end; + { TMycLatch } constructor TMycLatch.Create(ACount: Integer); @@ -152,27 +155,12 @@ begin FSubscribers.Create; end; -class constructor TMycLatch.ClassCreate; -begin - // Create a singleton null latch instance that is initially (and always) set. - FNull := TMycLatch.Create(0); // Calls the instance constructor -end; - destructor TMycLatch.Destroy; begin FSubscribers.Destroy; inherited; end; -class function TMycLatch.CreateLatch(Count: Integer): IMycLatch; -begin - // Factory method: returns a new latch or the shared Null latch if Count <= 0. - if Count > 0 then - Result := TMycLatch.Create(Count) // Instance constructor - else - Result := FNull; -end; - function TMycLatch.Notify: Boolean; var currentCountAfterDecrement: Integer; @@ -259,6 +247,23 @@ begin end; end; +{ TMycNullDirty } + +function TMycNullDirty.GetState: IMycState; +begin + Result := TState.Null; +end; + +function TMycNullDirty.Notify: Boolean; +begin + Result := false; +end; + +function TMycNullDirty.Reset: Boolean; +begin + Result := true; +end; + { TMycDirty } constructor TMycDirty.Create; diff --git a/Src/Myc.Core.Tasks.pas b/Src/Myc.Core.Tasks.pas index 04ce932..2cdc580 100644 --- a/Src/Myc.Core.Tasks.pas +++ b/Src/Myc.Core.Tasks.pas @@ -150,7 +150,7 @@ begin FWorkThreads[i].Free; if Assigned(FException) then - FException.Free; + FException.Free; FWorkGate.Free; FWorkStack.Clear; @@ -203,7 +203,7 @@ var capturedProc: TProc; begin // Create a latch that will be signaled when the proc finishes - res := TMycLatch.CreateLatch(1); // Changed to use direct static call on TMycLatch + res := TLatch.Construct(1); // Changed to use direct static call on TMycLatch capturedProc := Proc; // Capture Proc for the anonymous method CreateAnonymousThread('Thread', @@ -303,7 +303,7 @@ begin raise ETaskException.Create('Task factory terminated'); if not Assigned(Job) then - exit(TMycLatch.Null); // Already correct, uses direct static property on TMycLatch + exit(TLatch.Null); // Already correct, uses direct static property on TMycLatch // Create a pending job Result := TMycPendingJob.Create(Self, Job); diff --git a/Src/Myc.Futures.pas b/Src/Myc.Futures.pas index 42d38fb..9603aee 100644 --- a/Src/Myc.Futures.pas +++ b/Src/Myc.Futures.pas @@ -20,16 +20,20 @@ type end; TFuture = record - strict private + private FFuture: IMycFuture; function GetDone: IMycState; inline; function GetResult: T; inline; + class var + FNull: IMycFuture; + class constructor CreateClass; class destructor DestroyClass; public constructor Create(const AFuture: IMycFuture); + class operator Implicit(const A: IMycFuture): TFuture; overload; class operator Implicit(const A: TFuture): IMycFuture; overload; @@ -52,11 +56,14 @@ uses constructor TFuture.Create(const AFuture: IMycFuture); begin FFuture := AFuture; + if not Assigned(FFuture) then + FFuture := FNull; end; class constructor TFuture.CreateClass; begin TMycTaskFactory.AquireTaskManager; + FNull := TMycNullFuture.Create; end; class destructor TFuture.DestroyClass; @@ -77,12 +84,12 @@ end; class function TFuture.Construct(const Proc: TFunc): IMycFuture; begin - Result := TMycInitStateFuncFuture.Create( TaskManager, nil, Proc ); + Result := TMycGateFuncFuture.Create( TaskManager, nil, Proc ); end; class function TFuture.Construct(const Gate: IMycState; const Proc: TFunc): IMycFuture; begin - Result := TMycInitStateFuncFuture.Create( TaskManager, Gate, Proc ); + Result := TMycGateFuncFuture.Create( TaskManager, Gate, Proc ); end; function TFuture.GetDone: IMycState; diff --git a/Src/Myc.Signals.pas b/Src/Myc.Signals.pas index d2c669f..11cb0b9 100644 --- a/Src/Myc.Signals.pas +++ b/Src/Myc.Signals.pas @@ -24,7 +24,7 @@ type // Unsubscribes from the associated TState. procedure Unsubscribe; class operator Initialize( out Dest: TMycSubscription ); - property TState: IMycState read GetState; + property State: IMycState read GetState; end; IMycState = interface @@ -39,6 +39,30 @@ type property IsSet: Boolean read GetIsSet; end; + TState = record // helper for IMycState + strict private + class var + FNull: IMycState; + class constructor ClassCreate; + private + FState: IMycState; + function GetIsSet: Boolean; inline; + public + constructor Create(const AState: IMycState); + class operator Implicit(const A: IMycState): TState; overload; + class operator Implicit(const A: TState): IMycState; overload; + + class function All( const States: TArray ): IMycState; static; + class function Any( const States: TArray ): IMycState; static; + + class property Null: IMycState read FNull; + + function Subscribe(Subscriber: IMycSubscriber): TMycSubscription; inline; + procedure Unsubscribe(Tag: Pointer); inline; + + property IsSet: Boolean read GetIsSet; + end; + // IMycLatch is a specific type of flag that, once set, remains set (non-resettable). // It typically becomes set when an internal countdown reaches zero. IMycLatch = interface( IMycSubscriber ) @@ -48,6 +72,16 @@ type // Inherits IMycSubscriber and State property from IMycFlag. No new members. end; + TLatch = record + strict private + class var + FNull: IMycLatch; + class constructor ClassCreate; + public + class function Construct(Count: Integer): IMycLatch; static; + class property Null: IMycLatch read FNull; + end; + // IMycDirty represents a resettable flag, typically indicating if a State is "dirty" (requiring attention) or "clean". // It inherits from IMycFlag, meaning it has a State, can be notified, and subscribed to. IMycDirty = interface( IMycSubscriber ) @@ -59,19 +93,14 @@ type property State: IMycState read GetState; end; - TState = record - private - FState: IMycState; - class function GetNull: IMycState; static; + TDirty = record + strict private + class var + FNull: IMycDirty; + class constructor ClassCreate; public - constructor Create(const AState: IMycState); - class function CreateLatch( Count: Integer ): IMycLatch; static; - class function CreateDirty: IMycDirty; static; - class function All( const States: TArray ): IMycState; static; - class function Any( const States: TArray ): IMycState; static; - class operator Implicit(const A: IMycState): TState; overload; - class operator Implicit(const A: TState): IMycState; overload; - class property Null: IMycState read GetNull; + class function Construct: IMycDirty; static; + class property Null: IMycDirty read FNull; end; implementation @@ -92,7 +121,7 @@ end; procedure TMycSubscription.Unsubscribe; begin FState.Unsubscribe( FTag ); - FState := TMycState.Null; + FState := TState.Null; FTag := nil; end; @@ -103,7 +132,7 @@ end; class operator TMycSubscription.Initialize( out Dest: TMycSubscription ); begin - Dest.FState := TMycState.Null; + Dest.FState := TState.Null; Dest.FTag := nil; end; @@ -113,19 +142,20 @@ constructor TState.Create(const AState: IMycState); begin FState := AState; if not Assigned(FState) then - FState := TMycState.Null; + FState := FNull; end; -class function TState.CreateLatch( Count: Integer ): IMycLatch; +class constructor TState.ClassCreate; begin - Result := TMycLatch.CreateLatch( Count ); + // Create a singleton null latch instance that is initially (and always) set. + FNull := TMycNullState.Create(); // Calls the instance constructor end; class function TState.All( const States: TArray ): IMycState; var Latch: IMycLatch; begin - Latch := CreateLatch( Length( States ) ); + Latch := TLatch.Construct( Length( States ) ); for var i := 0 to High( States ) do States[i].Subscribe( Latch ); Result := Latch.State; @@ -141,21 +171,26 @@ begin end else begin - Latch := CreateLatch( 1 ); + Latch := TLatch.Construct( 1 ); for var i := 0 to High( States ) do States[i].Subscribe( Latch ); Result := Latch.State; end; end; -class function TState.CreateDirty: IMycDirty; +function TState.GetIsSet: Boolean; begin - Result := TMycDirty.CreateDirty; + Result := FState.IsSet; end; -class function TState.GetNull: IMycState; +function TState.Subscribe(Subscriber: IMycSubscriber): TMycSubscription; begin - Result := TMycState.Null; + Result := FState.Subscribe(Subscriber); +end; + +procedure TState.Unsubscribe(Tag: Pointer); +begin + FState.Unsubscribe(Tag); end; class operator TState.Implicit(const A: TState): IMycState; @@ -168,4 +203,32 @@ begin Result.Create( A ); end; +{ TLatch } + +class constructor TLatch.ClassCreate; +begin + // Create a singleton null latch instance that is initially (and always) set. + FNull := TMycNullLatch.Create; // Calls the instance constructor +end; + +class function TLatch.Construct(Count: Integer): IMycLatch; +begin + if Count > 0 then + Result := TMycLatch.Create( Count ) + else + Result := FNull; +end; + +{ TDirty } + +class constructor TDirty.ClassCreate; +begin + FNull := TMycNullDirty.Create; +end; + +class function TDirty.Construct: IMycDirty; +begin + Result := TMycDirty.Create; +end; + end. diff --git a/Test/MycTests.dpr b/Test/MycTests.dpr index 83bd83b..f7b3748 100644 --- a/Test/MycTests.dpr +++ b/Test/MycTests.dpr @@ -28,7 +28,11 @@ uses Myc.TaskManager in '..\Src\Myc.TaskManager.pas', Myc.Core.Futures in '..\Src\Myc.Core.Futures.pas', Myc.Core.Signals in '..\Src\Myc.Core.Signals.pas', - TestFutures in 'TestFutures.pas'; + TestFutures in 'TestFutures.pas', + Myc.Lazy in '..\Src\Myc.Lazy.pas', + Myc.Core.Lazy in '..\Src\Myc.Core.Lazy.pas', + Myc.Test.Core.Lazy in '..\Src\Myc.Test.Core.Lazy.pas', + Myc.Test.Lazy in '..\Src\Myc.Test.Lazy.pas'; { keep comment here to protect the following conditional from being removed by the IDE when adding a unit } {$IFNDEF TESTINSIGHT} diff --git a/Test/MycTests.dproj b/Test/MycTests.dproj index 029bcac..f58aab9 100644 --- a/Test/MycTests.dproj +++ b/Test/MycTests.dproj @@ -64,9 +64,9 @@ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= --exitbehavior:Pause TESTINSIGHT;$(DCC_Define) - TargetOutOfDate + vclwinx;fmx;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;FireDACCommonDriver;IndyProtocols;vclx;Skia.Package.RTL;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;bindcompfmx;inetdb;FireDACSqliteDriver;DbxClientDriver;Tee;soapmidas;vclactnband;TeeUI;fmxFireDAC;dbexpress;DBXMySQLDriver;VclSmp;inet;vcltouch;fmxase;dbrtl;Skia.Package.FMX;fmxdae;TeeDB;FireDACMSAccDriver;CustomIPTransport;vcldsnap;DBXInterBaseDriver;IndySystem;Skia.Package.VCL;vcldb;vclFireDAC;bindcomp;FireDACCommon;inetstn;IndyCore;RESTBackendComponents;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;adortl;vclimg;FireDACPgDriver;FireDAC;inetdbxpress;xmlrtl;tethering;bindcompvcl;dsnap;CloudService;fmxobj;bindcompvclsmp;FMXTee;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage) @@ -128,6 +128,10 @@ $(PostBuildEvent)]]> + + + + Base @@ -1149,35 +1153,35 @@ $(PostBuildEvent)]]> - + T:\DelphiIntfExtract\Win64\Debug\ExtractPascalInterfaces.exe -o T:\Myc\intf.txt T:\Myc\src False False - T:\DelphiIntfExtract\Win64\Debug\ExtractPascalInterfaces.exe -o T:\Myc\intf.txt T:\Myc\src + False - + T:\DelphiIntfExtract\Win64\Debug\ExtractPascalInterfaces.exe -o T:\Myc\intf.txt T:\Myc\src False False - T:\DelphiIntfExtract\Win64\Debug\ExtractPascalInterfaces.exe -o T:\Myc\intf.txt T:\Myc\src + False - + T:\DelphiIntfExtract\Win64\Debug\ExtractPascalInterfaces.exe -o T:\Myc\intf.txt T:\Myc\src False False - T:\DelphiIntfExtract\Win64\Debug\ExtractPascalInterfaces.exe -o T:\Myc\intf.txt T:\Myc\src + False - + T:\DelphiIntfExtract\Win64\Debug\ExtractPascalInterfaces.exe -o T:\Myc\intf.txt T:\Myc\src False False - T:\DelphiIntfExtract\Win64\Debug\ExtractPascalInterfaces.exe -o T:\Myc\intf.txt T:\Myc\src + False diff --git a/Test/TestCoreFutures.pas b/Test/TestCoreFutures.pas index 8906579..5c88a62 100644 --- a/Test/TestCoreFutures.pas +++ b/Test/TestCoreFutures.pas @@ -23,7 +23,7 @@ type [TestFixture] [IgnoreMemoryLeaks(true)] - TTestMycInitStateFuncFuture = class(TObject) + TTestMycGateFuncFuture = class(TObject) private FTaskFactory: IMycTaskFactory; FProcExecutionCount: Integer; // Counter for side effects of AProc @@ -67,16 +67,16 @@ begin Result := False; // This subscriber is typically one-shot for this purpose. end; -{ TTestMycInitStateFuncFuture } +{ TTestMycGateFuncFuture } -procedure TTestMycInitStateFuncFuture.Setup; +procedure TTestMycGateFuncFuture.Setup; begin FTaskFactory := TMycTaskFactory.Create; // Create a new task factory instance for each test [cite: 113, 235, 353] FProcExecutionCount := 0; FSharedCounter := 0; end; -procedure TTestMycInitStateFuncFuture.TearDown; +procedure TTestMycGateFuncFuture.TearDown; begin if Assigned(FTaskFactory) then begin @@ -85,7 +85,7 @@ begin end; end; -procedure TTestMycInitStateFuncFuture.Test_BasicSuccess_WithImmediateInitState; +procedure TTestMycGateFuncFuture.Test_BasicSuccess_WithImmediateInitState; var LFuture: IMycFuture; LInitStateAsState: IMycState; // Parameter for Create @@ -96,7 +96,7 @@ begin // Use TMycLatch.Null for an already set init state [cite: 71, 81, 206, 216, 324, 334] LInitStateAsState := TState.Null; - LFuture := TMycInitStateFuncFuture.Create(FTaskFactory, LInitStateAsState, + LFuture := TMycGateFuncFuture.Create(FTaskFactory, LInitStateAsState, function: Integer begin Inc(Self.FProcExecutionCount); @@ -112,7 +112,7 @@ begin Assert.AreEqual(CExpectedResult, LResultValue, 'GetResult returned an unexpected value.'); end; -procedure TTestMycInitStateFuncFuture.Test_ChainedExecution_WithDelayedInitState; +procedure TTestMycGateFuncFuture.Test_ChainedExecution_WithDelayedInitState; var LFuture: IMycFuture; LInitLatch: IMycLatch; @@ -120,9 +120,9 @@ var const CExpectedResult = 'ChainCompleted'; begin - LInitLatch := TState.CreateLatch(1); // Create an init state that is not yet set [cite: 77, 212, 330] + LInitLatch := TLatch.Construct(1); // Create an init state that is not yet set [cite: 77, 212, 330] - LFuture := TMycInitStateFuncFuture.Create(FTaskFactory, LInitLatch.State, + LFuture := TMycGateFuncFuture.Create(FTaskFactory, LInitLatch.State, function: string begin Inc(Self.FProcExecutionCount); @@ -143,7 +143,7 @@ begin Assert.AreEqual(CExpectedResult, LResultValue, 'GetResult returned an unexpected value after delayed init.'); end; -procedure TTestMycInitStateFuncFuture.Test_ExceptionInProc_HandledAsPlanned; +procedure TTestMycGateFuncFuture.Test_ExceptionInProc_HandledAsPlanned; var LFuture: IMycFuture; LLocalTaskFactory: IMycTaskFactory; @@ -157,7 +157,7 @@ begin LLocalTaskFactory := TMycTaskFactory.Create; LInitStateAsState := TState.Null; // Immediate execution - LFuture := TMycInitStateFuncFuture.Create(LLocalTaskFactory, LInitStateAsState, + LFuture := TMycGateFuncFuture.Create(LLocalTaskFactory, LInitStateAsState, function: Integer begin Inc(Self.FProcExecutionCount); @@ -203,14 +203,14 @@ begin end; end; -procedure TTestMycInitStateFuncFuture.Test_GetResult_BeforeDone_RaisesException; +procedure TTestMycGateFuncFuture.Test_GetResult_BeforeDone_RaisesException; var LFuture: IMycFuture; LInitLatch: IMycLatch; begin - LInitLatch := TState.CreateLatch(1); + LInitLatch := TLatch.Construct(1); - LFuture := TMycInitStateFuncFuture.Create(FTaskFactory, LInitLatch.State, + LFuture := TMycGateFuncFuture.Create(FTaskFactory, LInitLatch.State, function: Integer begin Inc(Self.FProcExecutionCount); @@ -236,7 +236,7 @@ begin end ); end; -procedure TTestMycInitStateFuncFuture.Test_FanIn_OneFutureWaitsForMultipleOthers; +procedure TTestMycGateFuncFuture.Test_FanIn_OneFutureWaitsForMultipleOthers; var LPrerequisiteFuture1, LPrerequisiteFuture2: IMycFuture; LMainFuture: IMycFuture; @@ -250,10 +250,10 @@ begin FProcExecutionCount := 0; // Reset for this test // Gate Latch: MainFuture waits for this latch, which needs 2 notifications. - LGateLatch := TState.CreateLatch(2); // [cite: 77, 212, 330] + LGateLatch := TLatch.Construct(2); // [cite: 77, 212, 330] // Create MainFuture, AInitState is the GateLatch's state. - LMainFuture := TMycInitStateFuncFuture.Create(FTaskFactory, LGateLatch.State, + LMainFuture := TMycGateFuncFuture.Create(FTaskFactory, LGateLatch.State, function: string begin Inc(Self.FProcExecutionCount, 10); // Indicate MainFuture's proc ran @@ -262,8 +262,8 @@ begin // Setup Prerequisite Futures // PrerequisiteFuture1 - LInitStateP1 := TState.CreateLatch(1); // Controllable init state for PF1 - LPrerequisiteFuture1 := TMycInitStateFuncFuture.Create(FTaskFactory, LInitStateP1.State, + LInitStateP1 := TLatch.Construct(1); // Controllable init state for PF1 + LPrerequisiteFuture1 := TMycGateFuncFuture.Create(FTaskFactory, LInitStateP1.State, function: Integer begin Inc(Self.FProcExecutionCount, 1); // PF1 ran @@ -273,8 +273,8 @@ begin Subscriptions[1] := LPrerequisiteFuture1.Done.Subscribe(LSub1); // Subscribe to PF1's completion [cite: 56, 188, 306] // PrerequisiteFuture2 - LInitStateP2 := TState.CreateLatch(1); // Controllable init state for PF2 - LPrerequisiteFuture2 := TMycInitStateFuncFuture.Create(FTaskFactory, LInitStateP2.State, + LInitStateP2 := TLatch.Construct(1); // Controllable init state for PF2 + LPrerequisiteFuture2 := TMycGateFuncFuture.Create(FTaskFactory, LInitStateP2.State, function: Integer begin Inc(Self.FProcExecutionCount, 1); // PF2 ran @@ -312,7 +312,7 @@ begin Subscriptions[2].Unsubscribe; // end; -procedure TTestMycInitStateFuncFuture.Test_FanOut_MultipleFuturesWaitForOneTrigger; +procedure TTestMycGateFuncFuture.Test_FanOut_MultipleFuturesWaitForOneTrigger; var LTriggerLatch: IMycLatch; LFutureA, LFutureB: IMycFuture; @@ -322,10 +322,10 @@ begin LFlagFutureBRan := False; Self.FSharedCounter := 0; // Reset shared counter for this test - LTriggerLatch := TState.CreateLatch(1); // Single trigger [cite: 77, 212, 330] + LTriggerLatch := TLatch.Construct(1); // Single trigger [cite: 77, 212, 330] // Future A - LFutureA := TMycInitStateFuncFuture.Create(FTaskFactory, LTriggerLatch.State, + LFutureA := TMycGateFuncFuture.Create(FTaskFactory, LTriggerLatch.State, function: Integer begin LFlagFutureARan := True; @@ -334,7 +334,7 @@ begin end); // Future B - LFutureB := TMycInitStateFuncFuture.Create(FTaskFactory, LTriggerLatch.State, + LFutureB := TMycGateFuncFuture.Create(FTaskFactory, LTriggerLatch.State, function: Integer begin LFlagFutureBRan := True; @@ -364,5 +364,5 @@ end; initialization // For DUnitX, attributes typically handle registration. - // If needed for older DUnit: RegisterTest(TTestMycInitStateFuncFuture.Suite); + // If needed for older DUnit: RegisterTest(TTestMycGateFuncFuture.Suite); end. diff --git a/Test/TestFutures.pas b/Test/TestFutures.pas index 069fd3a..e3f4d01 100644 --- a/Test/TestFutures.pas +++ b/Test/TestFutures.pas @@ -146,7 +146,7 @@ var delayedGate: IMycLatch; // IMycLatch implements IMycState [cite: 58] resultValue: Integer; begin - delayedGate := TState.CreateLatch( 1 ); // Create a latch that requires one notification to be set [cite: 66] + delayedGate := TLatch.Construct( 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.IsFalse( delayedGate.State.IsSet, 'The delayed gate should not be initially set.' ); // Static string [cite: 59, 55] @@ -217,7 +217,7 @@ var delayedGate: IMycLatch; resultValue: string; begin - delayedGate := TState.CreateLatch( 1 ); // [cite: 66] + delayedGate := TLatch.Construct( 1 ); // [cite: 66] Assert.IsFalse( delayedGate.State.IsSet, 'The delayed gate for chain test should not be initially set.' ); // Static string [cite: 59, 55] diff --git a/Test/TestNotifier_ChaosStress.pas b/Test/TestNotifier_ChaosStress.pas index c974228..af4e7f3 100644 --- a/Test/TestNotifier_ChaosStress.pas +++ b/Test/TestNotifier_ChaosStress.pas @@ -308,7 +308,7 @@ end; procedure TMycNotifierChaosStressTests.Test_MixedOperations_Stress; const NumThreads = 24; // Number of concurrent worker threads - IterationsPerThread = 5000; // Number of random operations per thread + IterationsPerThread = 2000; // Number of random operations per thread var threads: array of TStressWorkerThread; i: Integer; diff --git a/Test/TestNotifier_Threading.pas b/Test/TestNotifier_Threading.pas index 8103a64..3cce58c 100644 --- a/Test/TestNotifier_Threading.pas +++ b/Test/TestNotifier_Threading.pas @@ -198,7 +198,7 @@ end; procedure TMycNotifierThreadingTests.Test_ConcurrentAdvise_Then_UnadviseAll_Consistency; const NumThreads = 24; // Number of threads - ItemsPerThread = 2500; // Number of Advise operations per thread + ItemsPerThread = 250; // Number of Advise operations per thread var threads: array of TAdviseWorkerThread; i: Integer; diff --git a/Test/TestSignals_Dirty.pas b/Test/TestSignals_Dirty.pas index 8d4408e..b862632 100644 --- a/Test/TestSignals_Dirty.pas +++ b/Test/TestSignals_Dirty.pas @@ -105,7 +105,7 @@ end; function TTestMycDirtyFlag.CreateAndPrepareDirtyFlag(StartDirty: Boolean = True): IMycDirty; begin - Result := TState.CreateDirty; // Initially dirty + Result := TDirty.Construct; // Initially dirty if not StartDirty then Result.Reset; // Reset to make it clean Assert.AreEqual(StartDirty, Result.State.IsSet, 'CreateAndPrepareDirtyFlag initial state incorrect.'); @@ -127,7 +127,7 @@ procedure TTestMycDirtyFlag.TestCreate_InitialStateIsDirty; var dirtyFlag: IMycDirty; begin - dirtyFlag := TState.CreateDirty; + dirtyFlag := TDirty.Construct; Assert.IsTrue(dirtyFlag.State.IsSet, 'Newly created dirty flag should be IsSet (dirty).'); end; diff --git a/Test/TestSignals_Latch.pas b/Test/TestSignals_Latch.pas index fcff1e9..f8f19d3 100644 --- a/Test/TestSignals_Latch.pas +++ b/Test/TestSignals_Latch.pas @@ -124,7 +124,7 @@ var latch: IMycLatch; begin // TMycLatch.CreateLatch returns TMycLatch.Null if InitialCount <= 0 - latch := TState.CreateLatch(InitialCount); + latch := TLatch.Construct(InitialCount); Assert.AreEqual(ExpectedIsSet, latch.State.IsSet, 'Latch initial IsSet state mismatch for count ' + IntToStr(InitialCount) + '.'); end; @@ -133,7 +133,7 @@ var latch: IMycLatch; returnedValueFromNotify: Boolean; begin - latch := TState.CreateLatch(InitialCount); + latch := TLatch.Construct(InitialCount); returnedValueFromNotify := latch.Notify; // This is IMycLatch (as IMycSubscriber).Notify Assert.AreEqual(ExpectedReturn, returnedValueFromNotify, 'Unexpected return value from Latch.Notify call for initial count ' + IntToStr(InitialCount) + '.'); @@ -144,7 +144,7 @@ procedure TTestMycLatch.TestNotify_DecrementsCounter_StateChanges; var latch: IMycLatch; begin - latch := TState.CreateLatch(1); + latch := TLatch.Construct(1); Assert.IsFalse(latch.State.IsSet, 'Latch should initially not be set (Count=1).'); latch.Notify; // Count becomes 0 Assert.IsTrue(latch.State.IsSet, 'Latch should be set after Notify (Count became 0).'); @@ -154,7 +154,7 @@ procedure TTestMycLatch.TestNotify_MultipleNotifies_CounterBecomesNegativeAndSta var latch: IMycLatch; begin - latch := TState.CreateLatch(1); + latch := TLatch.Construct(1); latch.Notify; // Count becomes 0, IsSet = True Assert.IsTrue(latch.State.IsSet, 'Latch should be set after first Notify.'); latch.Notify; // Count becomes -1, IsSet should remain True @@ -169,7 +169,7 @@ var mockSub: TMockSubscriber; subscription: TMycSubscription; begin - latch := TState.CreateLatch(1); + latch := TLatch.Construct(1); mockSub := TMockSubscriber.Create; subscription := latch.State.Subscribe(mockSub); // Subscribing to the Latch's state @@ -188,7 +188,7 @@ var mockSub1, mockSub2, mockSub3: TMockSubscriber; sub1, sub2, sub3: TMycSubscription; // Keep subscriptions in scope begin - latch := TState.CreateLatch(1); + latch := TLatch.Construct(1); mockSub1 := TMockSubscriber.Create; mockSub2 := TMockSubscriber.Create; mockSub3 := TMockSubscriber.Create; @@ -210,7 +210,7 @@ var mockSub: TMockSubscriber; subscription: TMycSubscription; begin - latch := TState.CreateLatch(2); // Count = 2 + latch := TLatch.Construct(2); // Count = 2 mockSub := TMockSubscriber.Create; subscription := latch.State.Subscribe(mockSub); @@ -225,7 +225,7 @@ var mockSub: TMockSubscriber; subscription: TMycSubscription; begin - latch := TState.CreateLatch(1); + latch := TLatch.Construct(1); mockSub := TMockSubscriber.Create; subscription := latch.State.Subscribe(mockSub); @@ -242,7 +242,7 @@ var mockSub1, mockSub2: TMockSubscriber; subscription1, subscription2: TMycSubscription; begin - latch := TState.CreateLatch(1); // Create with count 1 + latch := TLatch.Construct(1); // Create with count 1 mockSub1 := TMockSubscriber.Create; subscription1 := latch.State.Subscribe(mockSub1); // Subscribe before set @@ -270,8 +270,8 @@ var mockSubForB: TMockSubscriber; subHandle_B_listens_A, subHandle_Mock_listens_B: TMycSubscription; begin - latchA := TState.CreateLatch(1); - latchB := TState.CreateLatch(1); // latchB is an IMycSubscriber + latchA := TLatch.Construct(1); + latchB := TLatch.Construct(1); // latchB is an IMycSubscriber mockSubForB := TMockSubscriber.Create; subHandle_Mock_listens_B := latchB.State.Subscribe(mockSubForB); @@ -295,9 +295,9 @@ var mockSubForC: TMockSubscriber; sub_Mock_C, sub_C_B, sub_B_A: TMycSubscription; begin - latchA := TState.CreateLatch(1); - latchB := TState.CreateLatch(1); - latchC := TState.CreateLatch(1); + latchA := TLatch.Construct(1); + latchB := TLatch.Construct(1); + latchC := TLatch.Construct(1); mockSubForC := TMockSubscriber.Create; sub_Mock_C := latchC.State.Subscribe(mockSubForC); @@ -320,8 +320,8 @@ var 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. - latchA := TState.CreateLatch(2); - latchB := TState.CreateLatch(1); + latchA := TLatch.Construct(2); + latchB := TLatch.Construct(1); mockSubForB := TMockSubscriber.Create; sub_Mock_B := latchB.State.Subscribe(mockSubForB); @@ -347,7 +347,7 @@ var mockSub: TMockSubscriber; subscription: TMycSubscription; begin - latch := TState.CreateLatch(0); // Returns TMycLatch.Null which is set. + latch := TLatch.Construct(0); // Returns TMycLatch.Null which is set. mockSub := TMockSubscriber.Create; subscription := latch.State.Subscribe(mockSub); // TMycLatch.Subscribe notifies immediately if already set. @@ -362,7 +362,7 @@ var mockSub: TMockSubscriber; subscription: TMycSubscription; begin - latch := TState.CreateLatch(1); + latch := TLatch.Construct(1); mockSub := TMockSubscriber.Create; subscription := latch.State.Subscribe(mockSub); @@ -381,7 +381,7 @@ var mockSub: TMockSubscriber; subscription: TMycSubscription; begin - latch := TState.CreateLatch(3); + latch := TLatch.Construct(3); latch.Notify; // Count -> 2 latch.Notify; // Count -> 1 @@ -404,7 +404,7 @@ var mockSub1, mockSub2: TMockSubscriber; subscription1, subscription2: TMycSubscription; begin - latch := TState.CreateLatch(1); + latch := TLatch.Construct(1); mockSub1 := TMockSubscriber.Create; mockSub2 := TMockSubscriber.Create; @@ -432,7 +432,7 @@ var mockSub: TMockSubscriber; // 'subscription' will be declared in an inner scope to control its finalization begin - latch := TState.CreateLatch(1); + latch := TLatch.Construct(1); mockSub := TMockSubscriber.Create; var noException: Boolean := True; @@ -468,7 +468,7 @@ var mockSubInitial, mockSubNew: TMockSubscriber; subscriptionInitial, subscriptionNew: TMycSubscription; begin - latch := TState.CreateLatch(1); + latch := TLatch.Construct(1); mockSubInitial := TMockSubscriber.Create; subscriptionInitial := latch.State.Subscribe(mockSubInitial); diff --git a/Test/TestTasks.pas b/Test/TestTasks.pas index e9f057d..6612f5e 100644 --- a/Test/TestTasks.pas +++ b/Test/TestTasks.pas @@ -91,7 +91,7 @@ var jobCompletedLatch: IMycLatch; begin jobExecuted := False; - jobCompletedLatch := TMycLatch.CreateLatch(1); // Changed from Signals.CreateLatch + jobCompletedLatch := TLatch.Construct(1); // Changed from Signals.CreateLatch FFactory.Run( procedure @@ -111,7 +111,7 @@ var subscriber: IMycSubscriber; begin jobExecuted := False; - jobCompletedLatch := TMycLatch.CreateLatch(1); // Changed from Signals.CreateLatch + jobCompletedLatch := TLatch.Construct(1); // Changed from Signals.CreateLatch subscriber := FFactory.Run( procedure @@ -122,7 +122,7 @@ begin Assert.IsNotNull(subscriber, 'Run should return a subscriber for delayed job'); // Use TMycLatch.Null for comparison - Assert.AreNotEqual(TMycLatch.Null, subscriber, 'Subscriber should not be TMycLatch.Null for StartCount > 0'); + Assert.AreNotEqual(TLatch.Null, subscriber, 'Subscriber should not be TMycLatch.Null for StartCount > 0'); subscriber.Notify; @@ -137,7 +137,7 @@ var startTime, endTime: Cardinal; begin // TMycLatch.CreateLatch(0) will return TMycLatch.Null - alreadySetLatch := TMycLatch.CreateLatch(0); // Changed from Signals.CreateLatch + alreadySetLatch := TLatch.Construct(0); // Changed from Signals.CreateLatch Assert.IsTrue(alreadySetLatch.State.IsSet, 'Latch should be initially set'); startTime := TThread.GetTickCount; @@ -155,7 +155,7 @@ var begin inMainThreadInJob := True; inWorkerThreadInJob := False; - jobDoneLatch := TMycLatch.CreateLatch(1); // Changed from Signals.CreateLatch + jobDoneLatch := TLatch.Construct(1); // Changed from Signals.CreateLatch Assert.IsTrue(FFactory.InMainThread, 'Test method itself should be in main thread'); Assert.IsFalse(FFactory.InWorkerThread, 'Test method itself should not be in a factory worker thread'); @@ -178,7 +178,7 @@ procedure TMycTaskFactoryTests.TestExceptionPropagationFromJob; var jobDoneLatch: IMycLatch; begin - jobDoneLatch := TMycLatch.CreateLatch(1); + jobDoneLatch := TLatch.Construct(1); FFactory.EnqueueJob( procedure @@ -229,8 +229,8 @@ var dummyStateToWaitFor: IMycState; begin exceptionCaughtInJob := False; - jobDoneLatch := TMycLatch.CreateLatch(1); - dummyStateToWaitFor := TMycLatch.CreateLatch(1).State; + jobDoneLatch := TLatch.Construct(1); + dummyStateToWaitFor := TLatch.Construct(1).State; FFactory.EnqueueJob( procedure