com.wallstop-studios.dxmessaging 2.2.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +315 -67
- package/CHANGELOG.md.meta +7 -7
- package/Editor/Analyzers/BaseCallIlInspector.cs +277 -0
- package/Editor/Analyzers/BaseCallIlInspector.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallLogMessageParser.cs +295 -0
- package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallReportAggregator.cs +308 -0
- package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallTypeScanner.cs +110 -0
- package/Editor/Analyzers/BaseCallTypeScanner.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallTypeScannerCore.cs +562 -0
- package/Editor/Analyzers/BaseCallTypeScannerCore.cs.meta +11 -0
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +1129 -0
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +11 -0
- package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +44 -44
- package/Editor/Analyzers/Microsoft.CodeAnalysis.dll.meta +44 -44
- package/Editor/Analyzers/System.Collections.Immutable.dll.meta +44 -44
- package/Editor/Analyzers/System.Reflection.Metadata.dll.meta +44 -44
- package/Editor/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +44 -44
- package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
- package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +46 -0
- package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
- package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +72 -72
- package/Editor/Analyzers.meta +8 -8
- package/Editor/AssemblyInfo.cs.meta +11 -3
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +81 -0
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +11 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +429 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +11 -0
- package/Editor/CustomEditors/MessagingComponentEditor.cs +1 -1
- package/Editor/CustomEditors/MessagingComponentEditor.cs.meta +11 -3
- package/Editor/CustomEditors.meta +2 -2
- package/Editor/DxMessagingEditorIdle.cs +62 -0
- package/Editor/DxMessagingEditorIdle.cs.meta +11 -0
- package/Editor/DxMessagingEditorInitializer.cs +112 -15
- package/Editor/DxMessagingEditorInitializer.cs.meta +11 -3
- package/Editor/DxMessagingEditorLog.cs +32 -0
- package/Editor/DxMessagingEditorLog.cs.meta +11 -0
- package/Editor/DxMessagingMenu.cs.meta +11 -11
- package/Editor/DxMessagingSceneBuildProcessor.cs.meta +11 -11
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +313 -0
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +11 -0
- package/Editor/Settings/DxMessagingSettings.cs +261 -11
- package/Editor/Settings/DxMessagingSettings.cs.meta +11 -3
- package/Editor/Settings/DxMessagingSettingsProvider.cs +50 -33
- package/Editor/Settings/DxMessagingSettingsProvider.cs.meta +11 -3
- package/Editor/Settings.meta +2 -2
- package/Editor/SetupCscRsp.cs +406 -39
- package/Editor/SetupCscRsp.cs.meta +11 -3
- package/Editor/Testing/MessagingComponentEditorHarness.cs +2 -2
- package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +11 -3
- package/Editor/Testing.meta +3 -3
- package/Editor/WallstopStudios.DxMessaging.Editor.asmdef +14 -14
- package/Editor/WallstopStudios.DxMessaging.Editor.asmdef.meta +7 -7
- package/Editor.meta +8 -8
- package/LICENSE.md +9 -9
- package/LICENSE.md.meta +7 -7
- package/README.md +940 -900
- package/README.md.meta +7 -7
- package/Runtime/AssemblyInfo.cs +4 -0
- package/Runtime/AssemblyInfo.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs +26 -0
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +11 -0
- package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs +2 -4
- package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs +56 -0
- package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs.meta +11 -0
- package/Runtime/Core/Attributes.meta +2 -2
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs +195 -0
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs.meta +11 -0
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs +179 -0
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs.meta +11 -0
- package/Runtime/Core/Configuration.meta +9 -0
- package/Runtime/Core/DataStructure/CyclicBuffer.cs +46 -28
- package/Runtime/Core/DataStructure/CyclicBuffer.cs.meta +11 -3
- package/Runtime/Core/DataStructure.meta +2 -2
- package/Runtime/Core/Diagnostics/MessageEmissionData.cs.meta +11 -3
- package/Runtime/Core/Diagnostics/MessageRegistrationData.cs.meta +11 -3
- package/Runtime/Core/Diagnostics/MessageRegistrationType.cs.meta +11 -3
- package/Runtime/Core/Diagnostics.meta +2 -2
- package/Runtime/Core/DxMessagingStaticState.cs +19 -0
- package/Runtime/Core/DxMessagingStaticState.cs.meta +11 -11
- package/Runtime/Core/Extensions/EnumExtensions.cs +6 -5
- package/Runtime/Core/Extensions/EnumExtensions.cs.meta +11 -3
- package/Runtime/Core/Extensions/IListExtensions.cs.meta +11 -3
- package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -12
- package/Runtime/Core/Extensions/MessageExtensions.cs +0 -60
- package/Runtime/Core/Extensions/MessageExtensions.cs.meta +11 -11
- package/Runtime/Core/Extensions.meta +8 -8
- package/Runtime/Core/Helper/MessageCache.cs +32 -0
- package/Runtime/Core/Helper/MessageCache.cs.meta +11 -3
- package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +11 -3
- package/Runtime/Core/Helper.meta +2 -2
- package/Runtime/Core/IMessage.cs +3 -3
- package/Runtime/Core/IMessage.cs.meta +11 -11
- package/Runtime/Core/InstanceId.cs +25 -1
- package/Runtime/Core/InstanceId.cs.meta +11 -11
- package/Runtime/Core/Internal/DxUnsafe.cs +60 -0
- package/Runtime/Core/Internal/DxUnsafe.cs.meta +11 -0
- package/Runtime/Core/Internal/FlatDispatch.cs +198 -0
- package/Runtime/Core/Internal/FlatDispatch.cs.meta +11 -0
- package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs +38 -0
- package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs.meta +11 -0
- package/Runtime/Core/Internal/TypedSlotIndex.cs +81 -0
- package/Runtime/Core/Internal/TypedSlotIndex.cs.meta +11 -0
- package/Runtime/Core/Internal/TypedSlots.cs +597 -0
- package/Runtime/Core/Internal/TypedSlots.cs.meta +11 -0
- package/Runtime/Core/Internal.meta +9 -0
- package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +11 -11
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageBus.cs +189 -15
- package/Runtime/Core/MessageBus/IMessageBus.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +1 -0
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -11
- package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs +16 -0
- package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs +40 -0
- package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/BusSlots.cs +719 -0
- package/Runtime/Core/MessageBus/Internal/BusSlots.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/DispatchKind.cs +38 -0
- package/Runtime/Core/MessageBus/Internal/DispatchKind.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs +20 -0
- package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs +28 -0
- package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs +48 -0
- package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/ISweepable.cs +15 -0
- package/Runtime/Core/MessageBus/Internal/ISweepable.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs +222 -0
- package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/SlotKey.cs +192 -0
- package/Runtime/Core/MessageBus/Internal/SlotKey.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal.meta +9 -0
- package/Runtime/Core/MessageBus/MessageBus.cs +5366 -3838
- package/Runtime/Core/MessageBus/MessageBus.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +187 -14
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +11 -11
- package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +11 -11
- package/Runtime/Core/MessageBus.meta +8 -8
- package/Runtime/Core/MessageHandler.cs +2399 -1042
- package/Runtime/Core/MessageHandler.cs.meta +11 -11
- package/Runtime/Core/MessageRegistrationHandle.cs.meta +11 -11
- package/Runtime/Core/MessageRegistrationToken.cs +429 -44
- package/Runtime/Core/MessageRegistrationToken.cs.meta +11 -11
- package/Runtime/Core/Messages/GlobalStringMessage.cs.meta +11 -3
- package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/ITargetedMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/ReflexiveMessage.cs.meta +11 -3
- package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/StringMessage.cs.meta +11 -3
- package/Runtime/Core/Messages.meta +8 -8
- package/Runtime/Core/MessagingDebug.cs.meta +11 -11
- package/Runtime/Core/Pooling/CollectionPool.cs +266 -0
- package/Runtime/Core/Pooling/CollectionPool.cs.meta +11 -0
- package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs +30 -0
- package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs.meta +11 -0
- package/Runtime/Core/Pooling/DxPools.cs +157 -0
- package/Runtime/Core/Pooling/DxPools.cs.meta +11 -0
- package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs +106 -0
- package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs.meta +11 -0
- package/Runtime/Core/Pooling/IDxMessagingClock.cs +18 -0
- package/Runtime/Core/Pooling/IDxMessagingClock.cs.meta +11 -0
- package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs +55 -0
- package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs.meta +11 -0
- package/Runtime/Core/Pooling/StopwatchClock.cs +27 -0
- package/Runtime/Core/Pooling/StopwatchClock.cs.meta +11 -0
- package/Runtime/Core/Pooling/UnityRealtimeClock.cs +31 -0
- package/Runtime/Core/Pooling/UnityRealtimeClock.cs.meta +11 -0
- package/Runtime/Core/Pooling.meta +9 -0
- package/Runtime/Core.meta +8 -8
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -12
- package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +11 -11
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -12
- package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +11 -3
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -11
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -20
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -7
- package/Runtime/Unity/Integrations/Reflex.meta +8 -8
- package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +11 -3
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +109 -1
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -11
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -30
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -7
- package/Runtime/Unity/Integrations/VContainer.meta +8 -8
- package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +11 -3
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -30
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -7
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +79 -1
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -11
- package/Runtime/Unity/Integrations/Zenject.meta +8 -8
- package/Runtime/Unity/Integrations.meta +8 -8
- package/Runtime/Unity/MessageAwareComponent.cs +74 -0
- package/Runtime/Unity/MessageAwareComponent.cs.meta +11 -11
- package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -12
- package/Runtime/Unity/MessagingComponent.cs +43 -10
- package/Runtime/Unity/MessagingComponent.cs.meta +11 -11
- package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -12
- package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -12
- package/Runtime/Unity.meta +8 -8
- package/Runtime/WallstopStudios.DxMessaging.asmdef +14 -14
- package/Runtime/WallstopStudios.DxMessaging.asmdef.meta +7 -7
- package/Runtime.meta +8 -8
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -98
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -7
- package/Samples~/DI/Prefabs.meta +8 -8
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -14
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -8
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -14
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -8
- package/Samples~/DI/Providers.meta +8 -8
- package/Samples~/DI/README.md +51 -51
- package/Samples~/DI/README.md.meta +7 -7
- package/Samples~/DI/Reflex/SampleInstaller.cs +7 -0
- package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -11
- package/Samples~/DI/Reflex.meta +8 -8
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs +6 -1
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -11
- package/Samples~/DI/VContainer.meta +8 -8
- package/Samples~/DI/Zenject/SampleInstaller.cs +8 -0
- package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -11
- package/Samples~/DI/Zenject.meta +8 -8
- package/Samples~/DI.meta +8 -8
- package/Samples~/Mini Combat/Boot.cs.meta +11 -11
- package/Samples~/Mini Combat/Enemy.cs.meta +11 -11
- package/Samples~/Mini Combat/Messages.cs.meta +11 -11
- package/Samples~/Mini Combat/Player.cs.meta +11 -11
- package/Samples~/Mini Combat/README.md +324 -323
- package/Samples~/Mini Combat/README.md.meta +7 -7
- package/Samples~/Mini Combat/UIOverlay.cs.meta +11 -11
- package/Samples~/Mini Combat/Walkthrough.md +430 -430
- package/Samples~/Mini Combat/Walkthrough.md.meta +7 -7
- package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef +13 -13
- package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef.meta +7 -7
- package/Samples~/Mini Combat.meta +8 -8
- package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/Messages.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/README.md +210 -209
- package/Samples~/UI Buttons + Inspector/README.md.meta +7 -7
- package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef +13 -13
- package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef.meta +7 -7
- package/Samples~/UI Buttons + Inspector.meta +8 -8
- package/SourceGenerators/Directory.Build.props +50 -3
- package/SourceGenerators/Directory.Build.props.meta +7 -7
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs +96 -63
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +11 -11
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +745 -87
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +11 -3
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +39 -46
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj.meta +7 -7
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.meta +8 -8
- package/SourceGenerators/global.json +7 -0
- package/SourceGenerators/global.json.meta +7 -0
- package/SourceGenerators.meta +8 -8
- package/Third Party Notices.md +3 -3
- package/Third Party Notices.md.meta +7 -7
- package/package.json +102 -92
- package/package.json.meta +7 -7
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
namespace DxMessaging.Core.MessageBus.Internal
|
|
2
|
+
{
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.Diagnostics;
|
|
5
|
+
using System.Runtime.CompilerServices;
|
|
6
|
+
using DxMessaging.Core;
|
|
7
|
+
using DxMessaging.Core.Internal;
|
|
8
|
+
using DxMessaging.Core.Pooling;
|
|
9
|
+
|
|
10
|
+
/// <summary>
|
|
11
|
+
/// Per-priority leaf storage for a single dispatch slot. Mirrors the
|
|
12
|
+
/// per-priority bucket previously held inside the legacy nested
|
|
13
|
+
/// <c>HandlerCache</c> type previously declared in <see cref="MessageBus"/>:
|
|
14
|
+
/// a handler set keyed by <see cref="MessageHandler"/> with insertion-order
|
|
15
|
+
/// tracking via the integer payload (priority slot index), plus a flat
|
|
16
|
+
/// cache list for snapshot-friendly iteration.
|
|
17
|
+
/// </summary>
|
|
18
|
+
/// <remarks>
|
|
19
|
+
/// <para>
|
|
20
|
+
/// <see cref="BusPriorityBucket"/> is intentionally NOT an
|
|
21
|
+
/// <see cref="IEvictableSlot"/>: it is only ever owned by a
|
|
22
|
+
/// <see cref="BusSinkSlot"/> and is reclaimed transitively when its parent
|
|
23
|
+
/// is reset. Eviction never targets a bucket directly.
|
|
24
|
+
/// </para>
|
|
25
|
+
/// <para>
|
|
26
|
+
/// The <see cref="version"/> / <see cref="lastSeenVersion"/> /
|
|
27
|
+
/// <see cref="lastSeenEmissionId"/> triple is the same staged-dispatch
|
|
28
|
+
/// snapshot mechanism used by the legacy <c>HandlerCache</c> -- structural
|
|
29
|
+
/// mutations bump <see cref="version"/>, dispatchers compare against
|
|
30
|
+
/// <see cref="lastSeenVersion"/> to decide whether to re-snapshot.
|
|
31
|
+
/// </para>
|
|
32
|
+
/// </remarks>
|
|
33
|
+
internal sealed class BusPriorityBucket
|
|
34
|
+
{
|
|
35
|
+
/// <summary>
|
|
36
|
+
/// Live handlers in this priority bucket. Value is the priority slot
|
|
37
|
+
/// index used by the staged dispatch snapshot pattern (matches the
|
|
38
|
+
/// legacy <c>HandlerCache.handlers</c> layout).
|
|
39
|
+
/// </summary>
|
|
40
|
+
public readonly Dictionary<MessageHandler, int> handlers = new();
|
|
41
|
+
|
|
42
|
+
/// <summary>
|
|
43
|
+
/// Flat snapshot-friendly cache of <see cref="handlers"/> keys; rebuilt
|
|
44
|
+
/// lazily by the dispatcher when <see cref="version"/> changes.
|
|
45
|
+
/// </summary>
|
|
46
|
+
public readonly List<MessageHandler> cache = new();
|
|
47
|
+
|
|
48
|
+
/// <summary>Monotonic version counter for the bucket contents.</summary>
|
|
49
|
+
public long version;
|
|
50
|
+
|
|
51
|
+
/// <summary>
|
|
52
|
+
/// The <see cref="version"/> value observed by the most recent
|
|
53
|
+
/// dispatcher snapshot. Used to decide whether <see cref="cache"/> needs
|
|
54
|
+
/// to be re-materialized before the next dispatch.
|
|
55
|
+
/// </summary>
|
|
56
|
+
public long lastSeenVersion = -1;
|
|
57
|
+
|
|
58
|
+
/// <summary>
|
|
59
|
+
/// The bus emission id of the most recent dispatch that consumed this
|
|
60
|
+
/// bucket. Used by the staged dispatch staleness check.
|
|
61
|
+
/// </summary>
|
|
62
|
+
public long lastSeenEmissionId = -1;
|
|
63
|
+
|
|
64
|
+
/// <summary>
|
|
65
|
+
/// Clear all bucket state. Mirrors the legacy
|
|
66
|
+
/// <c>HandlerCache.Clear()</c> body -- empties handlers and cache and
|
|
67
|
+
/// resets the dispatch-snapshot counters. Resets <see cref="version"/>
|
|
68
|
+
/// to <c>0</c>; this is the legacy "full reset" semantic and is NOT
|
|
69
|
+
/// monotonic. Eviction-driven reset semantics live on the parent
|
|
70
|
+
/// <see cref="BusSinkSlot.Reset"/>, which preserves monotonicity by
|
|
71
|
+
/// bumping the parent slot's version after clearing buckets.
|
|
72
|
+
/// </summary>
|
|
73
|
+
public void Clear()
|
|
74
|
+
{
|
|
75
|
+
handlers.Clear();
|
|
76
|
+
cache.Clear();
|
|
77
|
+
version = 0;
|
|
78
|
+
lastSeenVersion = -1;
|
|
79
|
+
lastSeenEmissionId = -1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// <summary>
|
|
84
|
+
/// Per-message-type, per-context dispatch slot. Replaces the inner
|
|
85
|
+
/// <c>HandlerCache<int, HandlerCache></c> type previously declared in
|
|
86
|
+
/// <see cref="MessageBus"/>. Holds the priority-keyed map of
|
|
87
|
+
/// <see cref="BusPriorityBucket"/>s and the flat ordered-priority list used
|
|
88
|
+
/// by the staged dispatch snapshot pattern.
|
|
89
|
+
/// </summary>
|
|
90
|
+
/// <remarks>
|
|
91
|
+
/// <para>
|
|
92
|
+
/// In the new layout each <see cref="BusSinkSlot"/> belongs to either the
|
|
93
|
+
/// scalar slot grid (<c>WithoutContext</c> variants -- no
|
|
94
|
+
/// <see cref="InstanceId"/> hash on the hot path) or the inner map of a
|
|
95
|
+
/// <see cref="BusContextSlot"/> (variants that carry an
|
|
96
|
+
/// <see cref="InstanceId"/> recipient or source).
|
|
97
|
+
/// </para>
|
|
98
|
+
/// <para>
|
|
99
|
+
/// The <see cref="dispatchState"/> field carries the staged Stage/Acquire
|
|
100
|
+
/// snapshot for this slot.
|
|
101
|
+
/// </para>
|
|
102
|
+
/// </remarks>
|
|
103
|
+
internal sealed class BusSinkSlot : IEvictableSlot
|
|
104
|
+
{
|
|
105
|
+
/// <summary>
|
|
106
|
+
/// Per-priority handler buckets, keyed by priority value.
|
|
107
|
+
/// </summary>
|
|
108
|
+
public readonly Dictionary<int, BusPriorityBucket> handlersByPriority = new();
|
|
109
|
+
|
|
110
|
+
/// <summary>
|
|
111
|
+
/// Insertion-ordered list of priority keys present in
|
|
112
|
+
/// <see cref="handlersByPriority"/>. Mirrors the legacy
|
|
113
|
+
/// <c>HandlerCache.order</c> field.
|
|
114
|
+
/// </summary>
|
|
115
|
+
public readonly List<int> orderedPriorities = new();
|
|
116
|
+
|
|
117
|
+
/// <summary>
|
|
118
|
+
/// Flat snapshot-friendly cache of <see cref="handlersByPriority"/>
|
|
119
|
+
/// entries; rebuilt lazily by the dispatcher when <see cref="version"/>
|
|
120
|
+
/// changes. Mirrors the legacy <c>HandlerCache.cache</c> field.
|
|
121
|
+
/// </summary>
|
|
122
|
+
public readonly List<KeyValuePair<int, BusPriorityBucket>> cache = new();
|
|
123
|
+
|
|
124
|
+
/// <summary>Monotonic version counter for the slot's structural state.</summary>
|
|
125
|
+
public long version;
|
|
126
|
+
|
|
127
|
+
/// <summary>
|
|
128
|
+
/// The <see cref="version"/> value observed by the most recent
|
|
129
|
+
/// dispatcher snapshot. Used to decide whether <see cref="cache"/>
|
|
130
|
+
/// needs to be re-materialized before the next dispatch.
|
|
131
|
+
/// </summary>
|
|
132
|
+
public long lastSeenVersion = -1;
|
|
133
|
+
|
|
134
|
+
/// <summary>
|
|
135
|
+
/// The bus emission id of the most recent dispatch that consumed this
|
|
136
|
+
/// slot. Used by the staged dispatch staleness check.
|
|
137
|
+
/// </summary>
|
|
138
|
+
public long lastSeenEmissionId = -1;
|
|
139
|
+
|
|
140
|
+
/// <summary>
|
|
141
|
+
/// Bus tick counter value at the most recent register / deregister /
|
|
142
|
+
/// emit that touched this slot. Maintained by the sweep touch hook;
|
|
143
|
+
/// preserved across <see cref="Clear"/> and <see cref="Reset"/> so the
|
|
144
|
+
/// sweep can distinguish never-touched slots from freshly-reset slots.
|
|
145
|
+
/// </summary>
|
|
146
|
+
public long lastTouchTicks;
|
|
147
|
+
|
|
148
|
+
/// <summary>
|
|
149
|
+
/// <para>
|
|
150
|
+
/// Reserved live-handler counter intended to mirror the unique
|
|
151
|
+
/// (<see cref="MessageHandler"/>, priority) pair count across every
|
|
152
|
+
/// entry in <see cref="handlersByPriority"/>, so <see cref="IsEmpty"/>
|
|
153
|
+
/// becomes a single integer compare rather than a walk over priority
|
|
154
|
+
/// buckets. This counter is reserved until <see cref="BusSinkSlot"/> is
|
|
155
|
+
/// used as the typed-sink storage type. <see cref="IsEmpty"/> currently
|
|
156
|
+
/// returns <c>true</c> at all times because no writer increments
|
|
157
|
+
/// <see cref="liveCount"/>.
|
|
158
|
+
/// </para>
|
|
159
|
+
/// <para>
|
|
160
|
+
/// Intended transitions once wired: re-registration of an existing
|
|
161
|
+
/// pair will be a no-op on this counter; only newly-inserted pairs
|
|
162
|
+
/// will increment it, and only the final removal of a pair will
|
|
163
|
+
/// decrement it.
|
|
164
|
+
/// </para>
|
|
165
|
+
/// </summary>
|
|
166
|
+
public int liveCount;
|
|
167
|
+
|
|
168
|
+
/// <summary>
|
|
169
|
+
/// Per-slot dispatch state for the staged Stage/Acquire snapshot
|
|
170
|
+
/// pattern. Single field per slot -- the previous per-slot-key
|
|
171
|
+
/// dictionary keyed by dispatch slot is unnecessary once
|
|
172
|
+
/// each slot maps 1:1 to a <see cref="SlotKey"/>. Lazy alloc on
|
|
173
|
+
/// first Stage/Acquire; null after Reset(). This field is
|
|
174
|
+
/// forward-compatible plumbing. Wiring lands when
|
|
175
|
+
/// <see cref="BusSinkSlot"/> becomes the storage type backing the
|
|
176
|
+
/// typed-sink hot dispatch path (replacing the current legacy
|
|
177
|
+
/// <c>HandlerCache<int, HandlerCache></c> generic outer +
|
|
178
|
+
/// non-generic inner pair). The intermediate phases that retire
|
|
179
|
+
/// the legacy category enum and split the Handle-phase variants
|
|
180
|
+
/// do NOT touch this field; they continue working through the
|
|
181
|
+
/// legacy storage type's <c>dispatchState</c>.
|
|
182
|
+
/// </summary>
|
|
183
|
+
public MessageBus.DispatchState dispatchState;
|
|
184
|
+
|
|
185
|
+
/// <inheritdoc />
|
|
186
|
+
public long LastTouchTicks
|
|
187
|
+
{
|
|
188
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
189
|
+
get => lastTouchTicks;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// <inheritdoc />
|
|
193
|
+
public bool IsEmpty
|
|
194
|
+
{
|
|
195
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
196
|
+
get => liveCount == 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/// <inheritdoc />
|
|
200
|
+
public long Version
|
|
201
|
+
{
|
|
202
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
203
|
+
get => version;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/// <summary>
|
|
207
|
+
/// Full-reset semantic that mirrors the legacy
|
|
208
|
+
/// <c>HandlerCache<TKey, TValue>.Clear()</c> body. Clears all
|
|
209
|
+
/// priority buckets and the outer maps, and resets the
|
|
210
|
+
/// staged-dispatch snapshot counters.
|
|
211
|
+
/// Resets <see cref="version"/> to <c>0</c>; this is NOT monotonic and
|
|
212
|
+
/// is intended only for the bus-wide
|
|
213
|
+
/// <c>MessageBus.ResetState()</c> code path. Use <see cref="Reset"/>
|
|
214
|
+
/// for sweep-driven slot reclamation.
|
|
215
|
+
/// </summary>
|
|
216
|
+
public void Clear()
|
|
217
|
+
{
|
|
218
|
+
foreach (BusPriorityBucket bucket in handlersByPriority.Values)
|
|
219
|
+
{
|
|
220
|
+
bucket.Clear();
|
|
221
|
+
}
|
|
222
|
+
handlersByPriority.Clear();
|
|
223
|
+
orderedPriorities.Clear();
|
|
224
|
+
cache.Clear();
|
|
225
|
+
dispatchState?.Reset();
|
|
226
|
+
dispatchState = null;
|
|
227
|
+
version = 0;
|
|
228
|
+
lastSeenVersion = -1;
|
|
229
|
+
lastSeenEmissionId = -1;
|
|
230
|
+
liveCount = 0;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/// <summary>
|
|
234
|
+
/// Eviction-driven reset. Clears all structural state without touching
|
|
235
|
+
/// <see cref="version"/>, then bumps <see cref="version"/> as the LAST
|
|
236
|
+
/// step so any captured dispatch closure that observed the prior
|
|
237
|
+
/// version detects invalidation. <see cref="lastTouchTicks"/> is
|
|
238
|
+
/// intentionally preserved so the sweep can distinguish freshly-reset
|
|
239
|
+
/// slots from never-touched ones.
|
|
240
|
+
/// </summary>
|
|
241
|
+
public void Reset()
|
|
242
|
+
{
|
|
243
|
+
// Inline the structural-clear body of Clear(); do NOT call Clear()
|
|
244
|
+
// because that resets version=0 and would break the monotonic
|
|
245
|
+
// invariant the eviction layer depends on: stale deregister
|
|
246
|
+
// closures captured before reset must observe a strictly larger
|
|
247
|
+
// version after reset and skip their work.
|
|
248
|
+
foreach (BusPriorityBucket bucket in handlersByPriority.Values)
|
|
249
|
+
{
|
|
250
|
+
bucket.Clear();
|
|
251
|
+
}
|
|
252
|
+
handlersByPriority.Clear();
|
|
253
|
+
orderedPriorities.Clear();
|
|
254
|
+
cache.Clear();
|
|
255
|
+
dispatchState?.Reset();
|
|
256
|
+
dispatchState = null;
|
|
257
|
+
lastSeenVersion = -1;
|
|
258
|
+
lastSeenEmissionId = -1;
|
|
259
|
+
liveCount = 0;
|
|
260
|
+
unchecked
|
|
261
|
+
{
|
|
262
|
+
++version;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// <summary>
|
|
268
|
+
/// Per-message-type context-bound dispatch slot. Owns the
|
|
269
|
+
/// <see cref="InstanceId"/>-keyed map of inner <see cref="BusSinkSlot"/>s
|
|
270
|
+
/// for one message type's targeted or broadcast variants. Replaces the
|
|
271
|
+
/// outer per-message-type dictionaries previously held in the
|
|
272
|
+
/// targeted/broadcast sink fields on <see cref="MessageBus"/>.
|
|
273
|
+
/// </summary>
|
|
274
|
+
/// <remarks>
|
|
275
|
+
/// <para>
|
|
276
|
+
/// The inner map is rented from
|
|
277
|
+
/// <see cref="DxPools.InstanceIdDicts"/>. The pool stores
|
|
278
|
+
/// <c>Dictionary<InstanceId, object></c> -- generic-erased to share a
|
|
279
|
+
/// single pool across every message-type instantiation. Each value is a
|
|
280
|
+
/// <see cref="BusSinkSlot"/>, accessed via
|
|
281
|
+
/// <see cref="DxUnsafe.As{T}(object)"/>; the class is sealed and only inserted
|
|
282
|
+
/// from this type's own methods, so the cast cannot encounter a foreign
|
|
283
|
+
/// runtime type. <c>DEBUG</c> builds verify the invariant at every
|
|
284
|
+
/// cast site.
|
|
285
|
+
/// </para>
|
|
286
|
+
/// <para>
|
|
287
|
+
/// The map is left null until first registration so empty slots cost only
|
|
288
|
+
/// the field set itself. <see cref="Clear"/> empties the map in place but
|
|
289
|
+
/// does NOT return it to the pool; <see cref="Reset"/> returns the map to
|
|
290
|
+
/// the pool and nulls the field.
|
|
291
|
+
/// </para>
|
|
292
|
+
/// </remarks>
|
|
293
|
+
internal sealed class BusContextSlot : IEvictableSlot
|
|
294
|
+
{
|
|
295
|
+
/// <summary>
|
|
296
|
+
/// Inner per-context map. Null until first registration. Values are
|
|
297
|
+
/// <see cref="BusSinkSlot"/> instances stored as <see cref="object"/>
|
|
298
|
+
/// so the underlying dictionary can be pooled in the shared
|
|
299
|
+
/// <see cref="DxPools.InstanceIdDicts"/> pool.
|
|
300
|
+
/// </summary>
|
|
301
|
+
public Dictionary<InstanceId, object> byContext;
|
|
302
|
+
|
|
303
|
+
/// <summary>Monotonic version counter for the slot's structural state.</summary>
|
|
304
|
+
public long version;
|
|
305
|
+
|
|
306
|
+
/// <summary>
|
|
307
|
+
/// Bus tick counter value at the most recent register / deregister /
|
|
308
|
+
/// emit that touched this slot. Maintained by the sweep touch hook;
|
|
309
|
+
/// preserved across <see cref="Clear"/> and <see cref="Reset"/>.
|
|
310
|
+
/// </summary>
|
|
311
|
+
public long lastTouchTicks;
|
|
312
|
+
|
|
313
|
+
/// <summary>
|
|
314
|
+
/// <para>
|
|
315
|
+
/// Reserved live-context counter intended to mirror the count of
|
|
316
|
+
/// <see cref="InstanceId"/> keys in <see cref="byContext"/> that
|
|
317
|
+
/// currently retain at least one live handler, so <see cref="IsEmpty"/>
|
|
318
|
+
/// becomes a single integer compare rather than a recursive walk over
|
|
319
|
+
/// the inner per-context slots. This counter is reserved until
|
|
320
|
+
/// <see cref="BusContextSlot"/> is used as the typed-sink storage type.
|
|
321
|
+
/// <see cref="IsEmpty"/> currently returns <c>true</c> at all times
|
|
322
|
+
/// because no writer increments <see cref="liveCount"/>.
|
|
323
|
+
/// </para>
|
|
324
|
+
/// <para>
|
|
325
|
+
/// Intended transitions once wired: the bus will increment by 1 when
|
|
326
|
+
/// a context goes from zero handlers to one, and decrement by 1 when
|
|
327
|
+
/// a context drops back to zero handlers (and is removed via
|
|
328
|
+
/// <see cref="RemoveContext"/>); registering or deregistering inside
|
|
329
|
+
/// an already-live context will not adjust this counter.
|
|
330
|
+
/// (<see cref="BusSinkSlot.liveCount"/> is the per-context handler
|
|
331
|
+
/// count; this is the per-slot context count.)
|
|
332
|
+
/// </para>
|
|
333
|
+
/// </summary>
|
|
334
|
+
public int liveCount;
|
|
335
|
+
|
|
336
|
+
/// <inheritdoc />
|
|
337
|
+
public long LastTouchTicks
|
|
338
|
+
{
|
|
339
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
340
|
+
get => lastTouchTicks;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/// <inheritdoc />
|
|
344
|
+
public bool IsEmpty
|
|
345
|
+
{
|
|
346
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
347
|
+
get => liveCount == 0;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/// <inheritdoc />
|
|
351
|
+
public long Version
|
|
352
|
+
{
|
|
353
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
354
|
+
get => version;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/// <summary>
|
|
358
|
+
/// Look up the inner <see cref="BusSinkSlot"/> for the supplied
|
|
359
|
+
/// context. Returns <c>false</c> when <see cref="byContext"/> is null
|
|
360
|
+
/// or the context is not present.
|
|
361
|
+
/// </summary>
|
|
362
|
+
/// <param name="context">The <see cref="InstanceId"/> context key.</param>
|
|
363
|
+
/// <param name="slot">
|
|
364
|
+
/// The inner slot when present; <c>null</c> otherwise.
|
|
365
|
+
/// </param>
|
|
366
|
+
/// <returns><c>true</c> when a slot was found.</returns>
|
|
367
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
368
|
+
public bool TryGetSlot(InstanceId context, out BusSinkSlot slot)
|
|
369
|
+
{
|
|
370
|
+
Dictionary<InstanceId, object> map = byContext;
|
|
371
|
+
if (map == null)
|
|
372
|
+
{
|
|
373
|
+
slot = null;
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
if (!map.TryGetValue(context, out object boxed))
|
|
377
|
+
{
|
|
378
|
+
slot = null;
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
DebugAssertSlot(boxed);
|
|
382
|
+
slot = DxUnsafe.As<BusSinkSlot>(boxed);
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/// <summary>
|
|
387
|
+
/// Look up or create the inner <see cref="BusSinkSlot"/> for the
|
|
388
|
+
/// supplied context. Lazily rents the inner map from
|
|
389
|
+
/// <see cref="DxPools.InstanceIdDicts"/> on first use.
|
|
390
|
+
/// </summary>
|
|
391
|
+
/// <param name="context">The <see cref="InstanceId"/> context key.</param>
|
|
392
|
+
/// <returns>
|
|
393
|
+
/// The existing or freshly-allocated <see cref="BusSinkSlot"/> for
|
|
394
|
+
/// <paramref name="context"/>.
|
|
395
|
+
/// </returns>
|
|
396
|
+
public BusSinkSlot GetOrAddSlot(InstanceId context)
|
|
397
|
+
{
|
|
398
|
+
Dictionary<InstanceId, object> map = byContext;
|
|
399
|
+
if (map == null)
|
|
400
|
+
{
|
|
401
|
+
map = DxPools.InstanceIdDicts.Rent();
|
|
402
|
+
byContext = map;
|
|
403
|
+
}
|
|
404
|
+
if (map.TryGetValue(context, out object boxed))
|
|
405
|
+
{
|
|
406
|
+
DebugAssertSlot(boxed);
|
|
407
|
+
return DxUnsafe.As<BusSinkSlot>(boxed);
|
|
408
|
+
}
|
|
409
|
+
BusSinkSlot slot = new BusSinkSlot();
|
|
410
|
+
map[context] = slot;
|
|
411
|
+
return slot;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/// <summary>
|
|
415
|
+
/// Remove the inner slot for the supplied context, if present. Returns
|
|
416
|
+
/// <c>true</c> when an entry was removed. The removed inner slot is NOT
|
|
417
|
+
/// reset by this method -- callers wanting to reclaim it should call
|
|
418
|
+
/// <see cref="BusSinkSlot.Reset"/> on the returned reference before
|
|
419
|
+
/// dropping it.
|
|
420
|
+
/// </summary>
|
|
421
|
+
/// <param name="context">The <see cref="InstanceId"/> context key.</param>
|
|
422
|
+
/// <returns>
|
|
423
|
+
/// <c>true</c> when the context was present in <see cref="byContext"/>.
|
|
424
|
+
/// </returns>
|
|
425
|
+
/// <remarks>
|
|
426
|
+
/// The caller (the bus) is responsible for adjusting
|
|
427
|
+
/// <see cref="liveCount"/> after a successful removal. This method
|
|
428
|
+
/// intentionally does not touch <see cref="liveCount"/> so the bus can
|
|
429
|
+
/// decide the right semantic at the call site (<see cref="InstanceId"/>
|
|
430
|
+
/// keys vs handler sum -- see the <see cref="liveCount"/> field
|
|
431
|
+
/// docstring).
|
|
432
|
+
/// </remarks>
|
|
433
|
+
public bool RemoveContext(InstanceId context)
|
|
434
|
+
{
|
|
435
|
+
Dictionary<InstanceId, object> map = byContext;
|
|
436
|
+
if (map == null)
|
|
437
|
+
{
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
return map.Remove(context);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/// <summary>
|
|
444
|
+
/// Full-reset semantic. Recursively clears every inner
|
|
445
|
+
/// <see cref="BusSinkSlot"/> in place via
|
|
446
|
+
/// <see cref="BusSinkSlot.Clear"/> (deeper than the legacy
|
|
447
|
+
/// <c>HandlerCache<TKey, TValue>.Clear()</c> body, which relied
|
|
448
|
+
/// on GC of dropped entries) and empties the outer map without
|
|
449
|
+
/// returning it to the pool. Resets <see cref="version"/> to <c>0</c>;
|
|
450
|
+
/// this is NOT
|
|
451
|
+
/// monotonic and is intended only for the bus-wide
|
|
452
|
+
/// <c>MessageBus.ResetState()</c> code path. Use <see cref="Reset"/>
|
|
453
|
+
/// for sweep-driven slot reclamation.
|
|
454
|
+
/// </summary>
|
|
455
|
+
public void Clear()
|
|
456
|
+
{
|
|
457
|
+
Dictionary<InstanceId, object> map = byContext;
|
|
458
|
+
if (map != null)
|
|
459
|
+
{
|
|
460
|
+
foreach (object boxed in map.Values)
|
|
461
|
+
{
|
|
462
|
+
if (boxed == null)
|
|
463
|
+
{
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
DebugAssertSlot(boxed);
|
|
467
|
+
DxUnsafe.As<BusSinkSlot>(boxed).Clear();
|
|
468
|
+
}
|
|
469
|
+
map.Clear();
|
|
470
|
+
}
|
|
471
|
+
version = 0;
|
|
472
|
+
liveCount = 0;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/// <summary>
|
|
476
|
+
/// Eviction-driven reset. Walks every inner <see cref="BusSinkSlot"/>
|
|
477
|
+
/// and calls <see cref="BusSinkSlot.Reset"/> on each. Inner pooled
|
|
478
|
+
/// state must be drained BEFORE the outer map is recycled. Then returns
|
|
479
|
+
/// <see cref="byContext"/> to the
|
|
480
|
+
/// shared <see cref="DxPools.InstanceIdDicts"/> pool and nulls the
|
|
481
|
+
/// field. Bumps <see cref="version"/> as the LAST step so any captured
|
|
482
|
+
/// dispatch closure that observed the prior version detects
|
|
483
|
+
/// invalidation. <see cref="lastTouchTicks"/> is intentionally
|
|
484
|
+
/// preserved.
|
|
485
|
+
/// </summary>
|
|
486
|
+
public void Reset()
|
|
487
|
+
{
|
|
488
|
+
Dictionary<InstanceId, object> map = byContext;
|
|
489
|
+
if (map != null)
|
|
490
|
+
{
|
|
491
|
+
foreach (object boxed in map.Values)
|
|
492
|
+
{
|
|
493
|
+
if (boxed == null)
|
|
494
|
+
{
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
DebugAssertSlot(boxed);
|
|
498
|
+
DxUnsafe.As<BusSinkSlot>(boxed).Reset();
|
|
499
|
+
}
|
|
500
|
+
// Pool's onRecycled callback clears the dictionary before re-use.
|
|
501
|
+
DxPools.InstanceIdDicts.Return(map);
|
|
502
|
+
byContext = null;
|
|
503
|
+
}
|
|
504
|
+
liveCount = 0;
|
|
505
|
+
unchecked
|
|
506
|
+
{
|
|
507
|
+
++version;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
[Conditional("DEBUG")]
|
|
512
|
+
private static void DebugAssertSlot(object boxed)
|
|
513
|
+
{
|
|
514
|
+
Debug.Assert(
|
|
515
|
+
boxed is BusSinkSlot,
|
|
516
|
+
"BusContextSlot.byContext must only contain BusSinkSlot values; "
|
|
517
|
+
+ "DxUnsafe.As<BusSinkSlot> would otherwise produce undefined behavior."
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/// <summary>
|
|
523
|
+
/// Per-bus global accept-all slot. Replaces the legacy non-generic
|
|
524
|
+
/// <c>HandlerCache</c> previously declared in <see cref="MessageBus"/> --
|
|
525
|
+
/// the slot that holds the "subscribe to every emit" handlers.
|
|
526
|
+
/// </summary>
|
|
527
|
+
/// <remarks>
|
|
528
|
+
/// <para>
|
|
529
|
+
/// This slot models global accept-all handlers as one shared handler set
|
|
530
|
+
/// (<see cref="sharedHandlers"/> / <see cref="sharedCache"/>) and three
|
|
531
|
+
/// separate per-kind dispatch state fields
|
|
532
|
+
/// (<see cref="untargetedDispatchState"/>,
|
|
533
|
+
/// <see cref="targetedDispatchState"/>,
|
|
534
|
+
/// <see cref="broadcastDispatchState"/>). The discrete fields keep the
|
|
535
|
+
/// per-emission slot select branch-free under JIT monomorphization,
|
|
536
|
+
/// avoiding the dictionary lookup the legacy non-generic
|
|
537
|
+
/// <c>HandlerCache</c> imposed.
|
|
538
|
+
/// </para>
|
|
539
|
+
/// </remarks>
|
|
540
|
+
internal sealed class BusGlobalSlot : IEvictableSlot
|
|
541
|
+
{
|
|
542
|
+
/// <summary>
|
|
543
|
+
/// Live global handlers, keyed by handler with insertion order tracked
|
|
544
|
+
/// via the integer payload. Mirrors the legacy non-generic
|
|
545
|
+
/// <c>HandlerCache.handlers</c> field.
|
|
546
|
+
/// </summary>
|
|
547
|
+
public readonly Dictionary<MessageHandler, int> sharedHandlers = new();
|
|
548
|
+
|
|
549
|
+
/// <summary>
|
|
550
|
+
/// Reserved for global-slot snapshot iteration. Mirrors the legacy
|
|
551
|
+
/// non-generic <c>HandlerCache.cache</c> field, which was likewise
|
|
552
|
+
/// allocated for parity but never populated or read by any dispatch path.
|
|
553
|
+
/// Cleared by <see cref="Clear"/> and <see cref="Reset"/> as part of the
|
|
554
|
+
/// slot lifecycle.
|
|
555
|
+
/// </summary>
|
|
556
|
+
public readonly List<MessageHandler> sharedCache = new();
|
|
557
|
+
|
|
558
|
+
/// <summary>Monotonic version counter for the slot's structural state.</summary>
|
|
559
|
+
public long version;
|
|
560
|
+
|
|
561
|
+
/// <summary>
|
|
562
|
+
/// Reserved counter intended to record the <see cref="version"/> value
|
|
563
|
+
/// observed by the most recent dispatcher snapshot. Allocated for parity
|
|
564
|
+
/// with the per-cache <see cref="BusSinkSlot.lastSeenVersion"/> contract.
|
|
565
|
+
/// </summary>
|
|
566
|
+
public long lastSeenVersion = -1;
|
|
567
|
+
|
|
568
|
+
/// <summary>
|
|
569
|
+
/// Reserved counter intended to record the bus emission id of the
|
|
570
|
+
/// most recent dispatch that consumed this slot. Allocated for parity
|
|
571
|
+
/// with the per-cache <see cref="BusSinkSlot.lastSeenEmissionId"/>
|
|
572
|
+
/// contract.
|
|
573
|
+
/// </summary>
|
|
574
|
+
public long lastSeenEmissionId = -1;
|
|
575
|
+
|
|
576
|
+
/// <summary>
|
|
577
|
+
/// Bus tick counter value at the most recent register / deregister /
|
|
578
|
+
/// emit that touched this slot. Maintained by the sweep touch hook;
|
|
579
|
+
/// preserved across <see cref="Clear"/> and <see cref="Reset"/>.
|
|
580
|
+
/// </summary>
|
|
581
|
+
public long lastTouchTicks;
|
|
582
|
+
|
|
583
|
+
/// <summary>
|
|
584
|
+
/// <para>
|
|
585
|
+
/// Live-handler counter that mirrors <c>sharedHandlers.Count</c> at
|
|
586
|
+
/// every stable observation point. Maintained by the bus at the
|
|
587
|
+
/// register / deregister sites for <c>RegisterGlobalAcceptAll</c> so
|
|
588
|
+
/// <see cref="IsEmpty"/> is a single integer compare rather than a
|
|
589
|
+
/// dictionary-count read.
|
|
590
|
+
/// </para>
|
|
591
|
+
/// <para>
|
|
592
|
+
/// The invariant is <c>liveCount == sharedHandlers.Count</c>: only the
|
|
593
|
+
/// per-handler refcount's <c>0 -> 1</c> transition (newly-inserted
|
|
594
|
+
/// handler) increments <see cref="liveCount"/>, and only the
|
|
595
|
+
/// <c>1 -> 0</c> transition (final removal of a handler) decrements
|
|
596
|
+
/// it. Re-registering an already-present handler (refcount
|
|
597
|
+
/// <c>n -> n+1</c> for <c>n >= 1</c>) leaves the counter alone,
|
|
598
|
+
/// matching the dictionary's behaviour. Over-deregistration is a
|
|
599
|
+
/// no-op for both fields. <c>DEBUG</c> builds verify the invariant
|
|
600
|
+
/// after every register / deregister via
|
|
601
|
+
/// <c>MessageBus.DebugAssertGlobalLiveCount</c> and
|
|
602
|
+
/// <see cref="DebugAssertLiveCountInvariant"/>.
|
|
603
|
+
/// </para>
|
|
604
|
+
/// </summary>
|
|
605
|
+
public int liveCount;
|
|
606
|
+
|
|
607
|
+
/// <summary>
|
|
608
|
+
/// Dispatch state for the Untargeted-global emission path. One of the
|
|
609
|
+
/// three discrete per-kind fields. Separate slots over a per-kind
|
|
610
|
+
/// dictionary keep the per-emission
|
|
611
|
+
/// select branch-free under JIT monomorphization. Lazy alloc on first
|
|
612
|
+
/// Stage/Acquire; null after Reset().
|
|
613
|
+
/// </summary>
|
|
614
|
+
public MessageBus.DispatchState untargetedDispatchState;
|
|
615
|
+
|
|
616
|
+
/// <summary>
|
|
617
|
+
/// Dispatch state for the Targeted-global emission path. Sibling of
|
|
618
|
+
/// <see cref="untargetedDispatchState"/>; same lifetime semantics.
|
|
619
|
+
/// </summary>
|
|
620
|
+
public MessageBus.DispatchState targetedDispatchState;
|
|
621
|
+
|
|
622
|
+
/// <summary>
|
|
623
|
+
/// Dispatch state for the Broadcast-global emission path. Sibling of
|
|
624
|
+
/// <see cref="untargetedDispatchState"/>; same lifetime semantics.
|
|
625
|
+
/// </summary>
|
|
626
|
+
public MessageBus.DispatchState broadcastDispatchState;
|
|
627
|
+
|
|
628
|
+
/// <inheritdoc />
|
|
629
|
+
public long LastTouchTicks
|
|
630
|
+
{
|
|
631
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
632
|
+
get => lastTouchTicks;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/// <inheritdoc />
|
|
636
|
+
public bool IsEmpty
|
|
637
|
+
{
|
|
638
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
639
|
+
get => liveCount == 0;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/// <inheritdoc />
|
|
643
|
+
public long Version
|
|
644
|
+
{
|
|
645
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
646
|
+
get => version;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/// <summary>
|
|
650
|
+
/// Full-reset semantic that mirrors the legacy non-generic
|
|
651
|
+
/// <c>HandlerCache.Clear()</c> body. Clears
|
|
652
|
+
/// <see cref="sharedHandlers"/> and <see cref="sharedCache"/>
|
|
653
|
+
/// and resets the dispatch-snapshot counters. Resets
|
|
654
|
+
/// <see cref="version"/> to <c>0</c>; this is NOT monotonic and is
|
|
655
|
+
/// intended only for the bus-wide <c>MessageBus.ResetState()</c> code
|
|
656
|
+
/// path. Use <see cref="Reset"/> for sweep-driven slot reclamation.
|
|
657
|
+
/// </summary>
|
|
658
|
+
public void Clear()
|
|
659
|
+
{
|
|
660
|
+
sharedHandlers.Clear();
|
|
661
|
+
sharedCache.Clear();
|
|
662
|
+
untargetedDispatchState?.Reset();
|
|
663
|
+
untargetedDispatchState = null;
|
|
664
|
+
targetedDispatchState?.Reset();
|
|
665
|
+
targetedDispatchState = null;
|
|
666
|
+
broadcastDispatchState?.Reset();
|
|
667
|
+
broadcastDispatchState = null;
|
|
668
|
+
version = 0;
|
|
669
|
+
lastSeenVersion = -1;
|
|
670
|
+
lastSeenEmissionId = -1;
|
|
671
|
+
liveCount = 0;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/// <summary>
|
|
675
|
+
/// Eviction-driven reset. Clears all structural state without touching
|
|
676
|
+
/// <see cref="version"/>, then bumps <see cref="version"/> as the LAST
|
|
677
|
+
/// step so any captured dispatch closure that observed the prior
|
|
678
|
+
/// version detects invalidation. <see cref="lastTouchTicks"/> is
|
|
679
|
+
/// intentionally preserved.
|
|
680
|
+
/// </summary>
|
|
681
|
+
public void Reset()
|
|
682
|
+
{
|
|
683
|
+
// Inline the structural-clear body of Clear(); do NOT call Clear()
|
|
684
|
+
// because that resets version=0 and would break the monotonic
|
|
685
|
+
// invariant the eviction layer depends on.
|
|
686
|
+
sharedHandlers.Clear();
|
|
687
|
+
sharedCache.Clear();
|
|
688
|
+
untargetedDispatchState?.Reset();
|
|
689
|
+
untargetedDispatchState = null;
|
|
690
|
+
targetedDispatchState?.Reset();
|
|
691
|
+
targetedDispatchState = null;
|
|
692
|
+
broadcastDispatchState?.Reset();
|
|
693
|
+
broadcastDispatchState = null;
|
|
694
|
+
lastSeenVersion = -1;
|
|
695
|
+
lastSeenEmissionId = -1;
|
|
696
|
+
liveCount = 0;
|
|
697
|
+
unchecked
|
|
698
|
+
{
|
|
699
|
+
++version;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/// <summary>
|
|
704
|
+
/// Defensive <c>DEBUG</c>-only assertion that <see cref="liveCount"/>
|
|
705
|
+
/// equals <c>sharedHandlers.Count</c>. Provided so contract tests can
|
|
706
|
+
/// pin the invariant without exposing private bus state. Stripped in
|
|
707
|
+
/// Release builds via <see cref="ConditionalAttribute"/>.
|
|
708
|
+
/// </summary>
|
|
709
|
+
[Conditional("DEBUG")]
|
|
710
|
+
internal void DebugAssertLiveCountInvariant()
|
|
711
|
+
{
|
|
712
|
+
Debug.Assert(
|
|
713
|
+
liveCount == sharedHandlers.Count,
|
|
714
|
+
"BusGlobalSlot.liveCount must mirror sharedHandlers.Count at every "
|
|
715
|
+
+ "stable observation point."
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|