unit Myc.Signals; interface uses System.SysUtils; type IMycSubscriber = interface // Interface for an entity that can be notified by a TState or TState. // Returns true if this subscriber expects further notifications from the source, false otherwise. function Notify: Boolean; end; IMycState = interface; TMycSubscription = record private FState: IMycState; FTag: Pointer; // Tag identifying this subscription within the notifier list. function GetState: IMycState; public constructor Create( const AState: IMycState; ATag: Pointer ); // Unsubscribes from the associated TState. procedure Unsubscribe; class operator Initialize( out Dest: TMycSubscription ); property State: IMycState read GetState; end; IMycState = interface // Represents a subscribable TState that can be queried. {$REGION 'property access'} function GetIsSet: Boolean; {$ENDREGION} // Subscribes a given subscriber to this TState. function Subscribe( Subscriber: IMycSubscriber ): TMycSubscription; procedure Unsubscribe( Tag: Pointer ); // 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; 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 ) // Provides access to the IMycState interface of the flag. function GetState: IMycState; property State: IMycState read GetState; // 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 ) // Provides access to the IMycState interface of the flag. function GetState: IMycState; // 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: IMycState read GetState; end; TDirty = record strict private class var FNull: IMycDirty; class constructor ClassCreate; public class function Construct: IMycDirty; static; class property Null: IMycDirty read FNull; end; implementation uses Myc.Core.Notifier, Myc.Core.Signals; { TMycSubscription } constructor TMycSubscription.Create( const AState: IMycState; ATag: TMycNotifyList.TTag ); begin Assert( Assigned( AState ) ); FState := AState; FTag := ATag; end; procedure TMycSubscription.Unsubscribe; begin FState.Unsubscribe( FTag ); FState := TState.Null; FTag := nil; end; function TMycSubscription.GetState: IMycState; begin Result := FState end; class operator TMycSubscription.Initialize( out Dest: TMycSubscription ); begin Dest.FState := TState.Null; Dest.FTag := nil; end; { TState } constructor TState.Create(const AState: IMycState); begin FState := AState; if not Assigned(FState) then FState := FNull; end; class constructor TState.ClassCreate; begin // 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 := TLatch.Construct( Length( States ) ); for var i := 0 to High( States ) do States[i].Subscribe( Latch ); Result := Latch.State; end; class function TState.Any( const States: TArray ): IMycState; var Latch: IMycLatch; begin if Length( States ) = 0 then begin Result := TState.Null; end else begin Latch := TLatch.Construct( 1 ); for var i := 0 to High( States ) do States[i].Subscribe( Latch ); Result := Latch.State; end; end; function TState.GetIsSet: Boolean; begin Result := FState.IsSet; end; function TState.Subscribe(Subscriber: IMycSubscriber): TMycSubscription; begin Result := FState.Subscribe(Subscriber); end; procedure TState.Unsubscribe(Tag: Pointer); begin FState.Unsubscribe(Tag); end; class operator TState.Implicit(const A: TState): IMycState; begin Result := A.FState; end; class operator TState.Implicit(const A: IMycState): TState; 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.