1010 lines
24 KiB
ObjectPascal
1010 lines
24 KiB
ObjectPascal
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
/// Mycelium Framework
|
|
/// ------------------
|
|
///
|
|
/// (c)2007-2019 Michael Schimmel
|
|
/// Ehrenhainstraße 40
|
|
/// 42329 Wuppertal / Germany
|
|
/// info@mycelium.net
|
|
///
|
|
/// All rights reserved.
|
|
///
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
unit Myc.Signals;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils, System.SyncObjs,
|
|
Myc.Atomic;
|
|
|
|
{.$define DEBUG_SIGNALS}
|
|
|
|
type
|
|
IMyc2Sink = interface
|
|
function Notify: Boolean;
|
|
end;
|
|
|
|
IMyc2Flag = interface( IMyc2Sink )
|
|
function Reset: Boolean;
|
|
end;
|
|
|
|
{$message hint 'Replace TSignalTag by IMycDeletable? '}
|
|
TSignalTag = NativeInt;
|
|
|
|
IMyc2Signal = interface
|
|
function Advise( const Sink: IMyc2Sink ): TSignalTag;
|
|
procedure Unadvise( Tag: TSignalTag );
|
|
end;
|
|
|
|
IMyc2Notifier = interface( IMyc2Sink )
|
|
{$region 'property access'}
|
|
function GetSignal: IMyc2Signal;
|
|
{$endregion}
|
|
property Signal: IMyc2Signal read GetSignal;
|
|
end;
|
|
|
|
IMyc2State = interface( IMyc2Signal )
|
|
{$region 'property access'}
|
|
function GetIsSet: Boolean;
|
|
{$endregion}
|
|
property IsSet: Boolean read GetIsSet;
|
|
end;
|
|
|
|
IMyc2Condition = interface( IMyc2Sink )
|
|
{$region 'property access'}
|
|
function GetState: IMyc2State;
|
|
{$endregion}
|
|
property State: IMyc2State read GetState;
|
|
end;
|
|
|
|
IMyc2Event = interface( IMyc2Condition )
|
|
function Reset: Boolean;
|
|
end;
|
|
|
|
TMycSinkList = record
|
|
type
|
|
PItem = ^TItem;
|
|
|
|
TItem = record
|
|
Next, Prev: PItem;
|
|
Sink: IMyc2Sink;
|
|
end;
|
|
|
|
strict private
|
|
FFirst: NativeUInt;
|
|
FList: PItem;
|
|
|
|
{$ifdef DEBUG_SIGNALS}
|
|
FCount: Integer;
|
|
{$endif}
|
|
class var
|
|
[volatile] FItems: TMycAtomicStack<TItem>;
|
|
[volatile]
|
|
FPendingCnt: Int64;
|
|
[volatile]
|
|
FUsedCnt: Int64;
|
|
|
|
class constructor CreateClass;
|
|
class destructor DestroyClass;
|
|
class function AllocItem: PItem; static; inline;
|
|
class procedure FreeItem( Item: PItem ); static; inline;
|
|
|
|
public
|
|
procedure Create;
|
|
procedure Destroy;
|
|
function Advise( const Sink: IMyc2Sink ): TSignalTag;
|
|
procedure Unadvise( Tag: TSignalTag );
|
|
procedure UnadviseAll;
|
|
procedure Finalize;
|
|
procedure Lock; inline;
|
|
procedure Release; inline;
|
|
function IsLocked: Boolean; inline;
|
|
function IsFinalized: Boolean; inline;
|
|
procedure Notify;
|
|
end;
|
|
|
|
TMyc2Signal = class( TInterfacedObject, IMyc2Signal, IMyc2State, IMyc2Sink )
|
|
private
|
|
FList: TMycSinkList;
|
|
|
|
class var
|
|
FNull: IMyc2Condition;
|
|
|
|
protected
|
|
class constructor CreateClass;
|
|
class destructor DestroyClass;
|
|
function GetIsSet: Boolean; virtual;
|
|
function DoNotify: Boolean; virtual;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
|
|
function Advise( const Sink: IMyc2Sink ): TSignalTag;
|
|
procedure Unadvise( Tag: TSignalTag );
|
|
function Notify: Boolean;
|
|
|
|
class function CreateAdvised( Count: Integer; const Getter: TFunc<Integer, IMyc2Signal> ): IMyc2Signal; overload;
|
|
class function IsStatic( const Signal: IMyc2Signal ): Boolean; overload; inline;
|
|
class function IsStatic( const Sink: IMyc2Sink ): Boolean; overload; inline;
|
|
|
|
class function MakeValid( const Signal: IMyc2Signal ): IMyc2Signal; overload; static; inline;
|
|
class function MakeValid( const Signals: array of IMyc2Signal ): TArray<IMyc2Signal>; overload; static;
|
|
|
|
class function CreateCounter( Count: Integer ): IMyc2Condition; static; inline;
|
|
class function CreateEvent( Init: Boolean ): IMyc2Event; static; inline;
|
|
class function CreateGroup( Count: Integer; const Getter: TFunc<Integer, IMyc2Signal> ): IMyc2Signal; overload;
|
|
class function CreateNotifier: IMyc2Notifier; static; inline;
|
|
|
|
class function CreateUnion( const Sigs: array of IMyc2Signal ): IMyc2Signal; static;
|
|
class function CreateConcat( const States: array of IMyc2State ): IMyc2State; static;
|
|
|
|
property IsSet: Boolean read GetIsSet;
|
|
class property Null: IMyc2Condition read FNull;
|
|
end;
|
|
|
|
TMyc2Notifier = class( TMyc2Signal, IMyc2Notifier )
|
|
protected
|
|
function GetSignal: IMyc2Signal;
|
|
end;
|
|
|
|
TMyc2Condition = class( TMyc2Signal, IMyc2Condition )
|
|
protected
|
|
function GetState: IMyc2State;
|
|
end;
|
|
|
|
TMyc2Event = class( TMyc2Condition, IMyc2Event )
|
|
private
|
|
[volatile]
|
|
FValue: Integer;
|
|
|
|
protected
|
|
function DoNotify: Boolean; override;
|
|
function GetIsSet: Boolean; override;
|
|
|
|
public
|
|
constructor Create( AInit: Boolean );
|
|
function Reset: Boolean; virtual;
|
|
property IsSet: Boolean read GetIsSet;
|
|
end;
|
|
|
|
TMyc2CountSignal = class sealed( TMyc2Condition )
|
|
private
|
|
[volatile]
|
|
FCount: Integer;
|
|
|
|
protected
|
|
function GetIsSet: Boolean; override;
|
|
function DoNotify: Boolean; override;
|
|
|
|
public
|
|
constructor Create( ACount: Integer );
|
|
end;
|
|
|
|
TMyc2SinkProc = class sealed( TInterfacedObject, IMyc2Sink )
|
|
private
|
|
FOnce: Boolean;
|
|
FProc: TProc;
|
|
|
|
function Notify: Boolean;
|
|
|
|
public
|
|
constructor Create( AOnce: Boolean; const AProc: TProc );
|
|
class function CreateSink( Once: Boolean; const Proc: TProc ): IMyc2Sink; static;
|
|
end;
|
|
|
|
TMyc2NullState = class sealed( TInterfacedObject, IMyc2Signal, IMyc2State )
|
|
protected
|
|
function Advise( const Sink: IMyc2Sink ): TSignalTag;
|
|
function GetIsSet: Boolean;
|
|
procedure Unadvise( Tag: TSignalTag );
|
|
procedure UnadviseAll;
|
|
end;
|
|
|
|
TMyc2NullSink = class sealed( TInterfacedObject, IMyc2Sink, IMyc2Condition )
|
|
private
|
|
FState: IMyc2State;
|
|
function GetState: IMyc2State;
|
|
|
|
protected
|
|
function Notify: Boolean;
|
|
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TMyc2Flag = class( TInterfacedObject, IMyc2Sink, IMyc2Flag )
|
|
private
|
|
FFlag: Integer;
|
|
function Notify: Boolean;
|
|
function Reset: Boolean;
|
|
|
|
public
|
|
constructor Create( AFlag: Boolean );
|
|
end;
|
|
|
|
TMycObserver<T: IMyc2Sink> = class
|
|
type
|
|
TNull = class( TInterfacedObject, IMycObserver<T> )
|
|
private
|
|
FSink: T;
|
|
function GetItem: T;
|
|
|
|
public
|
|
constructor Create( const ASink: T );
|
|
end;
|
|
|
|
TOne = class( TNull )
|
|
private
|
|
FSignal: IMyc2Signal;
|
|
FTag: TSignalTag;
|
|
|
|
public
|
|
constructor Create( const ASignal: IMyc2Signal; const ASink: T );
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TArr = class( TNull )
|
|
private
|
|
FSignals: TArray<IMyc2Signal>;
|
|
FTags: TArray<TSignalTag>;
|
|
|
|
public
|
|
constructor Create( const ASignals: TArray<IMyc2Signal>; const ASink: T );
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
public
|
|
class function CreateObserver( const Signals: array of IMyc2Signal; const Item: T ): IMycObserver<T>; static;
|
|
end;
|
|
|
|
TMycSignalUnion = class( TInterfacedObject, IMyc2Signal )
|
|
private
|
|
FNotifier: IMyc2Notifier;
|
|
FSignals: TArray<IMyc2Signal>;
|
|
FTags: TArray<TSignalTag>;
|
|
|
|
function Advise( const Sink: IMyc2Sink ): TSignalTag;
|
|
procedure Unadvise( Tag: TSignalTag );
|
|
|
|
public
|
|
constructor Create( const ASignals: array of IMyc2Signal );
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ TMyc2Signal }
|
|
|
|
constructor TMyc2Signal.Create;
|
|
begin
|
|
inherited Create;
|
|
FList.Create;
|
|
end;
|
|
|
|
destructor TMyc2Signal.Destroy;
|
|
begin
|
|
FList.Destroy;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
class function TMyc2Signal.CreateAdvised( Count: Integer; const Getter: TFunc<Integer, IMyc2Signal> ): IMyc2Signal;
|
|
var
|
|
i, j: Integer;
|
|
Sig: IMyc2Signal;
|
|
Res: IMyc2Condition;
|
|
begin
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
Sig := Getter( i );
|
|
if not IsStatic( Sig ) then
|
|
begin
|
|
Res := TMyc2Condition.Create;
|
|
|
|
Sig.Advise( Res );
|
|
|
|
for j := i + 1 to Count - 1 do
|
|
begin
|
|
Sig := Getter( j );
|
|
if not IsStatic( Sig ) then
|
|
Sig.Advise( Res );
|
|
end;
|
|
|
|
exit( Res.State );
|
|
end;
|
|
end;
|
|
|
|
exit( Null.State );
|
|
end;
|
|
|
|
class constructor TMyc2Signal.CreateClass;
|
|
begin
|
|
FNull := TMyc2NullSink.Create;
|
|
end;
|
|
|
|
class destructor TMyc2Signal.DestroyClass;
|
|
begin
|
|
FNull := nil;
|
|
end;
|
|
|
|
class function TMyc2Signal.IsStatic( const Signal: IMyc2Signal ): Boolean;
|
|
begin
|
|
Result := ( Signal = nil ) or ( Signal = FNull.State );
|
|
end;
|
|
|
|
class function TMyc2Signal.MakeValid( const Signal: IMyc2Signal ): IMyc2Signal;
|
|
begin
|
|
Result := Signal;
|
|
if Result = nil then
|
|
Result := Null.State;
|
|
end;
|
|
|
|
function TMyc2Signal.Advise( const Sink: IMyc2Sink ): TSignalTag;
|
|
begin
|
|
if IsStatic( Sink ) then
|
|
exit( 0 );
|
|
|
|
FList.Lock;
|
|
try
|
|
Result := FList.Advise( Sink );
|
|
|
|
if IsSet then
|
|
Sink.Notify;
|
|
finally
|
|
FList.Release;
|
|
end;
|
|
end;
|
|
|
|
class function TMyc2Signal.CreateConcat( const States: array of IMyc2State ): IMyc2State;
|
|
|
|
function CreatePair( const A, B: IMyc2State ): IMyc2State;
|
|
var
|
|
Counter: IMyc2Condition;
|
|
begin
|
|
if A=B then
|
|
begin
|
|
Result := A;
|
|
end
|
|
else if IsStatic( A ) then
|
|
begin
|
|
if IsStatic( B ) then
|
|
Result := Signals.Null.State
|
|
else
|
|
Result := B;
|
|
end
|
|
else if IsStatic( B ) then
|
|
begin
|
|
Result := A;
|
|
end
|
|
else
|
|
begin
|
|
Counter := CreateCounter( 2 );
|
|
A.Advise( Counter );
|
|
B.Advise( Counter );
|
|
Result := Counter.State;
|
|
end;
|
|
end;
|
|
|
|
function CreateSet( const States: array of IMyc2State ): IMyc2State;
|
|
var
|
|
Counter: IMyc2Condition;
|
|
i, n: Integer;
|
|
begin
|
|
n := 0;
|
|
for i := 0 to High( States ) do
|
|
if not IsStatic( States[i] ) then
|
|
inc( n );
|
|
|
|
Counter := CreateCounter( n );
|
|
for i := 0 to High( States ) do
|
|
if not IsStatic( States[i] ) then
|
|
States[i].Advise( Counter );
|
|
|
|
Result := Counter.State;
|
|
end;
|
|
|
|
begin
|
|
{TODO -oMS: Potentielles Speicherloch. Im Gegensatz zur Union werden hier die States nicht explizit un-advised. Andererseits
|
|
kann man eigentlich davon ausgehen, das States im Gegensatz zu einfachen Notifications irgendwann gesetzt und dann aufgeräumt
|
|
werden. Im Auge behalten! }
|
|
|
|
case Length( States ) of
|
|
0:
|
|
Result := Signals.Null.State;
|
|
1:
|
|
Result := States[0];
|
|
2:
|
|
Result := CreatePair( States[0], States[1] );
|
|
else
|
|
Result := CreateSet( States );
|
|
end;
|
|
end;
|
|
|
|
class function TMyc2Signal.CreateCounter( Count: Integer ): IMyc2Condition;
|
|
begin
|
|
Assert( Count >=0 );
|
|
|
|
if Count > 0 then
|
|
Result := TMyc2CountSignal.Create( Count )
|
|
else
|
|
Result := Null;
|
|
end;
|
|
|
|
class function TMyc2Signal.CreateEvent( Init: Boolean ): IMyc2Event;
|
|
begin
|
|
Result := TMyc2Event.Create( Init )
|
|
end;
|
|
|
|
class function TMyc2Signal.CreateGroup( Count: Integer; const Getter: TFunc<Integer, IMyc2Signal> ): IMyc2Signal;
|
|
var
|
|
i, n: Integer;
|
|
Sigs: TArray<IMyc2Signal>;
|
|
Res: IMyc2Condition;
|
|
begin
|
|
if Count = 0 then
|
|
exit( Null.State );
|
|
|
|
SetLength( Sigs, Count );
|
|
n := 0;
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
Sigs[n] := Getter( i );
|
|
if not IsStatic( Sigs[n] ) then
|
|
inc( n );
|
|
end;
|
|
|
|
if n = 0 then
|
|
exit( Null.State );
|
|
|
|
Res := CreateCounter( n );
|
|
for i := 0 to n - 1 do
|
|
Sigs[i].Advise( Res );
|
|
|
|
exit( Res.State );
|
|
end;
|
|
|
|
class function TMyc2Signal.CreateNotifier: IMyc2Notifier;
|
|
begin
|
|
Result := TMyc2Notifier.Create;
|
|
end;
|
|
|
|
class function TMyc2Signal.MakeValid( const Signals: array of IMyc2Signal ): TArray<IMyc2Signal>;
|
|
var
|
|
i, n: Integer;
|
|
begin
|
|
n := 0;
|
|
for i := 0 to High( Signals ) do
|
|
if not IsStatic( Signals[i] ) then
|
|
inc( n );
|
|
|
|
SetLength( Result, n );
|
|
n := 0;
|
|
for i := 0 to High( Signals ) do
|
|
if not IsStatic( Signals[i] ) then
|
|
begin
|
|
Result[n] := Signals[i];
|
|
inc( n );
|
|
end;
|
|
end;
|
|
|
|
class function TMyc2Signal.CreateUnion( const Sigs: array of IMyc2Signal ): IMyc2Signal;
|
|
begin
|
|
case Length( Sigs ) of
|
|
0:
|
|
Result := Signals.Null.State;
|
|
1:
|
|
Result := Sigs[0];
|
|
else
|
|
Result := TMycSignalUnion.Create( Sigs );
|
|
end;
|
|
end;
|
|
|
|
function TMyc2Signal.DoNotify: Boolean;
|
|
begin
|
|
FList.Notify;
|
|
exit( true );
|
|
end;
|
|
|
|
function TMyc2Signal.GetIsSet: Boolean;
|
|
begin
|
|
Result := true;
|
|
end;
|
|
|
|
class function TMyc2Signal.IsStatic( const Sink: IMyc2Sink ): Boolean;
|
|
begin
|
|
Result := ( Sink = nil ) or ( Sink = FNull );
|
|
end;
|
|
|
|
function TMyc2Signal.Notify: Boolean;
|
|
begin
|
|
FList.Lock;
|
|
try
|
|
Result := DoNotify;
|
|
if not Result then
|
|
FList.Finalize;
|
|
finally
|
|
FList.Release;
|
|
end;
|
|
end;
|
|
|
|
procedure TMyc2Signal.Unadvise( Tag: TSignalTag );
|
|
begin
|
|
if Tag=0 then
|
|
exit;
|
|
|
|
FList.Lock;
|
|
try
|
|
FList.Unadvise( Tag );
|
|
finally
|
|
FList.Release;
|
|
end;
|
|
end;
|
|
|
|
{ TMyc2Event }
|
|
|
|
constructor TMyc2Event.Create( AInit: Boolean );
|
|
begin
|
|
inherited Create;
|
|
if AInit then
|
|
inc( FValue );
|
|
end;
|
|
|
|
function TMyc2Event.GetIsSet: Boolean;
|
|
begin
|
|
Result := FValue <> 0;
|
|
end;
|
|
|
|
function TMyc2Event.DoNotify: Boolean;
|
|
begin
|
|
if TInterlocked.Exchange( FValue, 1 ) = 0 then
|
|
inherited;
|
|
exit( true );
|
|
end;
|
|
|
|
function TMyc2Event.Reset: Boolean;
|
|
begin
|
|
Result := TInterlocked.Exchange( FValue, 0 ) = 1;
|
|
end;
|
|
|
|
{ TMyc2CountownSignal }
|
|
|
|
constructor TMyc2CountSignal.Create( ACount: Integer );
|
|
begin
|
|
inherited Create;
|
|
FCount := ACount;
|
|
end;
|
|
|
|
function TMyc2CountSignal.GetIsSet: Boolean;
|
|
begin
|
|
Result := FCount <= 0;
|
|
end;
|
|
|
|
function TMyc2CountSignal.DoNotify: Boolean;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
n := TInterlocked.Decrement( FCount );
|
|
if n < -MaxInt div 2 then
|
|
TInterlocked.Increment( FCount );
|
|
|
|
if n = 0 then
|
|
inherited;
|
|
|
|
Result := n > 0;
|
|
end;
|
|
|
|
{ TMyc2SinkProc }
|
|
|
|
constructor TMyc2SinkProc.Create( AOnce: Boolean; const AProc: TProc );
|
|
begin
|
|
inherited Create;
|
|
FProc := AProc;
|
|
FOnce := AOnce;
|
|
end;
|
|
|
|
class function TMyc2SinkProc.CreateSink( Once: Boolean; const Proc: TProc ): IMyc2Sink;
|
|
begin
|
|
if Assigned( Proc ) then
|
|
Result := TMyc2SinkProc.Create( Once, Proc )
|
|
else
|
|
Result := TMyc2Signal.Null;
|
|
end;
|
|
|
|
function TMyc2SinkProc.Notify: Boolean;
|
|
begin
|
|
if Assigned( FProc ) then
|
|
begin
|
|
FProc( );
|
|
if FOnce then
|
|
FProc := nil;
|
|
end;
|
|
|
|
Result := Assigned( FProc );
|
|
end;
|
|
|
|
{ TMyc2NullState }
|
|
|
|
function TMyc2NullState.Advise( const Sink: IMyc2Sink ): TSignalTag;
|
|
begin
|
|
Sink._AddRef;
|
|
try
|
|
Sink.Notify;
|
|
finally
|
|
Sink._Release;
|
|
end;
|
|
exit( 0 );
|
|
end;
|
|
|
|
function TMyc2NullState.GetIsSet: Boolean;
|
|
begin
|
|
exit( true );
|
|
end;
|
|
|
|
procedure TMyc2NullState.Unadvise( Tag: TSignalTag );
|
|
begin
|
|
end;
|
|
|
|
procedure TMyc2NullState.UnadviseAll;
|
|
begin
|
|
|
|
end;
|
|
|
|
constructor TMyc2NullSink.Create;
|
|
begin
|
|
inherited Create;
|
|
FState := TMyc2NullState.Create;
|
|
end;
|
|
|
|
destructor TMyc2NullSink.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TMyc2NullSink.GetState: IMyc2State;
|
|
begin
|
|
Result := FState;
|
|
end;
|
|
|
|
function TMyc2NullSink.Notify: Boolean;
|
|
begin
|
|
Result := false;
|
|
end;
|
|
|
|
{ TMyc2Notifier }
|
|
|
|
function TMyc2Notifier.GetSignal: IMyc2Signal;
|
|
begin
|
|
Result := Self;
|
|
end;
|
|
|
|
{ TMyc2Condition }
|
|
|
|
function TMyc2Condition.GetState: IMyc2State;
|
|
begin
|
|
Result := Self;
|
|
end;
|
|
|
|
{ TMycSinkList }
|
|
|
|
class constructor TMycSinkList.CreateClass;
|
|
begin
|
|
FItems := TMycAtomicStack<TItem>.Create;
|
|
FPendingCnt := 0;
|
|
FUsedCnt := 0;
|
|
end;
|
|
|
|
class destructor TMycSinkList.DestroyClass;
|
|
begin
|
|
Assert( FUsedCnt = 0 );
|
|
Assert( FPendingCnt = 0 );
|
|
|
|
FItems.Free;
|
|
end;
|
|
|
|
procedure TMycSinkList.Create;
|
|
begin
|
|
FFirst := 1;
|
|
FList := nil;
|
|
{$ifdef DEBUG_SIGNALS}
|
|
FCount := 0;
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TMycSinkList.Destroy;
|
|
begin
|
|
// Because refcounting is thread-safe, this will always be entered once after all references
|
|
// to Self are dropped. No locking needed!
|
|
Assert( not IsLocked, 'Signal must not be locked!' );
|
|
FFirst := FFirst and not 3;
|
|
UnadviseAll;
|
|
end;
|
|
|
|
function TMycSinkList.Advise( const Sink: IMyc2Sink ): TSignalTag;
|
|
var
|
|
Item: PItem;
|
|
begin
|
|
Assert( IsLocked, 'Signal has to be locked' );
|
|
|
|
if IsFinalized then
|
|
exit( 0 );
|
|
|
|
{$ifdef DEBUG_SIGNALS}
|
|
inc( FCount );
|
|
Assert( FCount < 40000 );
|
|
{$endif}
|
|
if FFirst=0 then
|
|
begin
|
|
IMyc2Sink( FFirst ) := Sink;
|
|
exit( TSignalTag( Sink ) );
|
|
end;
|
|
|
|
Item := AllocItem;
|
|
Item.Sink := Sink;
|
|
Item.Prev := nil;
|
|
Item.Next := FList;
|
|
if Item.Next<>nil then
|
|
Item.Next.Prev := Item;
|
|
|
|
FList := Item;
|
|
|
|
exit( TSignalTag( Item ) );
|
|
end;
|
|
|
|
procedure TMycSinkList.Lock;
|
|
begin
|
|
repeat
|
|
if FFirst and 1 = 0 then
|
|
begin
|
|
YieldProcessor;
|
|
continue;
|
|
end;
|
|
until TInterlocked.BitTestAndClear( PInteger( @FFirst )^, 0 );
|
|
|
|
Assert( IsLocked, 'Locking failed' );
|
|
end;
|
|
|
|
procedure TMycSinkList.Notify;
|
|
var
|
|
Item, P: PItem;
|
|
begin
|
|
Assert( IsLocked, 'Signal has to be locked' );
|
|
|
|
if IsFinalized then
|
|
exit;
|
|
|
|
if FFirst<>0 then
|
|
if not IMyc2Sink( FFirst ).Notify then
|
|
IMyc2Sink( FFirst ) := nil;
|
|
|
|
Item := FList;
|
|
while Item<>nil do
|
|
begin
|
|
P := Item.Next;
|
|
if not Item.Sink.Notify then
|
|
Unadvise( TSignalTag( Item ) );
|
|
Item := P;
|
|
end;
|
|
end;
|
|
|
|
class function TMycSinkList.AllocItem: PItem;
|
|
begin
|
|
GetMem( Result, sizeof( TItem ) );
|
|
Pointer( Result.Sink ) := nil;
|
|
end;
|
|
|
|
procedure TMycSinkList.Finalize;
|
|
begin
|
|
Assert( IsLocked, 'Signal has to be locked' );
|
|
UnadviseAll;
|
|
FFirst := FFirst or 2;
|
|
end;
|
|
|
|
class procedure TMycSinkList.FreeItem( Item: PItem );
|
|
begin
|
|
FreeMem( Item, sizeof( TItem ) );
|
|
end;
|
|
|
|
function TMycSinkList.IsLocked: Boolean;
|
|
begin
|
|
Result := FFirst and 1 = 0;
|
|
end;
|
|
|
|
function TMycSinkList.IsFinalized: Boolean;
|
|
begin
|
|
Result := FFirst and 2 <> 0;
|
|
end;
|
|
|
|
procedure TMycSinkList.Release;
|
|
begin
|
|
Assert( IsLocked, 'Signal not locked' );
|
|
TInterlocked.Exchange( Pointer( FFirst ), Pointer( FFirst or 1 ) );
|
|
end;
|
|
|
|
procedure TMycSinkList.Unadvise( Tag: TSignalTag );
|
|
var
|
|
Item: PItem;
|
|
begin
|
|
Assert( IsLocked, 'Signal not locked' );
|
|
|
|
if IsFinalized then
|
|
exit;
|
|
|
|
{$ifdef DEBUG_SIGNALS}
|
|
dec( FCount );
|
|
{$endif}
|
|
if Tag = FFirst then
|
|
begin
|
|
IMyc2Sink( FFirst ) := nil;
|
|
exit;
|
|
end;
|
|
|
|
Item := PItem( Tag );
|
|
|
|
if Item = FList then
|
|
FList := Item.Next;
|
|
|
|
if Item.Prev<>nil then
|
|
Item.Prev.Next := Item.Next;
|
|
if Item.Next<>nil then
|
|
Item.Next.Prev := Item.Prev;
|
|
|
|
Item.Sink := nil;
|
|
|
|
FreeItem( Item );
|
|
end;
|
|
|
|
procedure TMycSinkList.UnadviseAll;
|
|
begin
|
|
Assert( IsLocked, 'Signal has to be locked' );
|
|
|
|
if IsFinalized then
|
|
exit;
|
|
|
|
IMyc2Sink( FFirst ) := nil;
|
|
while FList<>nil do
|
|
Unadvise( TSignalTag( FList ) );
|
|
end;
|
|
|
|
{ TMycObserver<T> }
|
|
|
|
class function TMycObserver<T>.CreateObserver( const Signals: array of IMyc2Signal; const Item: T ): IMycObserver<T>;
|
|
var
|
|
Arr: TArray<IMyc2Signal>;
|
|
begin
|
|
{$message hint 'Design-Fehler!! Der Null-Observer ist nicht statisch!!'}
|
|
if Length( Signals ) = 0 then
|
|
Result := TNull.Create( Item )
|
|
else if ( Length( Signals )=1 ) and not TMyc2Signal.IsStatic( Signals[0] ) then
|
|
Result := TOne.Create( Signals[0], Item )
|
|
else
|
|
begin
|
|
Arr := TMyc2Signal.MakeValid( Signals );
|
|
if Arr = nil then
|
|
Result := TNull.Create( Item )
|
|
else if Length( Arr )=1 then
|
|
Result := TOne.Create( Arr[0], Item )
|
|
else
|
|
Result := TArr.Create( Arr, Item );
|
|
end;
|
|
end;
|
|
|
|
{ TMycObserver<T>.TNull }
|
|
|
|
constructor TMycObserver<T>.TNull.Create( const ASink: T );
|
|
begin
|
|
inherited Create;
|
|
FSink := ASink;
|
|
end;
|
|
|
|
function TMycObserver<T>.TNull.GetItem: T;
|
|
begin
|
|
Result := FSink;
|
|
end;
|
|
|
|
{ TMycObserver<T>.TOne }
|
|
|
|
constructor TMycObserver<T>.TOne.Create( const ASignal: IMyc2Signal; const ASink: T );
|
|
begin
|
|
inherited Create( ASink );
|
|
FSignal := ASignal;
|
|
FTag := FSignal.Advise( FSink );
|
|
end;
|
|
|
|
destructor TMycObserver<T>.TOne.Destroy;
|
|
begin
|
|
FSignal.Unadvise( FTag );
|
|
inherited;
|
|
end;
|
|
|
|
{ TMycObserver<T>.TArr }
|
|
|
|
constructor TMycObserver<T>.TArr.Create( const ASignals: TArray<IMyc2Signal>; const ASink: T );
|
|
var
|
|
i: Integer;
|
|
begin
|
|
inherited Create( ASink );
|
|
FSignals := ASignals;
|
|
|
|
SetLength( FTags, Length( FSignals ) );
|
|
for i := 0 to High( FTags ) do
|
|
begin
|
|
if not Signals.IsStatic( FSignals[i] ) then
|
|
FTags[i] := FSignals[i].Advise( FSink )
|
|
else
|
|
FTags[i] := 0;
|
|
end;
|
|
end;
|
|
|
|
destructor TMycObserver<T>.TArr.Destroy;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := High( FSignals ) downto 0 do
|
|
if FTags[i]<>0 then
|
|
FSignals[i].Unadvise( FTags[i] );
|
|
inherited;
|
|
end;
|
|
|
|
{ TMycSignalUnion }
|
|
|
|
constructor TMycSignalUnion.Create( const ASignals: array of IMyc2Signal );
|
|
var
|
|
i: Integer;
|
|
begin
|
|
inherited Create;
|
|
|
|
FNotifier := Signals.CreateNotifier;
|
|
SetLength( FTags, Length( ASignals ) );
|
|
SetLength( FSignals, Length( ASignals ) );
|
|
for i := 0 to High( FSignals ) do
|
|
begin
|
|
FSignals[i] := ASignals[i];
|
|
FTags[i] := FSignals[i].Advise( FNotifier );
|
|
end;
|
|
end;
|
|
|
|
destructor TMycSignalUnion.Destroy;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := High( FTags ) downto 0 do
|
|
FSignals[i].Unadvise( FTags[i] );
|
|
|
|
inherited;
|
|
end;
|
|
|
|
function TMycSignalUnion.Advise( const Sink: IMyc2Sink ): TSignalTag;
|
|
begin
|
|
Result := FNotifier.Signal.Advise( Sink );
|
|
end;
|
|
|
|
procedure TMycSignalUnion.Unadvise( Tag: TSignalTag );
|
|
begin
|
|
FNotifier.Signal.Unadvise( Tag );
|
|
end;
|
|
|
|
{ TMyc2Flag }
|
|
|
|
constructor TMyc2Flag.Create( AFlag: Boolean );
|
|
begin
|
|
inherited Create;
|
|
FFlag := Integer( AFlag );
|
|
end;
|
|
|
|
function TMyc2Flag.Notify: Boolean;
|
|
begin
|
|
Result := TInterlocked.Exchange( FFlag, 1 ) = 0;
|
|
end;
|
|
|
|
function TMyc2Flag.Reset: Boolean;
|
|
begin
|
|
Result := TInterlocked.Exchange( FFlag, 0 ) = 1;
|
|
end;
|
|
|
|
end.
|