diff --git a/AuraTrader/AuraTrader.dproj b/AuraTrader/AuraTrader.dproj
index db3eec0..0ea2347 100644
--- a/AuraTrader/AuraTrader.dproj
+++ b/AuraTrader/AuraTrader.dproj
@@ -4,7 +4,7 @@
20.3
FMX
True
- Debug
+ Release
Win64
AuraTrader
3
diff --git a/Src/Myc.Core.Notifier.pas b/Src/Myc.Core.Notifier.pas
index 1388a29..ce212af 100644
--- a/Src/Myc.Core.Notifier.pas
+++ b/Src/Myc.Core.Notifier.pas
@@ -7,43 +7,53 @@ uses
System.SyncObjs;
type
- // Low-level implementation for thread-safe multicast events.
- // Implemented as a list referencing IInterface instances, with a locking mechanism for thread safety.
- // Utilizes minimal memory.
- // `Advise` adds an interface and returns a tag for constant-time removal by `Unadvise`.
- // `UnadviseAll` removes all registered interfaces.
- // After `Finalize` is called, the list is cleared, and no new interfaces can be added (closed state).
+ // A thread-safe, low-level list for managing multicast event sinks of type IInterface.
+ // It uses a doubly linked list and a spinlock for synchronization.
+ // `Advise` registers a sink and returns a tag for constant-time removal via `Unadvise`.
TMycNotifyList = record
type
- TTag = Pointer; // Opaque tag used to identify a registered receiver for unsubscription.
+ // Opaque tag used to identify a registered receiver for unsubscription.
+ TTag = Pointer;
- PItem = ^TItem; // Pointer to an internal list item.
- TItem = record // Internal structure for storing a receiver and list linkage.
+ // Pointer to an internal list item.
+ PItem = ^TItem;
+ // Internal structure for storing a receiver and list linkage.
+ TItem = record
Next, Prev: PItem; // Pointers to the next and previous items in the doubly linked list.
Receiver: T; // The registered interface instance (the event sink).
end;
strict private
- // Bit 0: Lock state (0 = locked, 1 = unlocked).
- // Bit 1: Finalized state (0 = not finalized, 1 = finalized).
- // Other bits (if not 0 and bit 0 is 1) can be a direct interface pointer if FList is nil.
- FList: PItem; // Head of the linked list for additional receivers beyond the first one.
+ // Head of the linked list. The pointer value itself is repurposed for a spinlock.
+ // Bit 0 of the address stores the lock state (0 = locked, 1 = unlocked).
+ FList: PItem;
- class function AllocItem: PItem; static; inline; // Allocates and initializes memory for a new TItem.
- class procedure FreeItem(Item: PItem); static; inline; // Frees memory previously allocated for a TItem.
+ // Allocates memory for a new TItem.
+ class function AllocItem: PItem; static; inline;
+ // Frees memory previously allocated for a TItem.
+ class procedure FreeItem(Item: PItem); static; inline;
public
- procedure Create; // Initializes the notification list, preparing it for use.
- procedure Destroy; // Cleans up all resources, including unadvising all receivers. Assumes no concurrent access.
- function Advise(const Receiver: T): TTag; // Registers a receiver interface and returns an opaque tag for later unsubscription.
+ // Initializes the list in an unlocked state.
+ procedure Create;
+ // Safely clears all registered receivers and cleans up resources.
+ procedure Destroy;
+ // Registers a receiver interface and returns an opaque tag for later unsubscription.
+ function Advise(const Receiver: T): TTag;
+ // Unregisters a single receiver using its subscription tag.
procedure Unadvise(Tag: TTag);
- procedure UnadviseAll; // Unregisters all currently advised receivers.
- procedure Lock; inline; // Acquires an exclusive lock for thread-safe operations on the list.
- procedure Release; inline; // Releases the previously acquired exclusive lock.
- function IsLocked: Boolean; inline; // Checks if the list is currently locked by any thread.
- procedure Notify(
- Func: TPredicate
- ); // Iterates through registered receivers and invokes the predicate; removes receiver if predicate returns false.
+ // Unregisters all currently advised receivers.
+ procedure UnadviseAll;
+ // Acquires an exclusive lock for thread-safe operations on the list.
+ procedure Lock; inline;
+ // Releases the previously acquired exclusive lock.
+ procedure Release; inline;
+ // Checks if the list is currently locked by any thread.
+ function IsLocked: Boolean; inline;
+ // Invokes a predicate for each registered receiver.
+ // If the predicate returns false, the receiver is detached from further notifications
+ // by setting its interface reference to nil. The list item itself is not freed here.
+ procedure Notify(Func: TPredicate);
end;
implementation
@@ -55,10 +65,7 @@ end;
procedure TMycNotifyList.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);
- NativeUInt(FList) := NativeUInt(FList) and not 1;
+ Lock;
UnadviseAll;
end;
@@ -173,7 +180,7 @@ end;
procedure TMycNotifyList.Release;
begin
Assert(IsLocked);
- TInterlocked.Exchange(NativeUint(FList), NativeUint(FList) or 1);
+ TInterlocked.BitTestAndSet(NativeUint(FList), 0);
end;
procedure TMycNotifyList.Unadvise(Tag: TTag);