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.