Notify list refactoring
This commit is contained in:
+99
-59
@@ -19,8 +19,15 @@ type
|
|||||||
PItem = ^TItem;
|
PItem = ^TItem;
|
||||||
// Internal structure for storing a receiver and list linkage.
|
// Internal structure for storing a receiver and list linkage.
|
||||||
TItem = record
|
TItem = record
|
||||||
Next, Prev: PItem; // Pointers to the next and previous items in the doubly linked list.
|
private
|
||||||
|
FNext: PItem;
|
||||||
|
FPrev: PItem;
|
||||||
|
function GetNext: PItem; inline;
|
||||||
|
public
|
||||||
|
|
||||||
Receiver: T; // The registered interface instance (the event sink).
|
Receiver: T; // The registered interface instance (the event sink).
|
||||||
|
property Next: PItem read GetNext;
|
||||||
|
property Prev: PItem read FPrev;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Notify function. If this results false, it will be removed from the notify list
|
// Notify function. If this results false, it will be removed from the notify list
|
||||||
@@ -31,6 +38,9 @@ type
|
|||||||
// Bit 0 of the address stores the lock state (0 = locked, 1 = unlocked).
|
// Bit 0 of the address stores the lock state (0 = locked, 1 = unlocked).
|
||||||
[volatile]
|
[volatile]
|
||||||
FList: PItem;
|
FList: PItem;
|
||||||
|
class var
|
||||||
|
FReverseOnNotify: Boolean;
|
||||||
|
class constructor CreateClass;
|
||||||
|
|
||||||
// Allocates memory for a new TItem.
|
// Allocates memory for a new TItem.
|
||||||
class function AllocItem: PItem; static; inline;
|
class function AllocItem: PItem; static; inline;
|
||||||
@@ -62,10 +72,18 @@ type
|
|||||||
// by setting its interface reference to nil. The list item itself is not freed here.
|
// by setting its interface reference to nil. The list item itself is not freed here.
|
||||||
procedure Notify(const Func: TNotifyProc);
|
procedure Notify(const Func: TNotifyProc);
|
||||||
property First: PItem read GetFirst;
|
property First: PItem read GetFirst;
|
||||||
|
|
||||||
|
// If this is true, the list is reversed after each Notify, hoping for a better distrubution of events.
|
||||||
|
class property ReverseOnNotify: Boolean read FReverseOnNotify write FReverseOnNotify;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
class constructor TMycNotifyList<T>.CreateClass;
|
||||||
|
begin
|
||||||
|
FReverseOnNotify := true;
|
||||||
|
end;
|
||||||
|
|
||||||
class operator TMycNotifyList<T>.Initialize(out Dest: TMycNotifyList<T>);
|
class operator TMycNotifyList<T>.Initialize(out Dest: TMycNotifyList<T>);
|
||||||
begin
|
begin
|
||||||
NativeUInt(Dest.FList) := 1;
|
NativeUInt(Dest.FList) := 1;
|
||||||
@@ -88,10 +106,10 @@ begin
|
|||||||
|
|
||||||
Item := AllocItem;
|
Item := AllocItem;
|
||||||
Item.Receiver := Receiver;
|
Item.Receiver := Receiver;
|
||||||
Item.Prev := nil;
|
Item.FPrev := nil;
|
||||||
Item.Next := FList;
|
Item.FNext := FList;
|
||||||
if Item.Next <> nil then
|
if Item.FNext <> nil then
|
||||||
Item.Next.Prev := Item;
|
Item.FNext.FPrev := Item;
|
||||||
|
|
||||||
FList := Item;
|
FList := Item;
|
||||||
|
|
||||||
@@ -133,108 +151,123 @@ var
|
|||||||
begin
|
begin
|
||||||
Assert(IsLocked);
|
Assert(IsLocked);
|
||||||
|
|
||||||
var rev: PItem := nil;
|
// Invariant: Append all detached items at end of the list. We can't simply free them, because the subscription is owned by the client.
|
||||||
var tmp: PItem := nil;
|
|
||||||
|
if FReverseOnNotify then
|
||||||
|
begin
|
||||||
|
// Method: stack assigned items and rebuild the list from the stack (reversing the order)
|
||||||
|
|
||||||
|
var stack: PItem := nil;
|
||||||
|
var released: PItem := nil;
|
||||||
|
|
||||||
while (FList <> nil) and Assigned(FList.Receiver) do
|
while (FList <> nil) and Assigned(FList.Receiver) do
|
||||||
begin
|
begin
|
||||||
Item := FList;
|
Item := FList;
|
||||||
FList := Item.Next;
|
FList := Item.FNext;
|
||||||
|
|
||||||
if Func(Item.Receiver) then
|
if Func(Item.Receiver) then
|
||||||
begin
|
begin
|
||||||
Item.Next := rev;
|
// item stays valid
|
||||||
rev := Item;
|
Item.FNext := stack;
|
||||||
|
stack := Item;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
// release the receiver
|
// release the receiver
|
||||||
Item.Receiver := nil;
|
Item.Receiver := nil;
|
||||||
|
|
||||||
Item.Next := tmp;
|
Item.FNext := released;
|
||||||
tmp := Item;
|
released := Item;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
while tmp <> nil do
|
// The list now only contains old released items. Push the newly released ones.
|
||||||
|
while released <> nil do
|
||||||
begin
|
begin
|
||||||
Item := tmp;
|
Item := released;
|
||||||
tmp := Item.Next;
|
released := Item.FNext;
|
||||||
|
|
||||||
Item.Prev := nil;
|
Item.FPrev := nil;
|
||||||
Item.Next := FList;
|
Item.FNext := FList;
|
||||||
FList := Item;
|
FList := Item;
|
||||||
if Item.Next <> nil then
|
if Item.FNext <> nil then
|
||||||
Item.Next.Prev := Item;
|
Item.FNext.FPrev := Item;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
while rev <> nil do
|
// Now push the valid items, so that they are at the beginning of the list.
|
||||||
|
while stack <> nil do
|
||||||
begin
|
begin
|
||||||
Item := rev;
|
Item := stack;
|
||||||
rev := Item.Next;
|
stack := Item.FNext;
|
||||||
|
|
||||||
Item.Prev := nil;
|
Item.FPrev := nil;
|
||||||
Item.Next := FList;
|
Item.FNext := FList;
|
||||||
FList := Item;
|
FList := Item;
|
||||||
if Item.Next <> nil then
|
if Item.FNext <> nil then
|
||||||
Item.Next.Prev := Item;
|
Item.FNext.FPrev := Item;
|
||||||
end;
|
end;
|
||||||
{
|
end
|
||||||
tmp := nil;
|
else
|
||||||
Last := nil;
|
begin
|
||||||
|
// Method: filter released items and add then to the end of the list
|
||||||
|
|
||||||
|
var released: PItem := nil;
|
||||||
|
var lastValid: PItem := nil;
|
||||||
|
|
||||||
Item := FList;
|
Item := FList;
|
||||||
while (Item <> nil) and Assigned(Item.Receiver) do
|
while (Item <> nil) and Assigned(Item.Receiver) do
|
||||||
begin
|
begin
|
||||||
if not Func(Item.Receiver) then
|
if not Func(Item.Receiver) then
|
||||||
begin
|
begin
|
||||||
// Receiver wants no more notifications, detach it
|
// item is now invalid
|
||||||
if Item = FList then
|
if Item = FList then
|
||||||
FList := Item.Next;
|
FList := Item.FNext;
|
||||||
|
|
||||||
if Item.Prev <> nil then
|
if Item.FPrev <> nil then
|
||||||
Item.Prev.Next := Item.Next;
|
Item.FPrev.FNext := Item.FNext;
|
||||||
if Item.Next <> nil then
|
if Item.FNext <> nil then
|
||||||
Item.Next.Prev := Item.Prev;
|
Item.FNext.FPrev := Item.FPrev;
|
||||||
|
|
||||||
// release the receiver
|
// release the receiver
|
||||||
Item.Receiver := nil;
|
Item.Receiver := nil;
|
||||||
|
|
||||||
// and save the list item in a tmp list for later use
|
// and save the list item in a tmp list for later use
|
||||||
var nxt := Item.Next;
|
var nxt := Item.FNext;
|
||||||
Item.Next := tmp;
|
Item.FNext := released;
|
||||||
tmp := Item;
|
released := Item;
|
||||||
Item := nxt;
|
Item := nxt;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
Last := Item;
|
// save a pointer to the last valid item
|
||||||
Item := Last.Next;
|
lastValid := Item;
|
||||||
|
Item := lastValid.FNext;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Append all detached items at end of the list. We can't simply free them, because the subscription is owned by the client.
|
// insert all new released items after the last valid item
|
||||||
while tmp <> nil do
|
while released <> nil do
|
||||||
begin
|
begin
|
||||||
Item := tmp;
|
Item := released;
|
||||||
tmp := Item.Next;
|
released := Item.FNext;
|
||||||
|
|
||||||
if Last = nil then
|
if lastValid = nil then
|
||||||
begin
|
begin
|
||||||
Item.Prev := nil;
|
Item.FPrev := nil;
|
||||||
Item.Next := FList;
|
Item.FNext := FList;
|
||||||
FList := Item;
|
FList := Item;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
Item.Prev := Last;
|
Item.FPrev := lastValid;
|
||||||
Item.Next := Last.Next;
|
Item.FNext := lastValid.Next;
|
||||||
if Item.Prev <> nil then
|
if Item.FPrev <> nil then
|
||||||
Item.Prev.Next := Item;
|
Item.FPrev.FNext := Item;
|
||||||
|
end;
|
||||||
|
if Item.FNext <> nil then
|
||||||
|
Item.FNext.FPrev := Item;
|
||||||
end;
|
end;
|
||||||
if Item.Next <> nil then
|
|
||||||
Item.Next.Prev := Item;
|
|
||||||
end;
|
end;
|
||||||
}
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycNotifyList<T>.Release;
|
procedure TMycNotifyList<T>.Release;
|
||||||
@@ -270,12 +303,12 @@ begin
|
|||||||
Item.Receiver := nil;
|
Item.Receiver := nil;
|
||||||
|
|
||||||
if Item = FList then
|
if Item = FList then
|
||||||
FList := Item.Next;
|
FList := Item.FNext;
|
||||||
|
|
||||||
if Item.Prev <> nil then
|
if Item.FPrev <> nil then
|
||||||
Item.Prev.Next := Item.Next;
|
Item.FPrev.FNext := Item.FNext;
|
||||||
if Item.Next <> nil then
|
if Item.FNext <> nil then
|
||||||
Item.Next.Prev := Item.Prev;
|
Item.FNext.FPrev := Item.FPrev;
|
||||||
|
|
||||||
FreeItem(Item);
|
FreeItem(Item);
|
||||||
end;
|
end;
|
||||||
@@ -287,4 +320,11 @@ begin
|
|||||||
Unadvise(TTag(FList));
|
Unadvise(TTag(FList));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TMycNotifyList<T>.TItem.GetNext: PItem;
|
||||||
|
begin
|
||||||
|
if not Assigned(Receiver) then
|
||||||
|
exit(nil);
|
||||||
|
Result := FNext;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ uses
|
|||||||
Myc.Trade.Types,
|
Myc.Trade.Types,
|
||||||
Myc.Trade.DataArray,
|
Myc.Trade.DataArray,
|
||||||
Myc.Trade.DataPoint,
|
Myc.Trade.DataPoint,
|
||||||
|
Myc.Trade.DataPoint.Impl,
|
||||||
Myc.Fmx.Chart;
|
Myc.Fmx.Chart;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|||||||
@@ -3,12 +3,57 @@ unit Myc.Trade.DataPoint.Impl;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
|
System.SysUtils,
|
||||||
Myc.Signals,
|
Myc.Signals,
|
||||||
Myc.Trade.Types,
|
Myc.Trade.Types,
|
||||||
Myc.Trade.DataPoint;
|
Myc.Trade.DataPoint,
|
||||||
|
Myc.Core.Notifier;
|
||||||
|
|
||||||
type
|
type
|
||||||
// Null object implementation for IMycConverter
|
// Abstract base class for data consumers.
|
||||||
|
TMycProcessor<T> = class abstract(TInterfacedObject, IMycProcessor<T>)
|
||||||
|
protected
|
||||||
|
function ProcessData(const Value: T): TState; virtual; abstract;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Concrete data provider that manages a list of processors (listeners).
|
||||||
|
TMycDataProvider<T> = class abstract(TContainedObject, TDataProvider<T>.IDataProvider)
|
||||||
|
private
|
||||||
|
FListeners: TMycNotifyList<IMycProcessor<T>>;
|
||||||
|
public
|
||||||
|
constructor Create(const Controller: IInterface);
|
||||||
|
destructor Destroy; override;
|
||||||
|
// Notifies all linked processors.
|
||||||
|
function Broadcast(const Value: T): TState;
|
||||||
|
// Link a Processor
|
||||||
|
function Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||||
|
// Unlink a linked strategy
|
||||||
|
procedure Unlink(Tag: TDataProvider<T>.TTag);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Null object implementation for IDataProvider.
|
||||||
|
TNullDataProvider<T> = class(TInterfacedObject, TDataProvider<T>.IDataProvider)
|
||||||
|
public
|
||||||
|
function Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||||
|
procedure Unlink(Tag: TDataProvider<T>.TTag);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Abstract base class for components that process data of type S and provide data of type T.
|
||||||
|
TMycConverter<S, T> = class abstract(TMycProcessor<S>, TConverter<S, T>.IConverter)
|
||||||
|
private
|
||||||
|
FSender: TMycDataProvider<T>;
|
||||||
|
function GetSender: TDataProvider<T>.IDataProvider;
|
||||||
|
protected
|
||||||
|
function ProcessData(const Value: S): TState; override; abstract;
|
||||||
|
// Broadcasts the given data to all linked processors.
|
||||||
|
function Broadcast(const Value: T): TState;
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
property Sender: TDataProvider<T>.IDataProvider read GetSender;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Null object implementation for IConverter.
|
||||||
TNullConverter<S, T> = class(TInterfacedObject, TConverter<S, T>.IConverter)
|
TNullConverter<S, T> = class(TInterfacedObject, TConverter<S, T>.IConverter)
|
||||||
private
|
private
|
||||||
function GetSender: TDataProvider<T>.IDataProvider;
|
function GetSender: TDataProvider<T>.IDataProvider;
|
||||||
@@ -16,22 +61,7 @@ type
|
|||||||
function ProcessData(const Value: S): TState;
|
function ProcessData(const Value: S): TState;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycConverter<S, T> = class abstract(TMycProcessor<S>, TConverter<S, T>.IConverter)
|
// A generic converter that uses a function reference for the conversion logic.
|
||||||
private
|
|
||||||
FSender: TMycDataProvider<T>;
|
|
||||||
function GetSender: TDataProvider<T>.IDataProvider;
|
|
||||||
protected
|
|
||||||
function ProcessData(const Value: S): TState; override; abstract;
|
|
||||||
|
|
||||||
// Broadcasts the given data to all linked processors.
|
|
||||||
function Broadcast(const Value: T): TState;
|
|
||||||
|
|
||||||
public
|
|
||||||
constructor Create;
|
|
||||||
destructor Destroy; override;
|
|
||||||
property Sender: TDataProvider<T>.IDataProvider read GetSender;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TMycGenericConverter<S, T> = class(TMycConverter<S, T>)
|
TMycGenericConverter<S, T> = class(TMycConverter<S, T>)
|
||||||
private
|
private
|
||||||
FFunc: TConstFunc<S, T>;
|
FFunc: TConstFunc<S, T>;
|
||||||
@@ -41,12 +71,14 @@ type
|
|||||||
constructor Create(const AFunc: TConstFunc<S, T>);
|
constructor Create(const AFunc: TConstFunc<S, T>);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// A converter specialized for calculating indicators.
|
||||||
TMycIndicator<S, T> = class(TMycConverter<S, T>)
|
TMycIndicator<S, T> = class(TMycConverter<S, T>)
|
||||||
protected
|
protected
|
||||||
function ProcessData(const Value: S): TState; override; final;
|
function ProcessData(const Value: S): TState; override; final;
|
||||||
function Calculate(const Value: S): T; virtual; abstract;
|
function Calculate(const Value: S): T; virtual; abstract;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// A converter that counts incoming data points and outputs the current count.
|
||||||
TMycDataCounter<T> = class(TMycConverter<T, Int64>)
|
TMycDataCounter<T> = class(TMycConverter<T, Int64>)
|
||||||
private
|
private
|
||||||
FCount: Int64;
|
FCount: Int64;
|
||||||
@@ -56,27 +88,124 @@ type
|
|||||||
constructor Create;
|
constructor Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// A converter that takes an array and broadcasts each element individually.
|
||||||
TMycTicker<T> = class(TMycConverter<TArray<T>, T>)
|
TMycTicker<T> = class(TMycConverter<TArray<T>, T>)
|
||||||
public
|
public
|
||||||
function ProcessData(const Values: TArray<T>): TState; override;
|
function ProcessData(const Values: TArray<T>): TState; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// A converter that reads a specific field from a record using RTTI.
|
||||||
TMycRecordFieldReader<S, T> = class(TMycConverter<S, T>)
|
TMycRecordFieldReader<S, T> = class(TMycConverter<S, T>)
|
||||||
private
|
private
|
||||||
FOffset: Integer;
|
FOffset: Integer;
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(const AFieldName: String);
|
constructor Create(const AFieldName: String);
|
||||||
function ProcessData(const Values: S): TState; override;
|
function ProcessData(const Values: S): TState; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// A generic processor implementation that uses a function reference.
|
||||||
|
TMycGenericProcessor<T> = class(TMycProcessor<T>)
|
||||||
|
type
|
||||||
|
TProc = reference to function(const Value: T): TState;
|
||||||
|
private
|
||||||
|
FProc: TProc;
|
||||||
|
protected
|
||||||
|
function ProcessData(const Value: T): TState; override; final;
|
||||||
|
public
|
||||||
|
constructor Create(const AProc: TProc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// A processor implementation that is owned by a controller.
|
||||||
|
TMycContainedProcessor<T> = class(TContainedObject, IMycProcessor<T>)
|
||||||
|
type
|
||||||
|
TProc = function(const Value: T): TState of object;
|
||||||
|
private
|
||||||
|
FProc: TProc;
|
||||||
|
function ProcessData(const Value: T): TState;
|
||||||
|
public
|
||||||
|
constructor Create(const Controller: IInterface; const AProc: TProc);
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
System.TypInfo,
|
System.TypInfo,
|
||||||
System.SysUtils,
|
|
||||||
System.RTTI;
|
System.RTTI;
|
||||||
|
|
||||||
|
{ TMycDataProvider<T> }
|
||||||
|
|
||||||
|
constructor TMycDataProvider<T>.Create(const Controller: IInterface);
|
||||||
|
begin
|
||||||
|
inherited Create(Controller);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TMycDataProvider<T>.Destroy;
|
||||||
|
begin
|
||||||
|
FListeners.Finalize;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycDataProvider<T>.Broadcast(const Value: T): TState;
|
||||||
|
begin
|
||||||
|
FListeners.Lock;
|
||||||
|
try
|
||||||
|
var item := FListeners.First;
|
||||||
|
if item = nil then
|
||||||
|
exit(TState.Null);
|
||||||
|
|
||||||
|
var i := 1;
|
||||||
|
while item.Next <> nil do
|
||||||
|
begin
|
||||||
|
inc(i);
|
||||||
|
item := item.Next;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var done := TLatch.CreateLatch(i);
|
||||||
|
while item <> nil do
|
||||||
|
begin
|
||||||
|
item.Receiver.ProcessData(Value).Signal.Subscribe(done);
|
||||||
|
item := item.Prev;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := done.State;
|
||||||
|
finally
|
||||||
|
FListeners.Release;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycDataProvider<T>.Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||||
|
begin
|
||||||
|
// Add the Processor to the notification list
|
||||||
|
FListeners.Lock;
|
||||||
|
try
|
||||||
|
Result := FListeners.Advise(Processor);
|
||||||
|
finally
|
||||||
|
FListeners.Release;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMycDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
|
||||||
|
begin
|
||||||
|
FListeners.Lock;
|
||||||
|
try
|
||||||
|
FListeners.Unadvise(Tag);
|
||||||
|
finally
|
||||||
|
FListeners.Release;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TNullDataProvider<T> }
|
||||||
|
|
||||||
|
function TNullDataProvider<T>.Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||||
|
begin
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TNullDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
|
||||||
|
begin
|
||||||
|
// Do nothing in the null implementation.
|
||||||
|
end;
|
||||||
|
|
||||||
{ TMycConverter<S, T> }
|
{ TMycConverter<S, T> }
|
||||||
|
|
||||||
constructor TMycConverter<S, T>.Create;
|
constructor TMycConverter<S, T>.Create;
|
||||||
@@ -101,7 +230,7 @@ begin
|
|||||||
Result := FSender;
|
Result := FSender;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TNullConverter }
|
{ TNullConverter<S, T> }
|
||||||
|
|
||||||
function TNullConverter<S, T>.GetSender: TDataProvider<T>.IDataProvider;
|
function TNullConverter<S, T>.GetSender: TDataProvider<T>.IDataProvider;
|
||||||
begin
|
begin
|
||||||
@@ -133,6 +262,8 @@ begin
|
|||||||
Result := Broadcast(Calculate(Value));
|
Result := Broadcast(Calculate(Value));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TMycDataCounter<T> }
|
||||||
|
|
||||||
constructor TMycDataCounter<T>.Create;
|
constructor TMycDataCounter<T>.Create;
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
@@ -145,6 +276,8 @@ begin
|
|||||||
inc(FCount);
|
inc(FCount);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TMycTicker<T> }
|
||||||
|
|
||||||
function TMycTicker<T>.ProcessData(const Values: TArray<T>): TState;
|
function TMycTicker<T>.ProcessData(const Values: TArray<T>): TState;
|
||||||
begin
|
begin
|
||||||
var done := TLatch.CreateLatch(Length(Values));
|
var done := TLatch.CreateLatch(Length(Values));
|
||||||
@@ -156,6 +289,8 @@ begin
|
|||||||
Result := done.State;
|
Result := done.State;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TMycRecordFieldReader<S, T> }
|
||||||
|
|
||||||
constructor TMycRecordFieldReader<S, T>.Create(const AFieldName: String);
|
constructor TMycRecordFieldReader<S, T>.Create(const AFieldName: String);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
@@ -193,4 +328,30 @@ begin
|
|||||||
Result := Broadcast(PT(fieldPtr)^);
|
Result := Broadcast(PT(fieldPtr)^);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TMycGenericProcessor<T> }
|
||||||
|
|
||||||
|
constructor TMycGenericProcessor<T>.Create(const AProc: TProc);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FProc := AProc;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycGenericProcessor<T>.ProcessData(const Value: T): TState;
|
||||||
|
begin
|
||||||
|
Result := FProc(Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TMycContainedProcessor<T> }
|
||||||
|
|
||||||
|
constructor TMycContainedProcessor<T>.Create(const Controller: IInterface; const AProc: TProc);
|
||||||
|
begin
|
||||||
|
inherited Create(Controller);
|
||||||
|
FProc := AProc;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycContainedProcessor<T>.ProcessData(const Value: T): TState;
|
||||||
|
begin
|
||||||
|
Result := FProc(Value);
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+1
-159
@@ -4,8 +4,7 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Myc.Signals,
|
Myc.Signals,
|
||||||
Myc.Trade.Types,
|
Myc.Trade.Types;
|
||||||
Myc.Core.Notifier;
|
|
||||||
|
|
||||||
type
|
type
|
||||||
// Represents a time-stamped data point in a series.
|
// Represents a time-stamped data point in a series.
|
||||||
@@ -19,11 +18,6 @@ type
|
|||||||
function ProcessData(const Value: T): TState;
|
function ProcessData(const Value: T): TState;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycProcessor<T> = class abstract(TInterfacedObject, IMycProcessor<T>)
|
|
||||||
protected
|
|
||||||
function ProcessData(const Value: T): TState; virtual; abstract;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TDataProvider<T> = record
|
TDataProvider<T> = record
|
||||||
type
|
type
|
||||||
TTag = Pointer;
|
TTag = Pointer;
|
||||||
@@ -56,48 +50,6 @@ type
|
|||||||
class property Null: IDataProvider read FNull;
|
class property Null: IDataProvider read FNull;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycDataProvider<T> = class abstract(TContainedObject, TDataProvider<T>.IDataProvider)
|
|
||||||
private
|
|
||||||
FListeners: TMycNotifyList<IMycProcessor<T>>;
|
|
||||||
public
|
|
||||||
constructor Create(const Controller: IInterface);
|
|
||||||
destructor Destroy; override;
|
|
||||||
// Notifies all linked processors.
|
|
||||||
function Broadcast(const Value: T): TState;
|
|
||||||
procedure Notify(const Func: TMycNotifyList<IMycProcessor<T>>.TNotifyProc);
|
|
||||||
// Link a Processor
|
|
||||||
function Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
|
|
||||||
// Unlink a linked strategy
|
|
||||||
procedure Unlink(Tag: TDataProvider<T>.TTag);
|
|
||||||
end;
|
|
||||||
|
|
||||||
TMycGenericProcessor<T> = class(TMycProcessor<T>)
|
|
||||||
type
|
|
||||||
TProc = reference to function(const Value: T): TState;
|
|
||||||
private
|
|
||||||
FProc: TProc;
|
|
||||||
protected
|
|
||||||
function ProcessData(const Value: T): TState; override; final;
|
|
||||||
public
|
|
||||||
constructor Create(const AProc: TProc);
|
|
||||||
end;
|
|
||||||
|
|
||||||
TMycContainedProcessor<T> = class(TContainedObject, IMycProcessor<T>)
|
|
||||||
type
|
|
||||||
TProc = function(const Value: T): TState of object;
|
|
||||||
private
|
|
||||||
FProc: TProc;
|
|
||||||
function ProcessData(const Value: T): TState;
|
|
||||||
public
|
|
||||||
constructor Create(const Controller: IInterface; const AProc: TProc);
|
|
||||||
end;
|
|
||||||
|
|
||||||
TNullDataProvider<T> = class(TInterfacedObject, TDataProvider<T>.IDataProvider)
|
|
||||||
public
|
|
||||||
function Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
|
|
||||||
procedure Unlink(Tag: TDataProvider<T>.TTag);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Interface helper for IMycConverter<S,T> providing the null object pattern.
|
// Interface helper for IMycConverter<S,T> providing the null object pattern.
|
||||||
TConverter<S, T> = record
|
TConverter<S, T> = record
|
||||||
type
|
type
|
||||||
@@ -150,18 +102,6 @@ implementation
|
|||||||
uses
|
uses
|
||||||
Myc.Trade.DataPoint.Impl;
|
Myc.Trade.DataPoint.Impl;
|
||||||
|
|
||||||
{ TNullDataProvider }
|
|
||||||
|
|
||||||
function TNullDataProvider<T>.Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
|
|
||||||
begin
|
|
||||||
Result := nil;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TNullDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
|
|
||||||
begin
|
|
||||||
// Do nothing in the null implementation.
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TDataPoint<T> }
|
{ TDataPoint<T> }
|
||||||
|
|
||||||
constructor TDataPoint<T>.Create(ATime: TDateTime; const AData: T);
|
constructor TDataPoint<T>.Create(ATime: TDateTime; const AData: T);
|
||||||
@@ -216,104 +156,6 @@ begin
|
|||||||
FDataProvider.Unlink(Tag);
|
FDataProvider.Unlink(Tag);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TMycGenericProcessor<T> }
|
|
||||||
|
|
||||||
constructor TMycGenericProcessor<T>.Create(const AProc: TProc);
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
FProc := AProc;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMycGenericProcessor<T>.ProcessData(const Value: T): TState;
|
|
||||||
begin
|
|
||||||
Result := FProc(Value);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TMycContainedProcessor<T> }
|
|
||||||
|
|
||||||
constructor TMycContainedProcessor<T>.Create(const Controller: IInterface; const AProc: TProc);
|
|
||||||
begin
|
|
||||||
inherited Create(Controller);
|
|
||||||
FProc := AProc;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMycContainedProcessor<T>.ProcessData(const Value: T): TState;
|
|
||||||
begin
|
|
||||||
Result := FProc(Value);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TMycDataProvider<T> }
|
|
||||||
|
|
||||||
constructor TMycDataProvider<T>.Create(const Controller: IInterface);
|
|
||||||
begin
|
|
||||||
inherited Create(Controller);
|
|
||||||
end;
|
|
||||||
|
|
||||||
destructor TMycDataProvider<T>.Destroy;
|
|
||||||
begin
|
|
||||||
FListeners.Finalize;
|
|
||||||
inherited Destroy;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMycDataProvider<T>.Broadcast(const Value: T): TState;
|
|
||||||
begin
|
|
||||||
FListeners.Lock;
|
|
||||||
try
|
|
||||||
var item := FListeners.First;
|
|
||||||
if item = nil then
|
|
||||||
exit(TState.Null);
|
|
||||||
|
|
||||||
var i := 1;
|
|
||||||
while item.Next <> nil do
|
|
||||||
begin
|
|
||||||
inc(i);
|
|
||||||
item := item.Next;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var done := TLatch.CreateLatch(i);
|
|
||||||
while item <> nil do
|
|
||||||
begin
|
|
||||||
item.Receiver.ProcessData(Value).Signal.Subscribe(done);
|
|
||||||
item := item.Prev;
|
|
||||||
end;
|
|
||||||
|
|
||||||
Result := done.State;
|
|
||||||
finally
|
|
||||||
FListeners.Release;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMycDataProvider<T>.Notify(const Func: TMycNotifyList<IMycProcessor<T>>.TNotifyProc);
|
|
||||||
begin
|
|
||||||
FListeners.Lock;
|
|
||||||
try
|
|
||||||
FListeners.Notify(Func);
|
|
||||||
finally
|
|
||||||
FListeners.Release;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMycDataProvider<T>.Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
|
|
||||||
begin
|
|
||||||
// Add the Processor to the notification list
|
|
||||||
FListeners.Lock;
|
|
||||||
try
|
|
||||||
Result := FListeners.Advise(Processor);
|
|
||||||
finally
|
|
||||||
FListeners.Release;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMycDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
|
|
||||||
begin
|
|
||||||
FListeners.Lock;
|
|
||||||
try
|
|
||||||
FListeners.Unadvise(Tag);
|
|
||||||
finally
|
|
||||||
FListeners.Release;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TConverter<S, T> }
|
{ TConverter<S, T> }
|
||||||
|
|
||||||
class constructor TConverter<S, T>.CreateClass;
|
class constructor TConverter<S, T>.CreateClass;
|
||||||
|
|||||||
Reference in New Issue
Block a user