unit Myc.Signals; interface uses System.SysUtils, Myc.Core.Notifier; type IMycSemaphore = interface; TMycSignal = class; IMycSubscriber = interface function Notify: Boolean; end; TMycSubscription = record private FSignal: TMycSignal; FTag: TMycNotifyList.TTag; public procedure Setup(ASignal: TMycSignal; ATag: TMycNotifyList.TTag); inline; class operator Assign(var Dest: TMycSubscription; const [ref] Src: TMycSubscription); class operator Initialize(out Dest: TMycSubscription); class operator Finalize(var Dest: TMycSubscription); end; IMycSignal = interface function Subscribe(const Subscriber: IMycSubscriber): TMycSubscription; end; TMycSignal = class abstract( TInterfacedObject, IMycSignal ) strict private FSubscribers: TMycNotifyList; private procedure Unsubscribe(Tag: TMycNotifyList.TTag); protected procedure Lock; procedure Unlock; procedure Finalize; procedure Notify; public function Subscribe(const Subscriber: IMycSubscriber): TMycSubscription; property Subscribers: TMycNotifyList read FSubscribers; end; IMycState = interface( IMycSignal ) {$region 'property access'} function GetIsSet: Boolean; {$endregion} property IsSet: Boolean read GetIsSet; end; IMycSemaphore = interface(IMycSubscriber) {$region 'property access'} function GetState: IMycState; {$endregion} property State: IMycState read GetState; end; TMycSemaphore = class(TMycSignal, IMycSemaphore, IMycState) private [volatile] FCount: Integer; function GetState: IMycState; function GetIsSet: Boolean; protected function Notify: Boolean; public constructor Create(ACount: Integer); end; Signals = record class function CreateSemaphore(Count: Integer): IMycSemaphore; static; end; implementation uses System.SyncObjs; procedure TMycSignal.Finalize; begin FSubscribers.Finalize; end; procedure TMycSignal.Lock; begin FSubscribers.Lock; end; procedure TMycSignal.Notify; begin FSubscribers.Notify( function( Subscriber: IMycSubscriber ): Boolean begin Result := Subscriber.Notify; end ); end; function TMycSignal.Subscribe(const Subscriber: IMycSubscriber): TMycSubscription; begin if not Assigned(Subscriber) then exit; FSubscribers.Lock; try Result.Setup( Self, FSubscribers.Advise( Subscriber ) ); finally FSubscribers.Release; end; end; procedure TMycSignal.Unlock; begin end; procedure TMycSignal.Unsubscribe(Tag: TMycNotifyList.TTag); begin if Tag=nil then exit; FSubscribers.Lock; try FSubscribers.Unadvise( Tag ); finally FSubscribers.Release; end; end; procedure TMycSubscription.Setup(ASignal: TMycSignal; ATag: TMycNotifyList.TTag); begin FSignal := ASignal; FTag := ATag; end; class operator TMycSubscription.Assign(var Dest: TMycSubscription; const [ref] Src: TMycSubscription); begin Dest.Setup( Src.FSignal, Src.FTag ); end; class operator TMycSubscription.Initialize(out Dest: TMycSubscription); begin Dest.FTag := nil; end; class operator TMycSubscription.Finalize(var Dest: TMycSubscription); begin if Dest.FTag=nil then exit; Dest.FSignal.Unsubscribe( Dest.FTag ); end; constructor TMycSemaphore.Create(ACount: Integer); begin inherited Create; FCount := ACount; end; function TMycSemaphore.Notify: Boolean; begin Lock; try var n := TInterlocked.Decrement( FCount ); if n < -MaxInt div 2 then TInterlocked.Increment( FCount ); if n = 0 then Notify; Result := n > 0; if not Result then Finalize; finally Release; end; end; function TMycSemaphore.GetIsSet: Boolean; begin Result := FCount <= 0; end; function TMycSemaphore.GetState: IMycState; begin exit( Self ); end; { Signals } class function Signals.CreateSemaphore(Count: Integer): IMycSemaphore; begin Result := TMycSemaphore.Create( Count ); end; end.