unit Myc.Signals; interface uses System.SysUtils; type TSignal = record type ISubscriber = interface function Notify: Boolean; end; TSubscriptionTag = Pointer; ISignal = interface function Subscribe(Subscriber: ISubscriber): TSubscriptionTag; procedure Unsubscribe(Tag: TSubscriptionTag); end; TSubscription = record private FSignal: ISignal; FTag: TSubscriptionTag; constructor Create(const ASignal: ISignal; ATag: TSubscriptionTag); public // Unsubscribes from the associated signal procedure Unsubscribe; end; {$REGION 'private'} strict private class var FNull: ISignal; class constructor ClassCreate; private FSignal: ISignal; {$ENDREGION} public constructor Create(const ASignal: ISignal); class operator Initialize(out Dest: TSignal); class operator Implicit(const A: ISignal): TSignal; overload; class operator Implicit(const A: TSignal): ISignal; overload; function Subscribe(Subscriber: ISubscriber): TSubscription; inline; class property Null: ISignal read FNull; end; TState = record type IState = interface {$REGION 'property access'} function GetIsSet: Boolean; function GetSignal: TSignal; {$ENDREGION} // IsSet is true if the TState has been reached or the condition is met. property IsSet: Boolean read GetIsSet; property Signal: TSignal read GetSignal; end; {$REGION 'private'} strict private class var FNull: IState; class constructor ClassCreate; private FState: IState; function GetIsSet: Boolean; inline; function GetSignal: TSignal; inline; {$ENDREGION} public constructor Create(const AState: IState); class operator Initialize(out Dest: TState); class operator Implicit(const A: IState): TState; overload; class operator Implicit(const A: TState): IState; overload; class operator Add(const A, B: TState): TState; class function All(const States: TArray): TState; static; class function Any(const States: TArray; Count: Integer = 1): TState; static; class property Null: IState read FNull; property IsSet: Boolean read GetIsSet; property Signal: TSignal read GetSignal; end; // An event is a stateless signal that forwards all notifications to the subscribers. TEvent = record type IEvent = interface(TSignal.ISubscriber) {$REGION 'property access'} function GetSignal: TSignal; {$ENDREGION} property Signal: TSignal read GetSignal; end; {$REGION 'private'} strict private class var FNull: IEvent; class constructor ClassCreate; private FEvent: IEvent; function GetSignal: TSignal; inline; {$ENDREGION} public constructor Create(const AEvent: IEvent); class operator Initialize(out Dest: TEvent); class operator Implicit(const A: IEvent): TEvent; overload; class operator Implicit(const A: TEvent): IEvent; overload; class function CreateEvent: TEvent; static; class function CreateRouter(const Signal: TSignal): TEvent; static; class property Null: IEvent read FNull; function Notify: Boolean; inline; property Signal: TSignal read GetSignal; end; // A latch is a specific type of event that, once set, remains set (non-resettable). // It becomes set when the internal countdown reaches zero. TLatch = record type ILatch = interface(TSignal.ISubscriber) {$REGION 'property access'} function GetState: TState; {$ENDREGION} property State: TState read GetState; end; {$REGION 'private'} strict private class var FNull: ILatch; [volatile] FQueueLock: Integer; class constructor ClassCreate; private FLatch: ILatch; function GetState: TState.IState; inline; {$ENDREGION} public constructor Create(const ALatch: ILatch); class operator Initialize(out Dest: TLatch); class operator Implicit(const A: ILatch): TLatch; overload; class operator Implicit(const A: TLatch): ILatch; overload; class function CreateLatch(Count: Integer): ILatch; static; class function Enqueue1(var Gate: TLatch): TState; overload; static; deprecated; experimental; function Enqueue: TState; overload; class property Null: ILatch read FNull; function Notify: Boolean; inline; property State: TState.IState read GetState; end; // TFlag represents a resettable flag, typically indicating if a State is "dirty" (requiring attention) or "clean". TFlag = record type IFlag = interface(TSignal.ISubscriber) {$REGION 'property access'} function GetState: TState; {$ENDREGION} function Reset: Boolean; property State: TState read GetState; end; {$REGION 'private'} strict private class var FNull: IFlag; class constructor ClassCreate; private FFlag: IFlag; function GetState: TState; inline; {$ENDREGION} public constructor Create(const AFlag: IFlag); class operator Initialize(out Dest: TFlag); class operator Implicit(const A: IFlag): TFlag; overload; class operator Implicit(const A: TFlag): IFlag; overload; class function CreateFlag(Init: Boolean = false): TFlag; static; class function CreateObserver(const Signal: TSignal): TFlag; static; class property Null: IFlag read FNull; function Notify: Boolean; inline; function Reset: Boolean; property State: TState read GetState; end; implementation uses Myc.Core.Notifier, Myc.Core.Signals; { TSignal.TSubscription } constructor TSignal.TSubscription.Create(const ASignal: ISignal; ATag: TSubscriptionTag); begin FSignal := ASignal; FTag := ATag; Assert(Assigned(FSignal)); end; procedure TSignal.TSubscription.Unsubscribe; begin if Assigned(FSignal) then begin FSignal.Unsubscribe(FTag); FTag := nil; FSignal := TSignal.Null; end; FTag := nil; end; { TState } constructor TState.Create(const AState: IState); begin FState := AState; if not Assigned(FState) then FState := Null; 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; begin Result := Any(States, Length(States)); end; class function TState.Any(const States: TArray; Count: Integer = 1): TState; var Latch: TLatch.ILatch; begin if (States = nil) or (Count <= 0) then exit(TState.Null); Latch := TLatch.CreateLatch(Count); for var state in States do state.Signal.Subscribe(Latch); Result := Latch.State; end; function TState.GetSignal: TSignal; begin Result := FState.Signal; end; function TState.GetIsSet: Boolean; begin Result := FState.IsSet; end; class operator TState.Add(const A, B: TState): TState; begin Result := All([A, B]); end; class operator TState.Implicit(const A: TState): IState; begin Result := A.FState; end; class operator TState.Implicit(const A: IState): TState; begin Result.Create(A); end; class operator TState.Initialize(out Dest: TState); begin Dest.FState := FNull; end; { TEvent } constructor TEvent.Create(const AEvent: IEvent); begin FEvent := AEvent; if not Assigned(FEvent) then FEvent := Null; end; class constructor TEvent.ClassCreate; begin FNull := TMycNullEvent.Create(); end; class function TEvent.CreateEvent: TEvent; begin Result := TMycEvent.Create; end; class function TEvent.CreateRouter(const Signal: TSignal): TEvent; begin Result := TMycRouterEvent.Create(Signal); end; function TEvent.GetSignal: TSignal; begin Result := FEvent.Signal; end; function TEvent.Notify: Boolean; begin Result := FEvent.Notify; end; class operator TEvent.Implicit(const A: TEvent): IEvent; begin Result := A.FEvent; end; class operator TEvent.Implicit(const A: IEvent): 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 AFlag: IFlag); begin FFlag := AFlag; if not Assigned(FFlag) then FFlag := Null; end; class function TFlag.CreateFlag(Init: Boolean = false): TFlag; begin Result := TMycFlag.Create(Init); end; class function TFlag.CreateObserver(const Signal: TSignal): TFlag; begin Result.Create(TMycObserverFlag.Create(Signal)); end; function TFlag.GetState: TState; begin Result := FFlag.State; end; function TFlag.Notify: Boolean; begin Result := FFlag.Notify; end; function TFlag.Reset: Boolean; begin Result := FFlag.Reset; end; class operator TFlag.Implicit(const A: IFlag): TFlag; begin Result.Create(A); end; class operator TFlag.Implicit(const A: TFlag): IFlag; begin Result := A.FFlag; end; class operator TFlag.Initialize(out Dest: TFlag); begin Dest.FFlag := FNull; end; { TLatch } class constructor TLatch.ClassCreate; begin // Create a singleton null latch instance that is initially (and always) set. FNull := TMycNullLatch.Create; FQueueLock := 0; end; constructor TLatch.Create(const ALatch: TLatch.ILatch); begin FLatch := ALatch; if not Assigned(FLatch) then FLatch := Null; end; class function TLatch.CreateLatch(Count: Integer): ILatch; begin if Count > 0 then Result := TMycLatch.Create(Count) else Result := FNull; end; class function TLatch.Enqueue1(var Gate: TLatch): TState; begin var Latch: TLatch.ILatch := TMycLatch.Create(1); repeat if FQueueLock > 0 then begin YieldProcessor; continue; end; until AtomicCmpExchange(FQueueLock, 1, 0) = 0; try Gate.State.Signal.Subscribe(Latch); Gate := Latch; Result := Latch.State; finally AtomicDecrement(FQueueLock); end; end; function TLatch.Enqueue: TState; begin var Latch: ILatch := TMycLatch.Create(1); repeat if FQueueLock > 0 then begin YieldProcessor; continue; end; until AtomicCmpExchange(FQueueLock, 1, 0) = 0; try FLatch.State.Signal.Subscribe(Latch); FLatch := Latch; Result := FLatch.State; finally AtomicDecrement(FQueueLock); end; end; function TLatch.GetState: TState.IState; begin Result := FLatch.State; end; function TLatch.Notify: Boolean; begin Result := FLatch.Notify; end; class operator TLatch.Implicit(const A: TLatch.ILatch): TLatch; begin Result.Create(A); end; class operator TLatch.Implicit(const A: TLatch): TLatch.ILatch; 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: ISignal); begin FSignal := ASignal; if not Assigned(FSignal) then FSignal := Null; end; function TSignal.Subscribe(Subscriber: ISubscriber): TSubscription; begin Result := TSubscription.Create(FSignal, FSignal.Subscribe(Subscriber)); end; class operator TSignal.Implicit(const A: TSignal): ISignal; begin Result := A.FSignal; end; class operator TSignal.Implicit(const A: ISignal): TSignal; begin Result.Create(A); end; class operator TSignal.Initialize(out Dest: TSignal); begin Dest.FSignal := FNull; end; end.