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 // 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 ): Pointer; 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 type TSubscription = record private FState: IMycState; FTag: Pointer; // Tag identifying this subscription within the notifier list. constructor Create( const AState: IMycState; ATag: Pointer ); public class operator Initialize( out Dest: TSubscription ); // Unsubscribes from the associated TState. procedure Unsubscribe; end; 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 ): TState; static; class function Any( const States: TArray ): TState; static; class property Null: IMycState read FNull; function Subscribe( Subscriber: IMycSubscriber ): TSubscription; 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: TState; property State: TState read GetState; end; TLatch = record strict private class var FNull: IMycLatch; class constructor ClassCreate; private FLatch: IMycLatch; function GetState: TState; inline; public constructor Create(const ALatch: IMycLatch); class operator Initialize(out Dest: TLatch); class operator Implicit(const A: IMycLatch): TLatch; overload; class operator Implicit(const A: TLatch): IMycLatch; overload; class function Construct(Count: Integer): TLatch; static; class function Enqueue(var Gate: TLatch; Count: Integer=1): TState; static; class property Null: IMycLatch read FNull; function Notify: Boolean; inline; property State: TState read GetState; 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: 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; 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; { TState.TSubscription } constructor TState.TSubscription.Create( const AState: IMycState; ATag: TMycNotifyList.TTag ); begin Assert( Assigned( AState ) ); FState := AState; FTag := ATag; end; procedure TState.TSubscription.Unsubscribe; begin FState.Unsubscribe( FTag ); FState := TState.Null; FTag := nil; end; class operator TState.TSubscription.Initialize( out Dest: TSubscription ); 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 ): TState; 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 ): TState; 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 ): TSubscription; begin Result := TSubscription.Create( FState, 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; { TLatch } constructor TLatch.Create(const ALatch: IMycLatch); begin FLatch := ALatch; if not Assigned( FLatch ) then FLatch := FNull; end; class function TLatch.Construct(Count: Integer): TLatch; begin if Count > 0 then Result := TMycLatch.Create( Count ) else Result := FNull; end; class function TLatch.Enqueue(var Gate: TLatch; Count: Integer=1): TState; begin var gateState := Gate.State; Gate := TMycLatch.Create( Count ); gateState.Subscribe( Gate ); exit( Gate.State ); end; function TLatch.GetState: TState; begin Result := FLatch.State; end; function TLatch.Notify: Boolean; begin Result := FLatch.Notify; end; class operator TLatch.Implicit(const A: IMycLatch): TLatch; begin Result.Create( A ); end; class operator TLatch.Implicit(const A: TLatch): IMycLatch; begin Result := A.FLatch; end; class operator TLatch.Initialize(out Dest: TLatch); begin Dest.FLatch := FNull; end; { TDirty } class constructor TDirty.ClassCreate; begin FNull := TMycNullDirty.Create; end; class function TDirty.Construct: IMycDirty; begin Result := TMycDirty.Create; end; end.