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; IMycEvent = interface(IMycSubscriber) function GetState: TState; property State: TState read GetState; end; TEvent = record strict private class var FNull: IMycEvent; class constructor ClassCreate; private FLatch: IMycEvent; function GetState: TState; inline; public constructor Create(const ALatch: IMycEvent); class operator Initialize(out Dest: TEvent); class operator Implicit(const A: IMycEvent): TEvent; overload; class operator Implicit(const A: TEvent): IMycEvent; overload; // A notifier is an event that acts as a multi-cast event. Its state is always set and it forwards all notifications the all subscribers. class function CreateNotifier: TEvent; static; // 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): TEvent; static; class function Enqueue(var Gate: TEvent; Count: Integer = 1): TState; static; class property Null: IMycEvent 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 Implicit(const A: IMycFlag): TFlag; overload; class operator Implicit(const A: TFlag): IMycFlag; overload; class operator Initialize(out Dest: TFlag); 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 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: IMycEvent; begin Latch := TEvent.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: IMycEvent; begin if Length(States) = 0 then begin Result := TState.Null; end else begin Latch := TEvent.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; { TFlag } class constructor TFlag.ClassCreate; begin FNull := TMycNullFlag.Create; end; { TFlag } 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; { TEvent } class constructor TEvent.ClassCreate; begin // Create a singleton null latch instance that is initially (and always) set. FNull := TMycNullEvent.Create; end; { TEvent } constructor TEvent.Create(const ALatch: IMycEvent); begin FLatch := ALatch; if not Assigned(FLatch) then FLatch := FNull; end; class function TEvent.CreateLatch(Count: Integer): TEvent; begin if Count > 0 then Result := TMycLatch.Create(Count) else Result := FNull; end; class function TEvent.CreateNotifier: TEvent; begin Result := TMycNotifyEvent.Create; end; class function TEvent.Enqueue(var Gate: TEvent; Count: Integer = 1): TState; begin var gateState := Gate.State; Gate := TMycLatch.Create(Count); gateState.Subscribe(Gate); exit(Gate.State); end; function TEvent.GetState: TState; begin Result := FLatch.State; end; function TEvent.Notify: Boolean; begin Result := FLatch.Notify; end; class operator TEvent.Implicit(const A: IMycEvent): TEvent; begin Result.Create(A); end; class operator TEvent.Implicit(const A: TEvent): IMycEvent; begin Result := A.FLatch; end; class operator TEvent.Initialize(out Dest: TEvent); begin Dest.FLatch := FNull; end; end.