unit Myc.Signals; interface uses System.SysUtils; type IMycSubscriber = interface 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 strict private class var FNull: IMycSignal; class constructor ClassCreate; private FSignal: IMycSignal; public constructor Create(const ASignal: IMycSignal); class operator Initialize(out Dest: TSignal); class operator Implicit(const A: IMycSignal): TSignal; overload; class operator Implicit(const A: TSignal): IMycSignal; overload; function Subscribe(Subscriber: IMycSubscriber): TSubscription; inline; procedure Unsubscribe(Tag: Pointer); inline; class property Null: IMycSignal read FNull; end; IMycEvent = interface( IMycSignal ) procedure Trigger; end; TEvent = record strict private class var FNull: IMycEvent; class constructor ClassCreate; private FEvent: IMycEvent; public constructor Create(const AEvent: IMycEvent); class operator Initialize(out Dest: TEvent); class operator Implicit(const A: IMycEvent): TEvent; overload; class operator Implicit(const A: TEvent): IMycEvent; overload; class property Null: IMycEvent read FNull; function Subscribe(Subscriber: IMycSubscriber): 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 // 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 Initialize(out Dest: TState); 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 = interface(IMycSubscriber) 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; // 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; 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 = record strict private class var FNull: IMycFlag; class constructor ClassCreate; private FDirty: IMycFlag; function GetState: TState; inline; public constructor Create(const ADirty: IMycFlag); class operator Initialize(out Dest: TFlag); class operator Implicit(const A: IMycFlag): TFlag; overload; class operator Implicit(const A: TFlag): IMycFlag; overload; class function CreateFlag: IMycFlag; static; class property Null: IMycFlag read FNull; function Notify: Boolean; inline; function Reset: Boolean; property State: TState read GetState; end; implementation uses Myc.Core.Notifier, Myc.Core.Signals; { TState.TSubscription } constructor TSubscription.Create(const AState: IMycSignal; ATag: Pointer); begin Assert(Assigned(AState)); FState := AState; FTag := ATag; end; procedure TSubscription.Unsubscribe; begin FState.Unsubscribe(FTag); FState := TState.Null; FTag := nil; end; class operator 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.CreateLatch(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.CreateLatch(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; class operator TState.Initialize(out Dest: TState); begin Dest.FState := FNull; end; { TEvent } constructor TEvent.Create(const AEvent: IMycEvent); begin FEvent := AEvent; if not Assigned(FEvent) then FEvent := FNull; end; class constructor TEvent.ClassCreate; begin FNull := TMycNullEvent.Create(); end; function TEvent.Subscribe(Subscriber: IMycSubscriber): TSubscription; begin Result := TSubscription.Create(FEvent, FEvent.Subscribe(Subscriber)); end; procedure TEvent.Unsubscribe(Tag: Pointer); begin FEvent.Unsubscribe(Tag); end; class operator TEvent.Implicit(const A: TEvent): IMycEvent; begin Result := A.FEvent; end; class operator TEvent.Implicit(const A: IMycEvent): TEvent; begin Result.Create(A); end; class operator TEvent.Initialize(out Dest: TEvent); begin Dest.FEvent := FNull; end; { TFlag } class constructor TFlag.ClassCreate; begin FNull := TMycNullFlag.Create; end; constructor TFlag.Create(const ADirty: IMycFlag); begin FDirty := ADirty; if not Assigned(FDirty) then FDirty := FNull; end; class function TFlag.CreateFlag: IMycFlag; begin Result := TMycFlag.Create; end; function TFlag.GetState: TState; begin Result := FDirty.State; end; function TFlag.Notify: Boolean; begin Result := FDirty.Notify; end; function TFlag.Reset: Boolean; begin Result := FDirty.Reset; end; class operator TFlag.Implicit(const A: IMycFlag): TFlag; begin Result.Create(A); end; class operator TFlag.Implicit(const A: TFlag): IMycFlag; begin Result := A.FDirty; end; class operator TFlag.Initialize(out Dest: TFlag); begin Dest.FDirty := FNull; end; { TLatch } class constructor TLatch.ClassCreate; begin // Create a singleton null latch instance that is initially (and always) set. FNull := TMycNullLatch.Create; end; constructor TLatch.Create(const ALatch: IMycLatch); begin FLatch := ALatch; if not Assigned(FLatch) then FLatch := FNull; end; class function TLatch.CreateLatch(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; class constructor TSignal.ClassCreate; begin FNull := TMycNullSignal.Create(); end; { TSignal } constructor TSignal.Create(const ASignal: IMycSignal); begin FSignal := ASignal; if not Assigned(FSignal) then FSignal := FNull; end; function TSignal.Subscribe(Subscriber: IMycSubscriber): TSubscription; 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; begin Result := A.FSignal; end; class operator TSignal.Implicit(const A: IMycSignal): TSignal; begin Result.Create(A); end; class operator TSignal.Initialize(out Dest: TSignal); begin Dest.FSignal := FNull; end; end.