com.wallstop-studios.dxmessaging 2.2.0 → 3.0.1
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 +106 -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 +1122 -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 +33 -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 +3 -3
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +81 -0
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +11 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +420 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +11 -0
- package/Editor/CustomEditors/MessagingComponentEditor.cs +1 -1
- package/Editor/CustomEditors/MessagingComponentEditor.cs.meta +2 -2
- package/Editor/CustomEditors.meta +2 -2
- package/Editor/DxMessagingEditorInitializer.cs.meta +2 -2
- package/Editor/DxMessagingMenu.cs.meta +11 -11
- package/Editor/DxMessagingSceneBuildProcessor.cs.meta +11 -11
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +190 -0
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +11 -0
- package/Editor/Settings/DxMessagingSettings.cs +189 -0
- package/Editor/Settings/DxMessagingSettings.cs.meta +2 -2
- package/Editor/Settings/DxMessagingSettingsProvider.cs +50 -33
- package/Editor/Settings/DxMessagingSettingsProvider.cs.meta +2 -2
- package/Editor/Settings.meta +2 -2
- package/Editor/SetupCscRsp.cs +209 -8
- package/Editor/SetupCscRsp.cs.meta +2 -2
- package/Editor/Testing/MessagingComponentEditorHarness.cs +1 -1
- package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +3 -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 +941 -900
- package/README.md.meta +7 -7
- package/Runtime/AssemblyInfo.cs +4 -0
- package/Runtime/AssemblyInfo.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs +26 -0
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +11 -0
- package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs.meta +2 -2
- 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 +2 -2
- package/Runtime/Core/DataStructure/CyclicBuffer.cs.meta +2 -2
- package/Runtime/Core/DataStructure.meta +2 -2
- package/Runtime/Core/Diagnostics/MessageEmissionData.cs.meta +2 -2
- package/Runtime/Core/Diagnostics/MessageRegistrationData.cs.meta +2 -2
- package/Runtime/Core/Diagnostics/MessageRegistrationType.cs.meta +2 -2
- 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.meta +2 -2
- package/Runtime/Core/Extensions/IListExtensions.cs.meta +2 -2
- package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -12
- 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 +2 -2
- package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +2 -2
- 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.meta +11 -11
- package/Runtime/Core/Internal/TypedDispatchLinkIndex.cs +51 -0
- package/Runtime/Core/Internal/TypedDispatchLinkIndex.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 +613 -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 +177 -3
- package/Runtime/Core/MessageBus/IMessageBus.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -11
- 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 +718 -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 +2651 -500
- package/Runtime/Core/MessageBus/MessageBus.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -11
- 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 +2019 -542
- package/Runtime/Core/MessageHandler.cs.meta +11 -11
- package/Runtime/Core/MessageRegistrationHandle.cs.meta +11 -11
- package/Runtime/Core/MessageRegistrationToken.cs +7 -0
- package/Runtime/Core/MessageRegistrationToken.cs.meta +11 -11
- package/Runtime/Core/Messages/GlobalStringMessage.cs.meta +2 -2
- 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 +2 -2
- package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/StringMessage.cs.meta +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +29 -0
- package/Runtime/Unity/MessageAwareComponent.cs.meta +11 -11
- package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -12
- 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.meta +7 -7
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +11 -11
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +2 -2
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj.meta +7 -7
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.meta +8 -8
- package/SourceGenerators.meta +8 -8
- package/Third Party Notices.md +3 -3
- package/Third Party Notices.md.meta +7 -7
- package/package.json +115 -92
- package/package.json.meta +7 -7
|
@@ -3,6 +3,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3
3
|
using System;
|
|
4
4
|
using System.Buffers;
|
|
5
5
|
using System.Collections.Generic;
|
|
6
|
+
using System.Diagnostics;
|
|
6
7
|
using System.Linq.Expressions;
|
|
7
8
|
using System.Reflection;
|
|
8
9
|
using System.Runtime.CompilerServices;
|
|
@@ -11,9 +12,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
11
12
|
using DxMessaging.Core;
|
|
12
13
|
using Extensions;
|
|
13
14
|
using Helper;
|
|
15
|
+
using Internal;
|
|
14
16
|
using Messages;
|
|
17
|
+
using Pooling;
|
|
15
18
|
using static IMessageBus;
|
|
16
19
|
#if UNITY_2021_3_OR_NEWER
|
|
20
|
+
using Configuration;
|
|
17
21
|
using UnityEngine;
|
|
18
22
|
#endif
|
|
19
23
|
|
|
@@ -24,8 +28,9 @@ namespace DxMessaging.Core.MessageBus
|
|
|
24
28
|
{
|
|
25
29
|
private long _emissionId;
|
|
26
30
|
public long EmissionId => _emissionId;
|
|
31
|
+
internal long TickCounter => _tickCounter;
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
internal readonly struct PrefreezeDescriptor
|
|
29
34
|
{
|
|
30
35
|
public PrefreezeDescriptor(byte kind, int priority)
|
|
31
36
|
{
|
|
@@ -38,37 +43,120 @@ namespace DxMessaging.Core.MessageBus
|
|
|
38
43
|
public readonly int priority;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
private enum DispatchCategory : byte
|
|
42
|
-
{
|
|
43
|
-
None = 0,
|
|
44
|
-
Untargeted = 1,
|
|
45
|
-
UntargetedPost = 2,
|
|
46
|
-
Targeted = 3,
|
|
47
|
-
TargetedPost = 4,
|
|
48
|
-
TargetedWithoutTargeting = 5,
|
|
49
|
-
TargetedWithoutTargetingPost = 6,
|
|
50
|
-
Broadcast = 7,
|
|
51
|
-
BroadcastPost = 8,
|
|
52
|
-
BroadcastWithoutSource = 9,
|
|
53
|
-
BroadcastWithoutSourcePost = 10,
|
|
54
|
-
GlobalUntargeted = 11,
|
|
55
|
-
GlobalTargeted = 12,
|
|
56
|
-
GlobalBroadcast = 13,
|
|
57
|
-
}
|
|
58
|
-
|
|
59
46
|
private const byte PrefreezeKindNone = 0;
|
|
60
47
|
private const byte PrefreezeKindTargetedWithoutTargetingHandlers = 1;
|
|
61
48
|
private const byte PrefreezeKindBroadcastWithoutSourceHandlers = 2;
|
|
62
49
|
private const byte PrefreezeKindGlobalUntargetedHandlers = 3;
|
|
63
50
|
private const byte PrefreezeKindGlobalTargetedHandlers = 4;
|
|
64
51
|
private const byte PrefreezeKindGlobalBroadcastHandlers = 5;
|
|
52
|
+
private const long DefaultIdleEvictionTicks = 30;
|
|
53
|
+
private const double DefaultEvictionTickIntervalSeconds = 5d;
|
|
54
|
+
internal const int SweepGateSampleSize = 16;
|
|
55
|
+
private const long SweepGateMask = SweepGateSampleSize - 1;
|
|
56
|
+
|
|
57
|
+
private static readonly SlotKey UntargetedHandleSlot = new SlotKey(
|
|
58
|
+
DispatchKind.Untargeted,
|
|
59
|
+
DispatchPhase.Handle,
|
|
60
|
+
DispatchVariant.Default
|
|
61
|
+
);
|
|
62
|
+
private static readonly SlotKey UntargetedPostSlot = new SlotKey(
|
|
63
|
+
DispatchKind.Untargeted,
|
|
64
|
+
DispatchPhase.PostProcess,
|
|
65
|
+
DispatchVariant.Default
|
|
66
|
+
);
|
|
67
|
+
private static readonly SlotKey TargetedHandleSlot = new SlotKey(
|
|
68
|
+
DispatchKind.Targeted,
|
|
69
|
+
DispatchPhase.Handle,
|
|
70
|
+
DispatchVariant.Default
|
|
71
|
+
);
|
|
72
|
+
private static readonly SlotKey TargetedWithoutContextHandleSlot = new SlotKey(
|
|
73
|
+
DispatchKind.Targeted,
|
|
74
|
+
DispatchPhase.Handle,
|
|
75
|
+
DispatchVariant.WithoutContext
|
|
76
|
+
);
|
|
77
|
+
private static readonly SlotKey TargetedPostSlot = new SlotKey(
|
|
78
|
+
DispatchKind.Targeted,
|
|
79
|
+
DispatchPhase.PostProcess,
|
|
80
|
+
DispatchVariant.Default
|
|
81
|
+
);
|
|
82
|
+
private static readonly SlotKey TargetedWithoutContextPostSlot = new SlotKey(
|
|
83
|
+
DispatchKind.Targeted,
|
|
84
|
+
DispatchPhase.PostProcess,
|
|
85
|
+
DispatchVariant.WithoutContext
|
|
86
|
+
);
|
|
87
|
+
private static readonly SlotKey BroadcastPostSlot = new SlotKey(
|
|
88
|
+
DispatchKind.Broadcast,
|
|
89
|
+
DispatchPhase.PostProcess,
|
|
90
|
+
DispatchVariant.Default
|
|
91
|
+
);
|
|
92
|
+
private static readonly SlotKey BroadcastWithoutContextPostSlot = new SlotKey(
|
|
93
|
+
DispatchKind.Broadcast,
|
|
94
|
+
DispatchPhase.PostProcess,
|
|
95
|
+
DispatchVariant.WithoutContext
|
|
96
|
+
);
|
|
97
|
+
internal const int ExpectedMessageCacheFieldCount = 5;
|
|
98
|
+
|
|
99
|
+
private static readonly ISweepable[] SweepableTypeCacheRegistry =
|
|
100
|
+
{
|
|
101
|
+
new SweepableTypeCache(
|
|
102
|
+
nameof(_scalarSinks),
|
|
103
|
+
typeof(MessageCache<HandlerCache<int, HandlerCache>>[]),
|
|
104
|
+
static (bus, force) => bus.SweepDirtyScalarTypeSlots(force)
|
|
105
|
+
),
|
|
106
|
+
new SweepableTypeCache(
|
|
107
|
+
nameof(_contextSinks),
|
|
108
|
+
typeof(MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>>[]),
|
|
109
|
+
static (bus, force) => bus.SweepDirtyTargetSlots(force)
|
|
110
|
+
),
|
|
111
|
+
new SweepableTypeCache(
|
|
112
|
+
nameof(_untargetedInterceptsByType),
|
|
113
|
+
typeof(MessageCache<InterceptorCache<object>>),
|
|
114
|
+
static (bus, force) =>
|
|
115
|
+
bus.SweepDirtyInterceptorTypeSlots(bus._untargetedInterceptsByType, force)
|
|
116
|
+
),
|
|
117
|
+
new SweepableTypeCache(
|
|
118
|
+
nameof(_targetedInterceptsByType),
|
|
119
|
+
typeof(MessageCache<InterceptorCache<object>>),
|
|
120
|
+
static (bus, force) =>
|
|
121
|
+
bus.SweepDirtyInterceptorTypeSlots(bus._targetedInterceptsByType, force)
|
|
122
|
+
),
|
|
123
|
+
new SweepableTypeCache(
|
|
124
|
+
nameof(_broadcastInterceptsByType),
|
|
125
|
+
typeof(MessageCache<InterceptorCache<object>>),
|
|
126
|
+
static (bus, force) =>
|
|
127
|
+
bus.SweepDirtyInterceptorTypeSlots(bus._broadcastInterceptsByType, force)
|
|
128
|
+
),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
internal static IReadOnlyList<ISweepable> SweepableTypeCaches => SweepableTypeCacheRegistry;
|
|
65
132
|
|
|
66
133
|
private static readonly ArrayPool<DispatchBucket> DispatchBucketPool =
|
|
67
134
|
ArrayPool<DispatchBucket>.Shared;
|
|
68
135
|
private static readonly ArrayPool<DispatchEntry> DispatchEntryPool =
|
|
69
136
|
ArrayPool<DispatchEntry>.Shared;
|
|
70
137
|
|
|
71
|
-
private
|
|
138
|
+
private static CollectionPool<
|
|
139
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
140
|
+
> ContextHandlerByTargetDicts => ContextHandlerByTargetDictPoolHolder.Instance;
|
|
141
|
+
|
|
142
|
+
private static class ContextHandlerByTargetDictPoolHolder
|
|
143
|
+
{
|
|
144
|
+
public static readonly CollectionPool<
|
|
145
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
146
|
+
> Instance = new(
|
|
147
|
+
maxRetained: 512,
|
|
148
|
+
useLru: true,
|
|
149
|
+
factory: static () => new Dictionary<InstanceId, HandlerCache<int, HandlerCache>>(),
|
|
150
|
+
onRecycled: static dict => dict.Clear()
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
internal static int ResetStaticPools()
|
|
155
|
+
{
|
|
156
|
+
return ContextHandlerByTargetDicts.Trim(0);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
internal readonly struct DispatchEntry
|
|
72
160
|
{
|
|
73
161
|
public DispatchEntry(
|
|
74
162
|
MessageHandler handler,
|
|
@@ -86,7 +174,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
86
174
|
public readonly PrefreezeDescriptor prefreeze;
|
|
87
175
|
}
|
|
88
176
|
|
|
89
|
-
|
|
177
|
+
internal struct DispatchBucket
|
|
90
178
|
{
|
|
91
179
|
public DispatchBucket(
|
|
92
180
|
int priority,
|
|
@@ -127,7 +215,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
127
215
|
}
|
|
128
216
|
}
|
|
129
217
|
|
|
130
|
-
|
|
218
|
+
internal sealed class DispatchSnapshot
|
|
131
219
|
{
|
|
132
220
|
public static readonly DispatchSnapshot Empty = new DispatchSnapshot(
|
|
133
221
|
Array.Empty<DispatchBucket>(),
|
|
@@ -169,281 +257,1740 @@ namespace DxMessaging.Core.MessageBus
|
|
|
169
257
|
}
|
|
170
258
|
}
|
|
171
259
|
|
|
172
|
-
|
|
260
|
+
internal sealed class DispatchState
|
|
173
261
|
{
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
public bool pendingDirty;
|
|
180
|
-
public long snapshotEmissionId = -1;
|
|
262
|
+
public DispatchSnapshot active = DispatchSnapshot.Empty;
|
|
263
|
+
public DispatchSnapshot pending = DispatchSnapshot.Empty;
|
|
264
|
+
public bool hasPending;
|
|
265
|
+
public bool pendingDirty;
|
|
266
|
+
public long snapshotEmissionId = -1;
|
|
181
267
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
268
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
269
|
+
public void Reset()
|
|
270
|
+
{
|
|
271
|
+
ReleaseSnapshot(ref active);
|
|
272
|
+
ReleaseSnapshot(ref pending);
|
|
273
|
+
hasPending = false;
|
|
274
|
+
pendingDirty = false;
|
|
275
|
+
snapshotEmissionId = -1;
|
|
191
276
|
}
|
|
277
|
+
}
|
|
192
278
|
|
|
279
|
+
private sealed class HandlerCache<TKey, TValue>
|
|
280
|
+
{
|
|
193
281
|
public readonly Dictionary<TKey, TValue> handlers = new();
|
|
194
282
|
public readonly List<TKey> order = new();
|
|
195
283
|
public readonly List<KeyValuePair<TKey, TValue>> cache = new();
|
|
196
284
|
public long version;
|
|
197
285
|
public long lastSeenVersion = -1;
|
|
198
|
-
public long lastSeenEmissionId;
|
|
199
|
-
|
|
286
|
+
public long lastSeenEmissionId = -1;
|
|
287
|
+
public long lastTouchTicks;
|
|
288
|
+
public DispatchState dispatchState;
|
|
200
289
|
|
|
201
290
|
/// <summary>
|
|
202
291
|
/// Clears all cached handler references and resets the version tracking metadata.
|
|
203
292
|
/// </summary>
|
|
204
293
|
public void Clear()
|
|
205
294
|
{
|
|
295
|
+
// LEGACY: version reset semantics. Bus-side deregistration closures use
|
|
296
|
+
// captured cache identity and reset generations, so monotonic versioning
|
|
297
|
+
// is handled by sweep-driven slot reset paths.
|
|
206
298
|
handlers.Clear();
|
|
207
299
|
order.Clear();
|
|
208
300
|
cache.Clear();
|
|
209
301
|
version = 0;
|
|
210
302
|
lastSeenVersion = -1;
|
|
211
|
-
lastSeenEmissionId =
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
foreach (DispatchState state in _dispatchStates.Values)
|
|
215
|
-
{
|
|
216
|
-
state.Reset();
|
|
217
|
-
}
|
|
218
|
-
_dispatchStates.Clear();
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
223
|
-
public DispatchState GetOrCreateDispatchState(DispatchCategory category)
|
|
224
|
-
{
|
|
225
|
-
if (!_dispatchStates.TryGetValue(category, out DispatchState state))
|
|
226
|
-
{
|
|
227
|
-
state = new DispatchState();
|
|
228
|
-
_dispatchStates[category] = state;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return state;
|
|
303
|
+
lastSeenEmissionId = -1;
|
|
304
|
+
dispatchState?.Reset();
|
|
305
|
+
dispatchState = null;
|
|
232
306
|
}
|
|
233
307
|
}
|
|
234
308
|
|
|
235
309
|
private sealed class InterceptorCache<TValue>
|
|
236
310
|
{
|
|
237
311
|
public readonly SortedList<int, List<TValue>> handlers = new();
|
|
238
|
-
public long lastSeenEmissionId;
|
|
312
|
+
public long lastSeenEmissionId = -1;
|
|
313
|
+
public long lastTouchTicks;
|
|
239
314
|
|
|
240
315
|
public void Clear()
|
|
241
316
|
{
|
|
242
317
|
handlers.Clear();
|
|
243
|
-
lastSeenEmissionId =
|
|
318
|
+
lastSeenEmissionId = -1;
|
|
319
|
+
lastTouchTicks = 0;
|
|
244
320
|
}
|
|
245
321
|
}
|
|
246
322
|
|
|
247
|
-
private sealed class
|
|
323
|
+
private sealed class SweepableTypeCache : ISweepable
|
|
248
324
|
{
|
|
249
|
-
|
|
325
|
+
private readonly Func<MessageBus, bool, int> _sweep;
|
|
326
|
+
|
|
327
|
+
public SweepableTypeCache(
|
|
328
|
+
string storageFieldName,
|
|
329
|
+
Type storageFieldType,
|
|
330
|
+
Func<MessageBus, bool, int> sweep
|
|
331
|
+
)
|
|
250
332
|
{
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
public long snapshotEmissionId = -1;
|
|
333
|
+
StorageFieldName = storageFieldName;
|
|
334
|
+
StorageFieldType = storageFieldType;
|
|
335
|
+
_sweep = sweep;
|
|
336
|
+
}
|
|
256
337
|
|
|
257
|
-
|
|
258
|
-
|
|
338
|
+
public string StorageFieldName { get; }
|
|
339
|
+
public Type StorageFieldType { get; }
|
|
340
|
+
|
|
341
|
+
public int Sweep(MessageBus bus, bool force)
|
|
342
|
+
{
|
|
343
|
+
if (bus == null)
|
|
259
344
|
{
|
|
260
|
-
|
|
261
|
-
ReleaseSnapshot(ref pending);
|
|
262
|
-
hasPending = false;
|
|
263
|
-
pendingDirty = false;
|
|
264
|
-
snapshotEmissionId = -1;
|
|
345
|
+
throw new ArgumentNullException(nameof(bus));
|
|
265
346
|
}
|
|
347
|
+
|
|
348
|
+
return _sweep(bus, force);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private readonly struct DispatchLease : IDisposable
|
|
353
|
+
{
|
|
354
|
+
private readonly MessageBus _bus;
|
|
355
|
+
|
|
356
|
+
public DispatchLease(MessageBus bus)
|
|
357
|
+
{
|
|
358
|
+
_bus = bus;
|
|
359
|
+
_bus._dispatchDepth++;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
public void Dispose()
|
|
363
|
+
{
|
|
364
|
+
_bus._dispatchDepth--;
|
|
266
365
|
}
|
|
366
|
+
}
|
|
267
367
|
|
|
368
|
+
private sealed class HandlerCache
|
|
369
|
+
{
|
|
268
370
|
public readonly Dictionary<MessageHandler, int> handlers = new();
|
|
269
371
|
public readonly List<MessageHandler> cache = new();
|
|
270
372
|
public long version;
|
|
271
373
|
public long lastSeenVersion = -1;
|
|
272
|
-
public long lastSeenEmissionId;
|
|
273
|
-
private readonly Dictionary<DispatchCategory, DispatchState> _dispatchStates = new();
|
|
374
|
+
public long lastSeenEmissionId = -1;
|
|
274
375
|
|
|
275
376
|
/// <summary>
|
|
276
377
|
/// Clears all cached handler references and resets the version tracking metadata.
|
|
277
378
|
/// </summary>
|
|
278
379
|
public void Clear()
|
|
279
380
|
{
|
|
381
|
+
// LEGACY: version reset semantics. Bus-side deregistration closures use
|
|
382
|
+
// captured cache identity and reset generations, so monotonic versioning
|
|
383
|
+
// is handled by sweep-driven slot reset paths.
|
|
280
384
|
handlers.Clear();
|
|
281
385
|
cache.Clear();
|
|
282
386
|
version = 0;
|
|
283
387
|
lastSeenVersion = -1;
|
|
284
|
-
lastSeenEmissionId =
|
|
285
|
-
|
|
388
|
+
lastSeenEmissionId = -1;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
public int RegisteredTargeted
|
|
393
|
+
{
|
|
394
|
+
get
|
|
395
|
+
{
|
|
396
|
+
int count = 0;
|
|
397
|
+
count += SumTargetedSinks(_contextSinks[BusContextIndex.TargetedHandleDefault]);
|
|
398
|
+
foreach (
|
|
399
|
+
HandlerCache<int, HandlerCache> entry in _scalarSinks[
|
|
400
|
+
BusSinkIndex.TargetedHandleWithoutContext
|
|
401
|
+
]
|
|
402
|
+
)
|
|
403
|
+
{
|
|
404
|
+
count += entry?.handlers?.Count ?? 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return count;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
public int RegisteredGlobalSequentialIndex { get; } = GenerateNewGlobalSequentialIndex();
|
|
412
|
+
|
|
413
|
+
public int OccupiedTypeSlots
|
|
414
|
+
{
|
|
415
|
+
get
|
|
416
|
+
{
|
|
417
|
+
int count = 0;
|
|
418
|
+
for (int i = 0; i < _scalarSinks.Length; ++i)
|
|
419
|
+
{
|
|
420
|
+
MessageCache<HandlerCache<int, HandlerCache>> sink = _scalarSinks[i];
|
|
421
|
+
if (sink == null)
|
|
422
|
+
{
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
foreach (HandlerCache<int, HandlerCache> _ in sink)
|
|
427
|
+
{
|
|
428
|
+
count++;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
for (int i = 0; i < _contextSinks.Length; ++i)
|
|
433
|
+
{
|
|
434
|
+
foreach (
|
|
435
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> _ in _contextSinks[
|
|
436
|
+
i
|
|
437
|
+
]
|
|
438
|
+
)
|
|
439
|
+
{
|
|
440
|
+
count++;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return count + OccupiedInterceptorTypeSlots + CountDirtyEmptyTypedHandlerSlots();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
private int OccupiedInterceptorTypeSlots
|
|
449
|
+
{
|
|
450
|
+
get
|
|
451
|
+
{
|
|
452
|
+
return CountOccupiedInterceptorTypeSlots(_untargetedInterceptsByType)
|
|
453
|
+
+ CountOccupiedInterceptorTypeSlots(_targetedInterceptsByType)
|
|
454
|
+
+ CountOccupiedInterceptorTypeSlots(_broadcastInterceptsByType);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
public int OccupiedTargetSlots
|
|
459
|
+
{
|
|
460
|
+
get
|
|
461
|
+
{
|
|
462
|
+
int count = 0;
|
|
463
|
+
for (int i = 0; i < _contextSinks.Length; ++i)
|
|
286
464
|
{
|
|
287
|
-
foreach (
|
|
465
|
+
foreach (
|
|
466
|
+
Dictionary<
|
|
467
|
+
InstanceId,
|
|
468
|
+
HandlerCache<int, HandlerCache>
|
|
469
|
+
> byTarget in _contextSinks[i]
|
|
470
|
+
)
|
|
288
471
|
{
|
|
289
|
-
|
|
472
|
+
count += byTarget?.Count ?? 0;
|
|
290
473
|
}
|
|
291
|
-
_dispatchStates.Clear();
|
|
292
474
|
}
|
|
475
|
+
|
|
476
|
+
return count;
|
|
293
477
|
}
|
|
478
|
+
}
|
|
294
479
|
|
|
295
|
-
|
|
296
|
-
|
|
480
|
+
public int RegisteredBroadcast
|
|
481
|
+
{
|
|
482
|
+
get
|
|
483
|
+
{
|
|
484
|
+
int count = 0;
|
|
485
|
+
count += SumTargetedSinks(_contextSinks[BusContextIndex.BroadcastHandleDefault]);
|
|
486
|
+
foreach (
|
|
487
|
+
HandlerCache<int, HandlerCache> entry in _scalarSinks[
|
|
488
|
+
BusSinkIndex.BroadcastHandleWithoutContext
|
|
489
|
+
]
|
|
490
|
+
)
|
|
491
|
+
{
|
|
492
|
+
count += entry?.handlers?.Count ?? 0;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return count;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
public int RegisteredUntargeted
|
|
500
|
+
{
|
|
501
|
+
get
|
|
502
|
+
{
|
|
503
|
+
int count = 0;
|
|
504
|
+
foreach (
|
|
505
|
+
HandlerCache<int, HandlerCache> entry in _scalarSinks[
|
|
506
|
+
BusSinkIndex.UntargetedHandleDefault
|
|
507
|
+
]
|
|
508
|
+
)
|
|
509
|
+
{
|
|
510
|
+
count += entry?.handlers?.Count ?? 0;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return count;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
public int RegisteredInterceptors
|
|
518
|
+
{
|
|
519
|
+
get
|
|
520
|
+
{
|
|
521
|
+
int count = 0;
|
|
522
|
+
count += SumInterceptorCache(_untargetedInterceptsByType);
|
|
523
|
+
count += SumInterceptorCache(_targetedInterceptsByType);
|
|
524
|
+
count += SumInterceptorCache(_broadcastInterceptsByType);
|
|
525
|
+
return count;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
public int RegisteredPostProcessors
|
|
530
|
+
{
|
|
531
|
+
get
|
|
532
|
+
{
|
|
533
|
+
int count = 0;
|
|
534
|
+
foreach (
|
|
535
|
+
HandlerCache<int, HandlerCache> entry in _scalarSinks[
|
|
536
|
+
BusSinkIndex.UntargetedPostProcessDefault
|
|
537
|
+
]
|
|
538
|
+
)
|
|
539
|
+
{
|
|
540
|
+
count += entry?.handlers?.Count ?? 0;
|
|
541
|
+
}
|
|
542
|
+
count += SumTargetedSinks(
|
|
543
|
+
_contextSinks[BusContextIndex.TargetedPostProcessDefault]
|
|
544
|
+
);
|
|
545
|
+
count += SumTargetedSinks(
|
|
546
|
+
_contextSinks[BusContextIndex.BroadcastPostProcessDefault]
|
|
547
|
+
);
|
|
548
|
+
foreach (
|
|
549
|
+
HandlerCache<int, HandlerCache> entry in _scalarSinks[
|
|
550
|
+
BusSinkIndex.TargetedPostProcessWithoutContext
|
|
551
|
+
]
|
|
552
|
+
)
|
|
553
|
+
{
|
|
554
|
+
count += entry?.handlers?.Count ?? 0;
|
|
555
|
+
}
|
|
556
|
+
foreach (
|
|
557
|
+
HandlerCache<int, HandlerCache> entry in _scalarSinks[
|
|
558
|
+
BusSinkIndex.BroadcastPostProcessWithoutContext
|
|
559
|
+
]
|
|
560
|
+
)
|
|
561
|
+
{
|
|
562
|
+
count += entry?.handlers?.Count ?? 0;
|
|
563
|
+
}
|
|
564
|
+
return count;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
public int RegisteredGlobalAcceptAll => _globalSlots.sharedHandlers.Count;
|
|
569
|
+
|
|
570
|
+
private static int SumInterceptorCache(MessageCache<InterceptorCache<object>> cache)
|
|
571
|
+
{
|
|
572
|
+
int count = 0;
|
|
573
|
+
foreach (InterceptorCache<object> entry in cache)
|
|
574
|
+
{
|
|
575
|
+
if (entry == null)
|
|
576
|
+
{
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
foreach (KeyValuePair<int, List<object>> bucket in entry.handlers)
|
|
580
|
+
{
|
|
581
|
+
count += bucket.Value?.Count ?? 0;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return count;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private static int SumTargetedSinks(
|
|
588
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> cache
|
|
589
|
+
)
|
|
590
|
+
{
|
|
591
|
+
int count = 0;
|
|
592
|
+
foreach (Dictionary<InstanceId, HandlerCache<int, HandlerCache>> entry in cache)
|
|
297
593
|
{
|
|
298
|
-
if (
|
|
594
|
+
if (entry == null)
|
|
595
|
+
{
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
foreach (KeyValuePair<InstanceId, HandlerCache<int, HandlerCache>> kvp in entry)
|
|
299
599
|
{
|
|
300
|
-
|
|
301
|
-
_dispatchStates[category] = state;
|
|
600
|
+
count += kvp.Value?.handlers?.Count ?? 0;
|
|
302
601
|
}
|
|
602
|
+
}
|
|
603
|
+
return count;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
public bool DiagnosticsMode
|
|
607
|
+
{
|
|
608
|
+
get => _diagnosticsMode;
|
|
609
|
+
set => _diagnosticsMode = value;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
private static readonly Type MessageBusType = typeof(MessageBus);
|
|
613
|
+
|
|
614
|
+
// For use with re-broadcasting to generic methods
|
|
615
|
+
private static readonly object[] ReflectionMethodArgumentsCache = new object[2];
|
|
616
|
+
private static readonly List<Expression> ArgumentExpressionsCache = new();
|
|
617
|
+
|
|
618
|
+
private const BindingFlags ReflectionHelperBindingFlags =
|
|
619
|
+
BindingFlags.Static | BindingFlags.NonPublic;
|
|
620
|
+
private const BindingFlags ReflexiveMethodBindingFlags =
|
|
621
|
+
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
|
622
|
+
|
|
623
|
+
private delegate void FastUntargetedBroadcast<T>(ref T message)
|
|
624
|
+
where T : IUntargetedMessage;
|
|
625
|
+
private delegate void FastTargetedBroadcast<T>(ref InstanceId target, ref T message)
|
|
626
|
+
where T : ITargetedMessage;
|
|
627
|
+
private delegate void FastSourcedBroadcast<T>(ref InstanceId target, ref T message)
|
|
628
|
+
where T : IBroadcastMessage;
|
|
629
|
+
|
|
630
|
+
public RegistrationLog Log => _log;
|
|
631
|
+
|
|
632
|
+
// Storage trio for typed and global dispatch. _scalarSinks and
|
|
633
|
+
// _contextSinks are SlotKey-indexed arrays of MessageCache (call sites
|
|
634
|
+
// index by BusSinkIndex / BusContextIndex constants; reserved-null
|
|
635
|
+
// entries are documented in BusSinkIndex.cs). _globalSlots is a single
|
|
636
|
+
// BusGlobalSlot -- the global accept-all slot is single-cardinality, so
|
|
637
|
+
// there is no array to index, but it is grouped here because it shares
|
|
638
|
+
// the lifecycle of the typed sinks (cleared together in ResetState,
|
|
639
|
+
// touched together by the eviction layer).
|
|
640
|
+
private readonly MessageCache<HandlerCache<int, HandlerCache>>[] _scalarSinks =
|
|
641
|
+
new MessageCache<HandlerCache<int, HandlerCache>>[BusSinkIndex.Length]
|
|
642
|
+
{
|
|
643
|
+
/* [0] UntargetedHandleDefault */new(),
|
|
644
|
+
/* [1] BroadcastHandleWithoutContext */new(),
|
|
645
|
+
/* [2] TargetedHandleWithoutContext */new(),
|
|
646
|
+
/* [3] UntargetedPostProcessDefault */new(),
|
|
647
|
+
/* [4] TargetedPostProcessWithoutContext */new(),
|
|
648
|
+
/* [5] BroadcastPostProcessWithoutContext */new(),
|
|
649
|
+
/* [6] Reserved6 */null,
|
|
650
|
+
/* [7] Reserved7 */null,
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
private readonly MessageCache<
|
|
654
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
655
|
+
>[] _contextSinks = new MessageCache<
|
|
656
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
657
|
+
>[BusContextIndex.Length]
|
|
658
|
+
{
|
|
659
|
+
/* [0] TargetedHandleDefault */new(),
|
|
660
|
+
/* [1] BroadcastHandleDefault */new(),
|
|
661
|
+
/* [2] TargetedPostProcessDefault */new(),
|
|
662
|
+
/* [3] BroadcastPostProcessDefault */new(),
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
private readonly BusGlobalSlot _globalSlots = new();
|
|
666
|
+
|
|
667
|
+
/// <summary>
|
|
668
|
+
/// Constructs a <see cref="MessageBus"/> using the default <see cref="StopwatchClock"/>
|
|
669
|
+
/// and runtime-settings provided eviction cadence. This is the only public constructor; DI
|
|
670
|
+
/// containers that scan constructors reflectively (for example VContainer, which inspects
|
|
671
|
+
/// both public and private constructors) must be configured with an explicit factory --
|
|
672
|
+
/// see the integration helpers under <c>Runtime/Unity/Integrations</c>.
|
|
673
|
+
/// </summary>
|
|
674
|
+
public MessageBus()
|
|
675
|
+
: this(StopwatchClock.Instance, DefaultIdleEvictionTicks, applyRuntimeSettings: true)
|
|
676
|
+
{ }
|
|
677
|
+
|
|
678
|
+
/// <summary>
|
|
679
|
+
/// Internal factory used by tests and integration assemblies to construct a
|
|
680
|
+
/// <see cref="MessageBus"/> with an injected <see cref="IDxMessagingClock"/> and optional
|
|
681
|
+
/// eviction overrides. Lives behind an <c>internal static</c> entry point so the public
|
|
682
|
+
/// surface exposes only the parameterless constructor; this keeps reflection-based DI
|
|
683
|
+
/// containers from latching onto a clock-taking overload they cannot satisfy.
|
|
684
|
+
/// </summary>
|
|
685
|
+
/// <param name="clock">Clock implementation. Must not be null.</param>
|
|
686
|
+
/// <param name="idleEvictionTicks">Optional idle-eviction tick budget; falls back to <see cref="DefaultIdleEvictionTicks"/> when null.</param>
|
|
687
|
+
/// <param name="evictionTickIntervalSeconds">Optional sweep cadence in seconds.</param>
|
|
688
|
+
/// <param name="idleEvictionEnabled">Optional opt-out for idle eviction.</param>
|
|
689
|
+
/// <param name="trimApiEnabled">Optional opt-out for the trim API.</param>
|
|
690
|
+
/// <returns>Configured <see cref="MessageBus"/> instance.</returns>
|
|
691
|
+
internal static MessageBus CreateForInternalUse(
|
|
692
|
+
IDxMessagingClock clock,
|
|
693
|
+
long? idleEvictionTicks = null,
|
|
694
|
+
double? evictionTickIntervalSeconds = null,
|
|
695
|
+
bool? idleEvictionEnabled = null,
|
|
696
|
+
bool? trimApiEnabled = null
|
|
697
|
+
)
|
|
698
|
+
{
|
|
699
|
+
if (clock == null)
|
|
700
|
+
{
|
|
701
|
+
throw new ArgumentNullException(nameof(clock));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
long resolvedIdleEvictionTicks = idleEvictionTicks ?? DefaultIdleEvictionTicks;
|
|
705
|
+
bool applyRuntimeSettings =
|
|
706
|
+
idleEvictionTicks == null
|
|
707
|
+
&& evictionTickIntervalSeconds == null
|
|
708
|
+
&& idleEvictionEnabled == null
|
|
709
|
+
&& trimApiEnabled == null;
|
|
710
|
+
|
|
711
|
+
MessageBus bus = new MessageBus(
|
|
712
|
+
clock,
|
|
713
|
+
resolvedIdleEvictionTicks,
|
|
714
|
+
applyRuntimeSettings: applyRuntimeSettings
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
if (evictionTickIntervalSeconds.HasValue)
|
|
718
|
+
{
|
|
719
|
+
bus._evictionTickIntervalSeconds = Math.Max(0d, evictionTickIntervalSeconds.Value);
|
|
720
|
+
}
|
|
721
|
+
if (idleEvictionEnabled.HasValue)
|
|
722
|
+
{
|
|
723
|
+
bus._idleEvictionEnabled = idleEvictionEnabled.Value;
|
|
724
|
+
}
|
|
725
|
+
if (trimApiEnabled.HasValue)
|
|
726
|
+
{
|
|
727
|
+
bus._trimApiEnabled = trimApiEnabled.Value;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return bus;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
private MessageBus(
|
|
734
|
+
IDxMessagingClock clock,
|
|
735
|
+
long idleEvictionTicks,
|
|
736
|
+
bool applyRuntimeSettings
|
|
737
|
+
)
|
|
738
|
+
{
|
|
739
|
+
_clock = clock ?? throw new ArgumentNullException(nameof(clock));
|
|
740
|
+
_idleEvictionTicks = Math.Max(0, idleEvictionTicks);
|
|
741
|
+
_evictionTickIntervalSeconds = DefaultEvictionTickIntervalSeconds;
|
|
742
|
+
_lastSweepSeconds = _clock.NowSeconds;
|
|
743
|
+
#if UNITY_2021_3_OR_NEWER
|
|
744
|
+
RegisterForIdleSweeps(this);
|
|
745
|
+
EnsureRuntimeSettingsSubscription();
|
|
746
|
+
if (applyRuntimeSettings)
|
|
747
|
+
{
|
|
748
|
+
ApplyRuntimeSettings(DxMessagingRuntimeSettingsProvider.Current);
|
|
749
|
+
}
|
|
750
|
+
#endif
|
|
751
|
+
ValidateSinkArrays();
|
|
752
|
+
}
|
|
303
753
|
|
|
304
|
-
|
|
754
|
+
#if UNITY_2021_3_OR_NEWER
|
|
755
|
+
private static readonly List<WeakReference<MessageBus>> IdleSweepBuses = new();
|
|
756
|
+
private static bool RuntimeSettingsSubscribed;
|
|
757
|
+
|
|
758
|
+
private static void RegisterForIdleSweeps(MessageBus bus)
|
|
759
|
+
{
|
|
760
|
+
for (int i = IdleSweepBuses.Count - 1; i >= 0; --i)
|
|
761
|
+
{
|
|
762
|
+
if (!IdleSweepBuses[i].TryGetTarget(out MessageBus existing))
|
|
763
|
+
{
|
|
764
|
+
IdleSweepBuses.RemoveAt(i);
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
if (ReferenceEquals(existing, bus))
|
|
768
|
+
{
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
IdleSweepBuses.Add(new WeakReference<MessageBus>(bus));
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
private static void EnsureRuntimeSettingsSubscription()
|
|
777
|
+
{
|
|
778
|
+
if (RuntimeSettingsSubscribed)
|
|
779
|
+
{
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
DxMessagingRuntimeSettings.SettingsChanged += HandleRuntimeSettingsChanged;
|
|
784
|
+
RuntimeSettingsSubscribed = true;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
private static void HandleRuntimeSettingsChanged(DxMessagingRuntimeSettings settings)
|
|
788
|
+
{
|
|
789
|
+
if (settings == null)
|
|
790
|
+
{
|
|
791
|
+
settings = DxMessagingRuntimeSettingsProvider.Current;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
for (int i = IdleSweepBuses.Count - 1; i >= 0; --i)
|
|
795
|
+
{
|
|
796
|
+
if (IdleSweepBuses[i].TryGetTarget(out MessageBus bus))
|
|
797
|
+
{
|
|
798
|
+
bus.ApplyRuntimeSettings(settings);
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
IdleSweepBuses.RemoveAt(i);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
internal static void SweepIdleBusesFromPlayerLoop()
|
|
807
|
+
{
|
|
808
|
+
for (int i = IdleSweepBuses.Count - 1; i >= 0; --i)
|
|
809
|
+
{
|
|
810
|
+
if (IdleSweepBuses[i].TryGetTarget(out MessageBus bus))
|
|
811
|
+
{
|
|
812
|
+
bus.TrySweepIdle(advanceTickForIdleAging: true);
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
IdleSweepBuses.RemoveAt(i);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
|
821
|
+
private static void ResetIdleSweepRegistry()
|
|
822
|
+
{
|
|
823
|
+
DxMessagingRuntimeSettings.SettingsChanged -= HandleRuntimeSettingsChanged;
|
|
824
|
+
IdleSweepBuses.Clear();
|
|
825
|
+
RuntimeSettingsSubscribed = false;
|
|
826
|
+
ResetStaticPools();
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
private void ApplyRuntimeSettings(DxMessagingRuntimeSettings settings)
|
|
830
|
+
{
|
|
831
|
+
if (settings == null)
|
|
832
|
+
{
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
DxPools.Configure(settings);
|
|
837
|
+
ContextHandlerByTargetDicts.UseLru = settings.BufferUseLruEviction;
|
|
838
|
+
ContextHandlerByTargetDicts.MaxRetained = settings.BufferMaxDistinctEntries;
|
|
839
|
+
if (!settings.IsFallbackInstance)
|
|
840
|
+
{
|
|
841
|
+
IMessageBus.GlobalMessageBufferSize = Math.Max(0, settings.MessageBufferSize);
|
|
842
|
+
}
|
|
843
|
+
_emissionBuffer.Resize(Math.Max(0, IMessageBus.GlobalMessageBufferSize));
|
|
844
|
+
_idleEvictionTicks = ComputeIdleEvictionTicks(settings.IdleEvictionSeconds);
|
|
845
|
+
_evictionTickIntervalSeconds = Math.Max(0d, settings.EvictionTickIntervalSeconds);
|
|
846
|
+
_idleEvictionEnabled = settings.EvictionEnabled;
|
|
847
|
+
_trimApiEnabled = settings.EnableTrimApi;
|
|
848
|
+
}
|
|
849
|
+
#endif
|
|
850
|
+
|
|
851
|
+
private static long ComputeIdleEvictionTicks(float idleEvictionSeconds)
|
|
852
|
+
{
|
|
853
|
+
if (idleEvictionSeconds <= 0f)
|
|
854
|
+
{
|
|
855
|
+
return 0;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
return (long)Math.Ceiling(idleEvictionSeconds);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
[Conditional("DEBUG")]
|
|
862
|
+
private void ValidateSinkArrays()
|
|
863
|
+
{
|
|
864
|
+
if (_scalarSinks.Length != BusSinkIndex.Length)
|
|
865
|
+
{
|
|
866
|
+
throw new InvalidOperationException(
|
|
867
|
+
$"_scalarSinks length is {_scalarSinks.Length} but BusSinkIndex.Length is {BusSinkIndex.Length}."
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
if (_contextSinks.Length != BusContextIndex.Length)
|
|
871
|
+
{
|
|
872
|
+
throw new InvalidOperationException(
|
|
873
|
+
$"_contextSinks length is {_contextSinks.Length} but BusContextIndex.Length is {BusContextIndex.Length}."
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
if (_scalarSinks[BusSinkIndex.Reserved6] != null)
|
|
877
|
+
{
|
|
878
|
+
throw new InvalidOperationException(
|
|
879
|
+
"_scalarSinks[Reserved6] is a permanent future-expansion stub and must be null."
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
if (_scalarSinks[BusSinkIndex.Reserved7] != null)
|
|
883
|
+
{
|
|
884
|
+
throw new InvalidOperationException(
|
|
885
|
+
"_scalarSinks[Reserved7] is a permanent future-expansion stub and must be null."
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
if (_scalarSinks[BusSinkIndex.UntargetedHandleDefault] == null)
|
|
889
|
+
{
|
|
890
|
+
throw new InvalidOperationException(
|
|
891
|
+
"_scalarSinks[UntargetedHandleDefault] must be non-null."
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
if (_scalarSinks[BusSinkIndex.BroadcastHandleWithoutContext] == null)
|
|
895
|
+
{
|
|
896
|
+
throw new InvalidOperationException(
|
|
897
|
+
"_scalarSinks[BroadcastHandleWithoutContext] must be non-null."
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
if (_scalarSinks[BusSinkIndex.TargetedHandleWithoutContext] == null)
|
|
901
|
+
{
|
|
902
|
+
throw new InvalidOperationException(
|
|
903
|
+
"_scalarSinks[TargetedHandleWithoutContext] must be non-null."
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
if (_scalarSinks[BusSinkIndex.UntargetedPostProcessDefault] == null)
|
|
907
|
+
{
|
|
908
|
+
throw new InvalidOperationException(
|
|
909
|
+
"_scalarSinks[UntargetedPostProcessDefault] must be non-null."
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
if (_scalarSinks[BusSinkIndex.TargetedPostProcessWithoutContext] == null)
|
|
913
|
+
{
|
|
914
|
+
throw new InvalidOperationException(
|
|
915
|
+
"_scalarSinks[TargetedPostProcessWithoutContext] must be non-null."
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
if (_scalarSinks[BusSinkIndex.BroadcastPostProcessWithoutContext] == null)
|
|
919
|
+
{
|
|
920
|
+
throw new InvalidOperationException(
|
|
921
|
+
"_scalarSinks[BroadcastPostProcessWithoutContext] must be non-null."
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
if (_contextSinks[BusContextIndex.TargetedHandleDefault] == null)
|
|
925
|
+
{
|
|
926
|
+
throw new InvalidOperationException(
|
|
927
|
+
"_contextSinks[TargetedHandleDefault] must be non-null."
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
if (_contextSinks[BusContextIndex.BroadcastHandleDefault] == null)
|
|
931
|
+
{
|
|
932
|
+
throw new InvalidOperationException(
|
|
933
|
+
"_contextSinks[BroadcastHandleDefault] must be non-null."
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
if (_contextSinks[BusContextIndex.TargetedPostProcessDefault] == null)
|
|
937
|
+
{
|
|
938
|
+
throw new InvalidOperationException(
|
|
939
|
+
"_contextSinks[TargetedPostProcessDefault] must be non-null."
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
if (_contextSinks[BusContextIndex.BroadcastPostProcessDefault] == null)
|
|
943
|
+
{
|
|
944
|
+
throw new InvalidOperationException(
|
|
945
|
+
"_contextSinks[BroadcastPostProcessDefault] must be non-null."
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// Asserts BusGlobalSlot.liveCount remains in lockstep with
|
|
951
|
+
// _globalSlots.sharedHandlers.Count after every register / deregister.
|
|
952
|
+
// Stripped in Release builds via [Conditional("DEBUG")] -- zero
|
|
953
|
+
// hot-path cost. Kept separate from ValidateSinkArrays (which runs
|
|
954
|
+
// once at construction) because this invariant must hold across
|
|
955
|
+
// mutations, not only at startup.
|
|
956
|
+
[Conditional("DEBUG")]
|
|
957
|
+
private void DebugAssertGlobalLiveCount()
|
|
958
|
+
{
|
|
959
|
+
System.Diagnostics.Debug.Assert(
|
|
960
|
+
_globalSlots.liveCount == _globalSlots.sharedHandlers.Count,
|
|
961
|
+
"BusGlobalSlot.liveCount must mirror sharedHandlers.Count at every "
|
|
962
|
+
+ "stable observation point. Drift indicates a missed register / "
|
|
963
|
+
+ "deregister wiring point or an unexpected mutation path."
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Interceptors split by category to avoid mixing types
|
|
968
|
+
private readonly MessageCache<InterceptorCache<object>> _untargetedInterceptsByType = new();
|
|
969
|
+
private readonly MessageCache<InterceptorCache<object>> _targetedInterceptsByType = new();
|
|
970
|
+
private readonly MessageCache<InterceptorCache<object>> _broadcastInterceptsByType = new();
|
|
971
|
+
private readonly Dictionary<object, Dictionary<int, int>> _uniqueInterceptorsAndPriorities =
|
|
972
|
+
new();
|
|
973
|
+
|
|
974
|
+
private readonly Dictionary<Type, object> _broadcastMethodsByType = new();
|
|
975
|
+
private readonly Stack<List<object>> _innerInterceptorsStack = new();
|
|
976
|
+
|
|
977
|
+
private readonly Dictionary<
|
|
978
|
+
Type,
|
|
979
|
+
Dictionary<MethodSignatureKey, Action<MonoBehaviour, object[]>>
|
|
980
|
+
> _methodCache = new();
|
|
981
|
+
|
|
982
|
+
#if UNITY_2021_3_OR_NEWER
|
|
983
|
+
private readonly HashSet<MonoBehaviour> _recipientCache = new();
|
|
984
|
+
private readonly List<MonoBehaviour> _componentCache = new();
|
|
985
|
+
#endif
|
|
986
|
+
|
|
987
|
+
private readonly RegistrationLog _log = new();
|
|
988
|
+
internal readonly CyclicBuffer<MessageEmissionData> _emissionBuffer = new(
|
|
989
|
+
GlobalMessageBufferSize
|
|
990
|
+
);
|
|
991
|
+
|
|
992
|
+
private bool _diagnosticsMode = ShouldEnableDiagnostics();
|
|
993
|
+
private bool _loggedReflexiveWarning;
|
|
994
|
+
private long _tickCounter;
|
|
995
|
+
private readonly IDxMessagingClock _clock;
|
|
996
|
+
private long _idleEvictionTicks = DefaultIdleEvictionTicks;
|
|
997
|
+
private double _evictionTickIntervalSeconds = DefaultEvictionTickIntervalSeconds;
|
|
998
|
+
private bool _idleEvictionEnabled = true;
|
|
999
|
+
private bool _trimApiEnabled = true;
|
|
1000
|
+
private double _lastSweepSeconds;
|
|
1001
|
+
private readonly List<int> _dirtyTypes = new();
|
|
1002
|
+
private readonly Dictionary<int, List<InstanceId>> _dirtyTargets = new();
|
|
1003
|
+
private readonly Dictionary<int, int> _dirtyTargetHighWaterCounts = new();
|
|
1004
|
+
private readonly HashSet<int> _dirtyTypeSet = new();
|
|
1005
|
+
private readonly Dictionary<int, HashSet<InstanceId>> _dirtyTargetSets = new();
|
|
1006
|
+
private readonly Dictionary<
|
|
1007
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>,
|
|
1008
|
+
int
|
|
1009
|
+
> _contextMapHighWaterCounts = new();
|
|
1010
|
+
private readonly List<MessageHandler> _dirtyHandlers = new();
|
|
1011
|
+
private readonly HashSet<MessageHandler> _dirtyHandlerSet = new();
|
|
1012
|
+
private readonly Dictionary<MessageHandler, long> _dirtyHandlerTicks = new();
|
|
1013
|
+
private bool _globalSlotSweepCandidate;
|
|
1014
|
+
private long _globalSlotSweepGeneration;
|
|
1015
|
+
private int _lastContextTypeSlotsEvicted;
|
|
1016
|
+
private int _dispatchDepth;
|
|
1017
|
+
|
|
1018
|
+
// Bumped by ResetState. Deregister closures captured before the bump
|
|
1019
|
+
// compare their captured generation to this field and silently skip
|
|
1020
|
+
// when they no longer match, so a deferred Object.Destroy that lands
|
|
1021
|
+
// after a Reset cannot log spurious over-deregistration errors.
|
|
1022
|
+
private long _resetGeneration;
|
|
1023
|
+
|
|
1024
|
+
/// <summary>
|
|
1025
|
+
/// Bumps the internal reset generation counter without clearing any registrations or sinks.
|
|
1026
|
+
/// </summary>
|
|
1027
|
+
/// <remarks>
|
|
1028
|
+
/// <para>
|
|
1029
|
+
/// Deregister closures returned by the registration entry points capture the value of the
|
|
1030
|
+
/// reset generation at registration time and silently no-op when the captured value differs
|
|
1031
|
+
/// from the bus's current value. Calling this method invalidates every previously-issued
|
|
1032
|
+
/// deregister closure for this bus, which is the desired behaviour after a logical "wipe"
|
|
1033
|
+
/// performed by external state-management code (for example, a custom domain-reload-disabled
|
|
1034
|
+
/// reset utility) that does not wish to clear registrations via <see cref="ResetState"/>.
|
|
1035
|
+
/// </para>
|
|
1036
|
+
/// <para>
|
|
1037
|
+
/// <see cref="DxMessagingStaticState.Reset"/> uses this method to extend the destroy-then-Reset
|
|
1038
|
+
/// race-safety guarantee to user-installed custom global buses without clobbering their state.
|
|
1039
|
+
/// </para>
|
|
1040
|
+
/// </remarks>
|
|
1041
|
+
public void BumpResetGeneration()
|
|
1042
|
+
{
|
|
1043
|
+
unchecked
|
|
1044
|
+
{
|
|
1045
|
+
_resetGeneration++;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1050
|
+
internal static long GetCurrentTouchTick(IMessageBus messageBus)
|
|
1051
|
+
{
|
|
1052
|
+
return messageBus is MessageBus bus ? bus._tickCounter : messageBus?.EmissionId ?? 0;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1056
|
+
internal static long GetResetGeneration(IMessageBus messageBus)
|
|
1057
|
+
{
|
|
1058
|
+
return messageBus is MessageBus bus ? bus._resetGeneration : 0;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1062
|
+
internal static bool IsResetGenerationCurrent(IMessageBus messageBus, long generation)
|
|
1063
|
+
{
|
|
1064
|
+
return messageBus is not MessageBus bus || bus._resetGeneration == generation;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1068
|
+
private long AdvanceTick()
|
|
1069
|
+
{
|
|
1070
|
+
unchecked
|
|
1071
|
+
{
|
|
1072
|
+
_tickCounter++;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
return _tickCounter;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1079
|
+
private static void Touch(HandlerCache<int, HandlerCache> handlers, long tick)
|
|
1080
|
+
{
|
|
1081
|
+
if (handlers != null)
|
|
1082
|
+
{
|
|
1083
|
+
handlers.lastTouchTicks = tick;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1088
|
+
private void MarkDirtyType<TMessage>()
|
|
1089
|
+
where TMessage : IMessage
|
|
1090
|
+
{
|
|
1091
|
+
int typeIndex = MessageHelperIndexer<TMessage>.SequentialId;
|
|
1092
|
+
if (0 <= typeIndex && _dirtyTypeSet.Add(typeIndex))
|
|
1093
|
+
{
|
|
1094
|
+
_dirtyTypes.Add(typeIndex);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1099
|
+
private void MarkDirtyTarget<TMessage>(InstanceId target)
|
|
1100
|
+
where TMessage : IMessage
|
|
1101
|
+
{
|
|
1102
|
+
int typeIndex = MessageHelperIndexer<TMessage>.SequentialId;
|
|
1103
|
+
if (typeIndex < 0)
|
|
1104
|
+
{
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
if (!_dirtyTargets.TryGetValue(typeIndex, out List<InstanceId> targets))
|
|
1109
|
+
{
|
|
1110
|
+
targets = DxPools.InstanceIdLists.Rent();
|
|
1111
|
+
_dirtyTargets[typeIndex] = targets;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
if (!_dirtyTargetSets.TryGetValue(typeIndex, out HashSet<InstanceId> targetSet))
|
|
1115
|
+
{
|
|
1116
|
+
targetSet = DxPools.InstanceIdSets.Rent();
|
|
1117
|
+
_dirtyTargetSets[typeIndex] = targetSet;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
if (targetSet.Add(target))
|
|
1121
|
+
{
|
|
1122
|
+
targets.Add(target);
|
|
1123
|
+
_dirtyTargetHighWaterCounts[typeIndex] = Math.Max(
|
|
1124
|
+
GetDirtyTargetHighWaterCount(typeIndex),
|
|
1125
|
+
targets.Count
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1131
|
+
private void MarkDirtyHandler(MessageHandler handler)
|
|
1132
|
+
{
|
|
1133
|
+
if (handler == null)
|
|
1134
|
+
{
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
_dirtyHandlerTicks[handler] = _tickCounter;
|
|
1139
|
+
if (_dirtyHandlerSet.Add(handler))
|
|
1140
|
+
{
|
|
1141
|
+
_dirtyHandlers.Add(handler);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1146
|
+
private DispatchLease EnterDispatch()
|
|
1147
|
+
{
|
|
1148
|
+
return new DispatchLease(this);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
public TrimResult Trim(bool force = false)
|
|
1152
|
+
{
|
|
1153
|
+
if (!_trimApiEnabled)
|
|
1154
|
+
{
|
|
1155
|
+
return default;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return Sweep(force);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
internal TrimResult Sweep(bool force)
|
|
1162
|
+
{
|
|
1163
|
+
int typeSlotsEvicted = SweepableTypeCacheRegistry[0].Sweep(this, force);
|
|
1164
|
+
_lastContextTypeSlotsEvicted = 0;
|
|
1165
|
+
int targetSlotsEvicted = SweepableTypeCacheRegistry[1].Sweep(this, force);
|
|
1166
|
+
typeSlotsEvicted += _lastContextTypeSlotsEvicted;
|
|
1167
|
+
typeSlotsEvicted += SweepableTypeCacheRegistry[2].Sweep(this, force);
|
|
1168
|
+
typeSlotsEvicted += SweepableTypeCacheRegistry[3].Sweep(this, force);
|
|
1169
|
+
typeSlotsEvicted += SweepableTypeCacheRegistry[4].Sweep(this, force);
|
|
1170
|
+
typeSlotsEvicted += SweepGlobalSlot(force);
|
|
1171
|
+
typeSlotsEvicted += SweepDirtyTypedHandlerSlots(force);
|
|
1172
|
+
if (force)
|
|
1173
|
+
{
|
|
1174
|
+
ClearDirtySweepCandidates();
|
|
1175
|
+
}
|
|
1176
|
+
else
|
|
1177
|
+
{
|
|
1178
|
+
PruneDirtySweepCandidates();
|
|
1179
|
+
}
|
|
1180
|
+
int pooledCollectionsEvicted = DxPools.TrimAll(force);
|
|
1181
|
+
pooledCollectionsEvicted += ContextHandlerByTargetDicts.Trim(
|
|
1182
|
+
force ? 0 : ContextHandlerByTargetDicts.MaxRetained
|
|
1183
|
+
);
|
|
1184
|
+
_lastSweepSeconds = _clock.NowSeconds;
|
|
1185
|
+
|
|
1186
|
+
return new TrimResult(
|
|
1187
|
+
typeSlotsEvicted,
|
|
1188
|
+
targetSlotsEvicted,
|
|
1189
|
+
pooledCollectionsEvicted,
|
|
1190
|
+
OccupiedTypeSlots
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1195
|
+
private void TrySweepIdle(bool advanceTickForIdleAging = false)
|
|
1196
|
+
{
|
|
1197
|
+
if (!_idleEvictionEnabled)
|
|
1198
|
+
{
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
if (!advanceTickForIdleAging && ((unchecked(_emissionId + 1)) & SweepGateMask) != 0)
|
|
1203
|
+
{
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
double nowSeconds = _clock.NowSeconds;
|
|
1208
|
+
if (nowSeconds - _lastSweepSeconds < _evictionTickIntervalSeconds)
|
|
1209
|
+
{
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
if (advanceTickForIdleAging)
|
|
1214
|
+
{
|
|
1215
|
+
_ = AdvanceTick();
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
_ = Sweep(force: false);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
private int SweepDirtyScalarTypeSlots(bool force)
|
|
1222
|
+
{
|
|
1223
|
+
int evicted = 0;
|
|
1224
|
+
for (int i = 0; i < _dirtyTypes.Count; ++i)
|
|
1225
|
+
{
|
|
1226
|
+
int typeIndex = _dirtyTypes[i];
|
|
1227
|
+
for (int sinkIndex = 0; sinkIndex < _scalarSinks.Length; ++sinkIndex)
|
|
1228
|
+
{
|
|
1229
|
+
MessageCache<HandlerCache<int, HandlerCache>> sink = _scalarSinks[sinkIndex];
|
|
1230
|
+
if (
|
|
1231
|
+
sink == null
|
|
1232
|
+
|| !sink.TryGetValueAtIndex(
|
|
1233
|
+
typeIndex,
|
|
1234
|
+
out HandlerCache<int, HandlerCache> handlers
|
|
1235
|
+
)
|
|
1236
|
+
|| handlers.handlers.Count != 0
|
|
1237
|
+
|| HasActiveDispatchSnapshot(handlers.dispatchState)
|
|
1238
|
+
|| !IsIdleForSweep(handlers.lastTouchTicks, force)
|
|
1239
|
+
)
|
|
1240
|
+
{
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
handlers.Clear();
|
|
1245
|
+
sink.RemoveAtIndex(typeIndex);
|
|
1246
|
+
evicted++;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
return evicted;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
private int SweepDirtyInterceptorTypeSlots(
|
|
1254
|
+
MessageCache<InterceptorCache<object>> interceptorsByType,
|
|
1255
|
+
bool force
|
|
1256
|
+
)
|
|
1257
|
+
{
|
|
1258
|
+
int evicted = 0;
|
|
1259
|
+
for (int i = 0; i < _dirtyTypes.Count; ++i)
|
|
1260
|
+
{
|
|
1261
|
+
int typeIndex = _dirtyTypes[i];
|
|
1262
|
+
if (
|
|
1263
|
+
!interceptorsByType.TryGetValueAtIndex(
|
|
1264
|
+
typeIndex,
|
|
1265
|
+
out InterceptorCache<object> interceptors
|
|
1266
|
+
)
|
|
1267
|
+
|| interceptors.handlers.Count != 0
|
|
1268
|
+
|| !IsIdleForSweep(interceptors.lastTouchTicks, force)
|
|
1269
|
+
)
|
|
1270
|
+
{
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
interceptors.Clear();
|
|
1275
|
+
interceptorsByType.RemoveAtIndex(typeIndex);
|
|
1276
|
+
evicted++;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
return evicted;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
private int SweepDirtyTargetSlots(bool force)
|
|
1283
|
+
{
|
|
1284
|
+
int evicted = 0;
|
|
1285
|
+
foreach (KeyValuePair<int, List<InstanceId>> dirtyTargetEntry in _dirtyTargets)
|
|
1286
|
+
{
|
|
1287
|
+
int typeIndex = dirtyTargetEntry.Key;
|
|
1288
|
+
List<InstanceId> targets = dirtyTargetEntry.Value;
|
|
1289
|
+
for (int sinkIndex = 0; sinkIndex < _contextSinks.Length; ++sinkIndex)
|
|
1290
|
+
{
|
|
1291
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sink =
|
|
1292
|
+
_contextSinks[sinkIndex];
|
|
1293
|
+
if (
|
|
1294
|
+
sink == null
|
|
1295
|
+
|| !sink.TryGetValueAtIndex(
|
|
1296
|
+
typeIndex,
|
|
1297
|
+
out Dictionary<
|
|
1298
|
+
InstanceId,
|
|
1299
|
+
HandlerCache<int, HandlerCache>
|
|
1300
|
+
> handlersByTarget
|
|
1301
|
+
)
|
|
1302
|
+
)
|
|
1303
|
+
{
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
for (int targetIndex = 0; targetIndex < targets.Count; ++targetIndex)
|
|
1308
|
+
{
|
|
1309
|
+
InstanceId target = targets[targetIndex];
|
|
1310
|
+
if (
|
|
1311
|
+
!handlersByTarget.TryGetValue(
|
|
1312
|
+
target,
|
|
1313
|
+
out HandlerCache<int, HandlerCache> handlers
|
|
1314
|
+
)
|
|
1315
|
+
|| handlers.handlers.Count != 0
|
|
1316
|
+
|| HasActiveDispatchSnapshot(handlers.dispatchState)
|
|
1317
|
+
|| !IsIdleForSweep(handlers.lastTouchTicks, force)
|
|
1318
|
+
)
|
|
1319
|
+
{
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
handlers.Clear();
|
|
1324
|
+
_ = handlersByTarget.Remove(target);
|
|
1325
|
+
evicted++;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
if (handlersByTarget.Count == 0)
|
|
1329
|
+
{
|
|
1330
|
+
RemoveAndReturnContextMap(sink, typeIndex, handlersByTarget);
|
|
1331
|
+
_lastContextTypeSlotsEvicted++;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
return evicted;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
private int SweepGlobalSlot(bool force)
|
|
1340
|
+
{
|
|
1341
|
+
if (
|
|
1342
|
+
!_globalSlotSweepCandidate
|
|
1343
|
+
|| !_globalSlots.IsEmpty
|
|
1344
|
+
|| HasActiveGlobalDispatchSnapshot()
|
|
1345
|
+
|| !IsIdleForSweep(_globalSlots.lastTouchTicks, force)
|
|
1346
|
+
)
|
|
1347
|
+
{
|
|
1348
|
+
return 0;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// LEGACY: global slot reset keeps the sweep-generation guard for stale
|
|
1352
|
+
// deregistration closures.
|
|
1353
|
+
_globalSlots.Reset();
|
|
1354
|
+
unchecked
|
|
1355
|
+
{
|
|
1356
|
+
_globalSlotSweepGeneration++;
|
|
1357
|
+
}
|
|
1358
|
+
_globalSlotSweepCandidate = false;
|
|
1359
|
+
return 1;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
private int SweepDirtyTypedHandlerSlots(bool force)
|
|
1363
|
+
{
|
|
1364
|
+
int evicted = 0;
|
|
1365
|
+
if (_dispatchDepth > 0)
|
|
1366
|
+
{
|
|
1367
|
+
return evicted;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
int write = 0;
|
|
1371
|
+
int count = _dirtyHandlers.Count;
|
|
1372
|
+
for (int i = 0; i < count; ++i)
|
|
1373
|
+
{
|
|
1374
|
+
MessageHandler handler = _dirtyHandlers[i];
|
|
1375
|
+
if (
|
|
1376
|
+
!force
|
|
1377
|
+
&& (
|
|
1378
|
+
!_dirtyHandlerTicks.TryGetValue(handler, out long lastTouchTicks)
|
|
1379
|
+
|| !IsIdleForSweep(lastTouchTicks, force: false)
|
|
1380
|
+
)
|
|
1381
|
+
)
|
|
1382
|
+
{
|
|
1383
|
+
_dirtyHandlers[write++] = handler;
|
|
1384
|
+
continue;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
evicted += handler.ResetEmptyTypedSlotsForSweep(this);
|
|
1388
|
+
if (handler.HasTypedHandlersForBus(this))
|
|
1389
|
+
{
|
|
1390
|
+
_dirtyHandlers[write++] = handler;
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
_dirtyHandlerSet.Remove(handler);
|
|
1395
|
+
_dirtyHandlerTicks.Remove(handler);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
if (write < count)
|
|
1399
|
+
{
|
|
1400
|
+
_dirtyHandlers.RemoveRange(write, count - write);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
return evicted;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1407
|
+
private bool IsIdleForSweep(long lastTouchTicks, bool force)
|
|
1408
|
+
{
|
|
1409
|
+
return force || unchecked(_tickCounter - lastTouchTicks) > _idleEvictionTicks;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1413
|
+
private bool HasActiveDispatchSnapshot(DispatchState state)
|
|
1414
|
+
{
|
|
1415
|
+
return _dispatchDepth > 0 && state != null && !state.active.IsEmpty;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1419
|
+
private bool HasActiveGlobalDispatchSnapshot()
|
|
1420
|
+
{
|
|
1421
|
+
return HasActiveDispatchSnapshot(_globalSlots.untargetedDispatchState)
|
|
1422
|
+
|| HasActiveDispatchSnapshot(_globalSlots.targetedDispatchState)
|
|
1423
|
+
|| HasActiveDispatchSnapshot(_globalSlots.broadcastDispatchState);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
private void PruneDirtySweepCandidates()
|
|
1427
|
+
{
|
|
1428
|
+
PruneDirtyScalarTypeCandidates();
|
|
1429
|
+
PruneDirtyTargetCandidates();
|
|
1430
|
+
PruneDirtyHandlerCandidates();
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
private void PruneDirtyScalarTypeCandidates()
|
|
1434
|
+
{
|
|
1435
|
+
int write = 0;
|
|
1436
|
+
for (int i = 0; i < _dirtyTypes.Count; ++i)
|
|
1437
|
+
{
|
|
1438
|
+
int typeIndex = _dirtyTypes[i];
|
|
1439
|
+
if (
|
|
1440
|
+
HasFreshEmptyScalarTypeCandidate(typeIndex)
|
|
1441
|
+
|| HasFreshEmptyInterceptorTypeCandidate(typeIndex)
|
|
1442
|
+
)
|
|
1443
|
+
{
|
|
1444
|
+
_dirtyTypes[write++] = typeIndex;
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
_dirtyTypeSet.Remove(typeIndex);
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
if (write < _dirtyTypes.Count)
|
|
1452
|
+
{
|
|
1453
|
+
_dirtyTypes.RemoveRange(write, _dirtyTypes.Count - write);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
private bool HasFreshEmptyScalarTypeCandidate(int typeIndex)
|
|
1458
|
+
{
|
|
1459
|
+
for (int sinkIndex = 0; sinkIndex < _scalarSinks.Length; ++sinkIndex)
|
|
1460
|
+
{
|
|
1461
|
+
MessageCache<HandlerCache<int, HandlerCache>> sink = _scalarSinks[sinkIndex];
|
|
1462
|
+
if (
|
|
1463
|
+
sink != null
|
|
1464
|
+
&& sink.TryGetValueAtIndex(
|
|
1465
|
+
typeIndex,
|
|
1466
|
+
out HandlerCache<int, HandlerCache> handlers
|
|
1467
|
+
)
|
|
1468
|
+
&& handlers.handlers.Count == 0
|
|
1469
|
+
&& !IsIdleForSweep(handlers.lastTouchTicks, force: false)
|
|
1470
|
+
)
|
|
1471
|
+
{
|
|
1472
|
+
return true;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
private bool HasFreshEmptyInterceptorTypeCandidate(int typeIndex)
|
|
1480
|
+
{
|
|
1481
|
+
return HasFreshEmptyInterceptorTypeCandidate(_untargetedInterceptsByType, typeIndex)
|
|
1482
|
+
|| HasFreshEmptyInterceptorTypeCandidate(_targetedInterceptsByType, typeIndex)
|
|
1483
|
+
|| HasFreshEmptyInterceptorTypeCandidate(_broadcastInterceptsByType, typeIndex);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
private bool HasFreshEmptyInterceptorTypeCandidate(
|
|
1487
|
+
MessageCache<InterceptorCache<object>> interceptorsByType,
|
|
1488
|
+
int typeIndex
|
|
1489
|
+
)
|
|
1490
|
+
{
|
|
1491
|
+
return interceptorsByType.TryGetValueAtIndex(
|
|
1492
|
+
typeIndex,
|
|
1493
|
+
out InterceptorCache<object> interceptors
|
|
1494
|
+
)
|
|
1495
|
+
&& interceptors.handlers.Count == 0
|
|
1496
|
+
&& !IsIdleForSweep(interceptors.lastTouchTicks, force: false);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
private void PruneDirtyTargetCandidates()
|
|
1500
|
+
{
|
|
1501
|
+
List<int> emptyTypeKeys = null;
|
|
1502
|
+
foreach (KeyValuePair<int, List<InstanceId>> entry in _dirtyTargets)
|
|
1503
|
+
{
|
|
1504
|
+
int typeIndex = entry.Key;
|
|
1505
|
+
List<InstanceId> targets = entry.Value;
|
|
1506
|
+
_dirtyTargetSets.TryGetValue(typeIndex, out HashSet<InstanceId> targetSet);
|
|
1507
|
+
int write = 0;
|
|
1508
|
+
for (int i = 0; i < targets.Count; ++i)
|
|
1509
|
+
{
|
|
1510
|
+
InstanceId target = targets[i];
|
|
1511
|
+
if (HasFreshEmptyTargetCandidate(typeIndex, target))
|
|
1512
|
+
{
|
|
1513
|
+
targets[write++] = target;
|
|
1514
|
+
continue;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
targetSet?.Remove(target);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
if (write < targets.Count)
|
|
1521
|
+
{
|
|
1522
|
+
targets.RemoveRange(write, targets.Count - write);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
if (targets.Count == 0)
|
|
1526
|
+
{
|
|
1527
|
+
(emptyTypeKeys ??= new List<int>()).Add(typeIndex);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
if (emptyTypeKeys == null)
|
|
1532
|
+
{
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
for (int i = 0; i < emptyTypeKeys.Count; ++i)
|
|
1537
|
+
{
|
|
1538
|
+
int typeIndex = emptyTypeKeys[i];
|
|
1539
|
+
ReturnDirtyTargetCollections(typeIndex);
|
|
1540
|
+
_dirtyTargets.Remove(typeIndex);
|
|
1541
|
+
_dirtyTargetSets.Remove(typeIndex);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
private bool HasFreshEmptyTargetCandidate(int typeIndex, InstanceId target)
|
|
1546
|
+
{
|
|
1547
|
+
for (int sinkIndex = 0; sinkIndex < _contextSinks.Length; ++sinkIndex)
|
|
1548
|
+
{
|
|
1549
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sink =
|
|
1550
|
+
_contextSinks[sinkIndex];
|
|
1551
|
+
if (
|
|
1552
|
+
sink == null
|
|
1553
|
+
|| !sink.TryGetValueAtIndex(
|
|
1554
|
+
typeIndex,
|
|
1555
|
+
out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
1556
|
+
)
|
|
1557
|
+
|| !handlersByTarget.TryGetValue(
|
|
1558
|
+
target,
|
|
1559
|
+
out HandlerCache<int, HandlerCache> handlers
|
|
1560
|
+
)
|
|
1561
|
+
)
|
|
1562
|
+
{
|
|
1563
|
+
continue;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
if (
|
|
1567
|
+
handlers.handlers.Count == 0
|
|
1568
|
+
&& (
|
|
1569
|
+
HasActiveDispatchSnapshot(handlers.dispatchState)
|
|
1570
|
+
|| !IsIdleForSweep(handlers.lastTouchTicks, force: false)
|
|
1571
|
+
)
|
|
1572
|
+
)
|
|
1573
|
+
{
|
|
1574
|
+
return true;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
return false;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
private void PruneDirtyHandlerCandidates()
|
|
1582
|
+
{
|
|
1583
|
+
int write = 0;
|
|
1584
|
+
for (int i = 0; i < _dirtyHandlers.Count; ++i)
|
|
1585
|
+
{
|
|
1586
|
+
MessageHandler handler = _dirtyHandlers[i];
|
|
1587
|
+
if (
|
|
1588
|
+
handler != null
|
|
1589
|
+
&& _dirtyHandlerSet.Contains(handler)
|
|
1590
|
+
&& _dirtyHandlerTicks.TryGetValue(handler, out long lastTouchTicks)
|
|
1591
|
+
&& handler.CountEmptyTypedSlotsForSweep(this) > 0
|
|
1592
|
+
&& !IsIdleForSweep(lastTouchTicks, force: false)
|
|
1593
|
+
)
|
|
1594
|
+
{
|
|
1595
|
+
_dirtyHandlers[write++] = handler;
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
_dirtyHandlerSet.Remove(handler);
|
|
1600
|
+
_dirtyHandlerTicks.Remove(handler);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
if (write < _dirtyHandlers.Count)
|
|
1604
|
+
{
|
|
1605
|
+
_dirtyHandlers.RemoveRange(write, _dirtyHandlers.Count - write);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
private void ClearDirtySweepCandidates()
|
|
1610
|
+
{
|
|
1611
|
+
ClearDirtyTypeCandidatesWithoutEmptySlots();
|
|
1612
|
+
ClearDirtyTargetCandidatesWithoutEmptySlots();
|
|
1613
|
+
ClearDirtyHandlerCandidatesWithoutEmptySlots();
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
private void ReturnDirtyTargetCollections(int typeIndex)
|
|
1617
|
+
{
|
|
1618
|
+
_dirtyTargets.TryGetValue(typeIndex, out List<InstanceId> targets);
|
|
1619
|
+
_dirtyTargetSets.TryGetValue(typeIndex, out HashSet<InstanceId> targetSet);
|
|
1620
|
+
int highWaterCount = GetDirtyTargetHighWaterCount(typeIndex);
|
|
1621
|
+
ReturnDirtyTargetList(targets, highWaterCount);
|
|
1622
|
+
ReturnDirtyTargetSet(targetSet, highWaterCount);
|
|
1623
|
+
_dirtyTargetHighWaterCounts.Remove(typeIndex);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
private void ReturnAllDirtyTargetCollections()
|
|
1627
|
+
{
|
|
1628
|
+
foreach (KeyValuePair<int, List<InstanceId>> entry in _dirtyTargets)
|
|
1629
|
+
{
|
|
1630
|
+
int highWaterCount = GetDirtyTargetHighWaterCount(entry.Key);
|
|
1631
|
+
ReturnDirtyTargetList(entry.Value, highWaterCount);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
foreach (KeyValuePair<int, HashSet<InstanceId>> entry in _dirtyTargetSets)
|
|
1635
|
+
{
|
|
1636
|
+
int highWaterCount = GetDirtyTargetHighWaterCount(entry.Key);
|
|
1637
|
+
ReturnDirtyTargetSet(entry.Value, highWaterCount);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
_dirtyTargetHighWaterCounts.Clear();
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
private int GetDirtyTargetHighWaterCount(int typeIndex)
|
|
1644
|
+
{
|
|
1645
|
+
return _dirtyTargetHighWaterCounts.TryGetValue(typeIndex, out int count) ? count : 0;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
private static void ReturnDirtyTargetList(List<InstanceId> targets, int highWaterCount)
|
|
1649
|
+
{
|
|
1650
|
+
if (targets == null)
|
|
1651
|
+
{
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
if (ShouldDropOversizedPoolEntry(highWaterCount, DxPools.InstanceIdLists.MaxRetained))
|
|
1656
|
+
{
|
|
1657
|
+
targets.Clear();
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
DxPools.InstanceIdLists.Return(targets);
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
private static void ReturnDirtyTargetSet(HashSet<InstanceId> targets, int highWaterCount)
|
|
1665
|
+
{
|
|
1666
|
+
if (targets == null)
|
|
1667
|
+
{
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
if (ShouldDropOversizedPoolEntry(highWaterCount, DxPools.InstanceIdSets.MaxRetained))
|
|
1672
|
+
{
|
|
1673
|
+
targets.Clear();
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
DxPools.InstanceIdSets.Return(targets);
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
internal CollectionPoolDiagnostics GetContextDictPoolDiagnosticsForTesting()
|
|
1681
|
+
{
|
|
1682
|
+
return ContextHandlerByTargetDicts.Snapshot();
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
private Dictionary<InstanceId, HandlerCache<int, HandlerCache>> GetOrRentContextMap<T>(
|
|
1686
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sinks
|
|
1687
|
+
)
|
|
1688
|
+
where T : IMessage
|
|
1689
|
+
{
|
|
1690
|
+
if (
|
|
1691
|
+
sinks.TryGetValue<T>(
|
|
1692
|
+
out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
1693
|
+
)
|
|
1694
|
+
)
|
|
1695
|
+
{
|
|
1696
|
+
return handlersByTarget;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
handlersByTarget = ContextHandlerByTargetDicts.Rent();
|
|
1700
|
+
_contextMapHighWaterCounts[handlersByTarget] = handlersByTarget.Count;
|
|
1701
|
+
sinks.Set<T>(handlersByTarget);
|
|
1702
|
+
return handlersByTarget;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
private void RemoveAndReturnContextMap(
|
|
1706
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sink,
|
|
1707
|
+
int typeIndex,
|
|
1708
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
1709
|
+
)
|
|
1710
|
+
{
|
|
1711
|
+
sink.RemoveAtIndex(typeIndex);
|
|
1712
|
+
ReturnContextMap(handlersByTarget);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
private void ReturnContextMap(
|
|
1716
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
1717
|
+
)
|
|
1718
|
+
{
|
|
1719
|
+
if (handlersByTarget == null)
|
|
1720
|
+
{
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
int highWaterCount = GetContextMapHighWaterCount(handlersByTarget);
|
|
1725
|
+
_contextMapHighWaterCounts.Remove(handlersByTarget);
|
|
1726
|
+
|
|
1727
|
+
foreach (HandlerCache<int, HandlerCache> handlers in handlersByTarget.Values)
|
|
1728
|
+
{
|
|
1729
|
+
handlers?.Clear();
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
handlersByTarget.Clear();
|
|
1733
|
+
if (
|
|
1734
|
+
ShouldDropOversizedPoolEntry(
|
|
1735
|
+
highWaterCount,
|
|
1736
|
+
ContextHandlerByTargetDicts.MaxRetained
|
|
1737
|
+
)
|
|
1738
|
+
)
|
|
1739
|
+
{
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
ContextHandlerByTargetDicts.Return(handlersByTarget);
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
private void ClearAndReturnContextSink(
|
|
1747
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sink
|
|
1748
|
+
)
|
|
1749
|
+
{
|
|
1750
|
+
foreach (
|
|
1751
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget in sink
|
|
1752
|
+
)
|
|
1753
|
+
{
|
|
1754
|
+
ReturnContextMap(handlersByTarget);
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
sink.Clear();
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
private void TrackContextMapHighWater(
|
|
1761
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
1762
|
+
)
|
|
1763
|
+
{
|
|
1764
|
+
if (handlersByTarget == null)
|
|
1765
|
+
{
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
_contextMapHighWaterCounts[handlersByTarget] = Math.Max(
|
|
1770
|
+
GetContextMapHighWaterCount(handlersByTarget),
|
|
1771
|
+
handlersByTarget.Count
|
|
1772
|
+
);
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
private int GetContextMapHighWaterCount(
|
|
1776
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
1777
|
+
)
|
|
1778
|
+
{
|
|
1779
|
+
return _contextMapHighWaterCounts.TryGetValue(handlersByTarget, out int count)
|
|
1780
|
+
? count
|
|
1781
|
+
: handlersByTarget?.Count ?? 0;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
private static bool ShouldDropOversizedPoolEntry(int retainedEntryCount, int maxRetained)
|
|
1785
|
+
{
|
|
1786
|
+
return maxRetained > 0 && retainedEntryCount > maxRetained;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
private void ClearDirtyTypeCandidatesWithoutEmptySlots()
|
|
1790
|
+
{
|
|
1791
|
+
int write = 0;
|
|
1792
|
+
for (int i = 0; i < _dirtyTypes.Count; ++i)
|
|
1793
|
+
{
|
|
1794
|
+
int typeIndex = _dirtyTypes[i];
|
|
1795
|
+
if (HasEmptyScalarTypeCandidate(typeIndex))
|
|
1796
|
+
{
|
|
1797
|
+
_dirtyTypes[write++] = typeIndex;
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
_dirtyTypeSet.Remove(typeIndex);
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
if (write < _dirtyTypes.Count)
|
|
1805
|
+
{
|
|
1806
|
+
_dirtyTypes.RemoveRange(write, _dirtyTypes.Count - write);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
private bool HasEmptyScalarTypeCandidate(int typeIndex)
|
|
1811
|
+
{
|
|
1812
|
+
for (int sinkIndex = 0; sinkIndex < _scalarSinks.Length; ++sinkIndex)
|
|
1813
|
+
{
|
|
1814
|
+
MessageCache<HandlerCache<int, HandlerCache>> sink = _scalarSinks[sinkIndex];
|
|
1815
|
+
if (
|
|
1816
|
+
sink != null
|
|
1817
|
+
&& sink.TryGetValueAtIndex(
|
|
1818
|
+
typeIndex,
|
|
1819
|
+
out HandlerCache<int, HandlerCache> handlers
|
|
1820
|
+
)
|
|
1821
|
+
&& handlers.handlers.Count == 0
|
|
1822
|
+
)
|
|
1823
|
+
{
|
|
1824
|
+
return true;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
return HasEmptyInterceptorTypeCandidate(_untargetedInterceptsByType, typeIndex)
|
|
1829
|
+
|| HasEmptyInterceptorTypeCandidate(_targetedInterceptsByType, typeIndex)
|
|
1830
|
+
|| HasEmptyInterceptorTypeCandidate(_broadcastInterceptsByType, typeIndex);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
private static bool HasEmptyInterceptorTypeCandidate(
|
|
1834
|
+
MessageCache<InterceptorCache<object>> interceptorsByType,
|
|
1835
|
+
int typeIndex
|
|
1836
|
+
)
|
|
1837
|
+
{
|
|
1838
|
+
return interceptorsByType.TryGetValueAtIndex(
|
|
1839
|
+
typeIndex,
|
|
1840
|
+
out InterceptorCache<object> interceptors
|
|
1841
|
+
)
|
|
1842
|
+
&& interceptors.handlers.Count == 0;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
private void ClearDirtyTargetCandidatesWithoutEmptySlots()
|
|
1846
|
+
{
|
|
1847
|
+
List<int> emptyTypeKeys = null;
|
|
1848
|
+
foreach (KeyValuePair<int, List<InstanceId>> entry in _dirtyTargets)
|
|
1849
|
+
{
|
|
1850
|
+
int typeIndex = entry.Key;
|
|
1851
|
+
List<InstanceId> targets = entry.Value;
|
|
1852
|
+
_dirtyTargetSets.TryGetValue(typeIndex, out HashSet<InstanceId> targetSet);
|
|
1853
|
+
int write = 0;
|
|
1854
|
+
for (int i = 0; i < targets.Count; ++i)
|
|
1855
|
+
{
|
|
1856
|
+
InstanceId target = targets[i];
|
|
1857
|
+
if (HasEmptyTargetCandidate(typeIndex, target))
|
|
1858
|
+
{
|
|
1859
|
+
targets[write++] = target;
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
targetSet?.Remove(target);
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
if (write < targets.Count)
|
|
1867
|
+
{
|
|
1868
|
+
targets.RemoveRange(write, targets.Count - write);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
if (targets.Count == 0)
|
|
1872
|
+
{
|
|
1873
|
+
(emptyTypeKeys ??= new List<int>()).Add(typeIndex);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
if (emptyTypeKeys == null)
|
|
1878
|
+
{
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
for (int i = 0; i < emptyTypeKeys.Count; ++i)
|
|
1883
|
+
{
|
|
1884
|
+
int typeIndex = emptyTypeKeys[i];
|
|
1885
|
+
ReturnDirtyTargetCollections(typeIndex);
|
|
1886
|
+
_dirtyTargets.Remove(typeIndex);
|
|
1887
|
+
_dirtyTargetSets.Remove(typeIndex);
|
|
305
1888
|
}
|
|
306
1889
|
}
|
|
307
1890
|
|
|
308
|
-
|
|
1891
|
+
private bool HasEmptyTargetCandidate(int typeIndex, InstanceId target)
|
|
309
1892
|
{
|
|
310
|
-
|
|
1893
|
+
for (int sinkIndex = 0; sinkIndex < _contextSinks.Length; ++sinkIndex)
|
|
311
1894
|
{
|
|
312
|
-
int
|
|
313
|
-
|
|
314
|
-
|
|
1895
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sink =
|
|
1896
|
+
_contextSinks[sinkIndex];
|
|
1897
|
+
if (
|
|
1898
|
+
sink != null
|
|
1899
|
+
&& sink.TryGetValueAtIndex(
|
|
1900
|
+
typeIndex,
|
|
1901
|
+
out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
1902
|
+
)
|
|
1903
|
+
&& handlersByTarget.TryGetValue(
|
|
1904
|
+
target,
|
|
1905
|
+
out HandlerCache<int, HandlerCache> handlers
|
|
1906
|
+
)
|
|
1907
|
+
&& handlers.handlers.Count == 0
|
|
315
1908
|
)
|
|
316
1909
|
{
|
|
317
|
-
|
|
1910
|
+
return true;
|
|
318
1911
|
}
|
|
319
|
-
|
|
320
|
-
return count;
|
|
321
1912
|
}
|
|
322
|
-
}
|
|
323
1913
|
|
|
324
|
-
|
|
1914
|
+
return false;
|
|
1915
|
+
}
|
|
325
1916
|
|
|
326
|
-
|
|
1917
|
+
private void ClearDirtyHandlerCandidatesWithoutEmptySlots()
|
|
327
1918
|
{
|
|
328
|
-
|
|
1919
|
+
int write = 0;
|
|
1920
|
+
for (int i = 0; i < _dirtyHandlers.Count; ++i)
|
|
329
1921
|
{
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
1922
|
+
MessageHandler handler = _dirtyHandlers[i];
|
|
1923
|
+
if (
|
|
1924
|
+
handler != null
|
|
1925
|
+
&& _dirtyHandlerSet.Contains(handler)
|
|
1926
|
+
&& handler.CountEmptyTypedSlotsForSweep(this) > 0
|
|
333
1927
|
)
|
|
334
1928
|
{
|
|
335
|
-
|
|
1929
|
+
_dirtyHandlers[write++] = handler;
|
|
1930
|
+
continue;
|
|
336
1931
|
}
|
|
337
1932
|
|
|
338
|
-
|
|
1933
|
+
_dirtyHandlerSet.Remove(handler);
|
|
1934
|
+
_dirtyHandlerTicks.Remove(handler);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
if (write < _dirtyHandlers.Count)
|
|
1938
|
+
{
|
|
1939
|
+
_dirtyHandlers.RemoveRange(write, _dirtyHandlers.Count - write);
|
|
339
1940
|
}
|
|
340
1941
|
}
|
|
341
1942
|
|
|
342
|
-
|
|
1943
|
+
private int CountDirtyEmptyTypedHandlerSlots()
|
|
343
1944
|
{
|
|
344
|
-
|
|
1945
|
+
int count = 0;
|
|
1946
|
+
for (int i = 0; i < _dirtyHandlers.Count; ++i)
|
|
345
1947
|
{
|
|
346
|
-
|
|
347
|
-
|
|
1948
|
+
MessageHandler handler = _dirtyHandlers[i];
|
|
1949
|
+
if (handler != null && _dirtyHandlerSet.Contains(handler))
|
|
348
1950
|
{
|
|
349
|
-
count +=
|
|
1951
|
+
count += handler.CountEmptyTypedSlotsForSweep(this);
|
|
350
1952
|
}
|
|
351
|
-
|
|
352
|
-
return count;
|
|
353
1953
|
}
|
|
354
|
-
}
|
|
355
1954
|
|
|
356
|
-
|
|
357
|
-
{
|
|
358
|
-
get => _diagnosticsMode;
|
|
359
|
-
set => _diagnosticsMode = value;
|
|
1955
|
+
return count;
|
|
360
1956
|
}
|
|
361
1957
|
|
|
362
|
-
private static
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
where T : IUntargetedMessage;
|
|
375
|
-
private delegate void FastTargetedBroadcast<T>(ref InstanceId target, ref T message)
|
|
376
|
-
where T : ITargetedMessage;
|
|
377
|
-
private delegate void FastSourcedBroadcast<T>(ref InstanceId target, ref T message)
|
|
378
|
-
where T : IBroadcastMessage;
|
|
379
|
-
|
|
380
|
-
public RegistrationLog Log => _log;
|
|
381
|
-
|
|
382
|
-
private readonly MessageCache<HandlerCache<int, HandlerCache>> _sinks = new();
|
|
383
|
-
private readonly MessageCache<
|
|
384
|
-
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
385
|
-
> _targetedSinks = new();
|
|
386
|
-
private readonly MessageCache<
|
|
387
|
-
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
388
|
-
> _broadcastSinks = new();
|
|
389
|
-
private readonly MessageCache<HandlerCache<int, HandlerCache>> _postProcessingSinks = new();
|
|
390
|
-
private readonly MessageCache<
|
|
391
|
-
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
392
|
-
> _postProcessingTargetedSinks = new();
|
|
393
|
-
private readonly MessageCache<
|
|
394
|
-
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
395
|
-
> _postProcessingBroadcastSinks = new();
|
|
396
|
-
private readonly MessageCache<
|
|
397
|
-
HandlerCache<int, HandlerCache>
|
|
398
|
-
> _postProcessingTargetedWithoutTargetingSinks = new();
|
|
399
|
-
private readonly MessageCache<
|
|
400
|
-
HandlerCache<int, HandlerCache>
|
|
401
|
-
> _postProcessingBroadcastWithoutSourceSinks = new();
|
|
402
|
-
private readonly HandlerCache _globalSinks = new();
|
|
403
|
-
|
|
404
|
-
// Interceptors split by category to avoid mixing types
|
|
405
|
-
private readonly MessageCache<InterceptorCache<object>> _untargetedInterceptsByType = new();
|
|
406
|
-
private readonly MessageCache<InterceptorCache<object>> _targetedInterceptsByType = new();
|
|
407
|
-
private readonly MessageCache<InterceptorCache<object>> _broadcastInterceptsByType = new();
|
|
408
|
-
private readonly Dictionary<object, Dictionary<int, int>> _uniqueInterceptorsAndPriorities =
|
|
409
|
-
new();
|
|
410
|
-
|
|
411
|
-
private readonly Dictionary<Type, object> _broadcastMethodsByType = new();
|
|
412
|
-
private readonly Stack<List<object>> _innerInterceptorsStack = new();
|
|
413
|
-
|
|
414
|
-
private readonly Dictionary<
|
|
415
|
-
Type,
|
|
416
|
-
Dictionary<MethodSignatureKey, Action<MonoBehaviour, object[]>>
|
|
417
|
-
> _methodCache = new();
|
|
418
|
-
|
|
419
|
-
#if UNITY_2021_3_OR_NEWER
|
|
420
|
-
private readonly HashSet<MonoBehaviour> _recipientCache = new();
|
|
421
|
-
private readonly List<MonoBehaviour> _componentCache = new();
|
|
422
|
-
#endif
|
|
423
|
-
|
|
424
|
-
private readonly RegistrationLog _log = new();
|
|
425
|
-
internal readonly CyclicBuffer<MessageEmissionData> _emissionBuffer = new(
|
|
426
|
-
GlobalMessageBufferSize
|
|
427
|
-
);
|
|
1958
|
+
private static int CountOccupiedInterceptorTypeSlots(
|
|
1959
|
+
MessageCache<InterceptorCache<object>> cache
|
|
1960
|
+
)
|
|
1961
|
+
{
|
|
1962
|
+
int count = 0;
|
|
1963
|
+
foreach (InterceptorCache<object> entry in cache)
|
|
1964
|
+
{
|
|
1965
|
+
if (entry != null)
|
|
1966
|
+
{
|
|
1967
|
+
count++;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
428
1970
|
|
|
429
|
-
|
|
430
|
-
|
|
1971
|
+
return count;
|
|
1972
|
+
}
|
|
431
1973
|
|
|
432
1974
|
internal void ResetState()
|
|
433
1975
|
{
|
|
1976
|
+
ResetTypedSlotsForReferencedHandlers();
|
|
434
1977
|
_emissionId = 0;
|
|
1978
|
+
_tickCounter = 0;
|
|
435
1979
|
_diagnosticsMode = ShouldEnableDiagnostics();
|
|
436
1980
|
_loggedReflexiveWarning = false;
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
1981
|
+
BumpResetGeneration();
|
|
1982
|
+
|
|
1983
|
+
_scalarSinks[BusSinkIndex.UntargetedHandleDefault].Clear();
|
|
1984
|
+
_scalarSinks[BusSinkIndex.BroadcastHandleWithoutContext].Clear();
|
|
1985
|
+
_scalarSinks[BusSinkIndex.TargetedHandleWithoutContext].Clear();
|
|
1986
|
+
ClearAndReturnContextSink(_contextSinks[BusContextIndex.TargetedHandleDefault]);
|
|
1987
|
+
ClearAndReturnContextSink(_contextSinks[BusContextIndex.BroadcastHandleDefault]);
|
|
1988
|
+
_scalarSinks[BusSinkIndex.UntargetedPostProcessDefault].Clear();
|
|
1989
|
+
ClearAndReturnContextSink(_contextSinks[BusContextIndex.TargetedPostProcessDefault]);
|
|
1990
|
+
ClearAndReturnContextSink(_contextSinks[BusContextIndex.BroadcastPostProcessDefault]);
|
|
1991
|
+
_scalarSinks[BusSinkIndex.TargetedPostProcessWithoutContext].Clear();
|
|
1992
|
+
_scalarSinks[BusSinkIndex.BroadcastPostProcessWithoutContext].Clear();
|
|
1993
|
+
_globalSlots.Clear();
|
|
447
1994
|
|
|
448
1995
|
_untargetedInterceptsByType.Clear();
|
|
449
1996
|
_targetedInterceptsByType.Clear();
|
|
@@ -452,6 +1999,18 @@ namespace DxMessaging.Core.MessageBus
|
|
|
452
1999
|
_broadcastMethodsByType.Clear();
|
|
453
2000
|
_innerInterceptorsStack.Clear();
|
|
454
2001
|
_methodCache.Clear();
|
|
2002
|
+
_dirtyTypes.Clear();
|
|
2003
|
+
ReturnAllDirtyTargetCollections();
|
|
2004
|
+
_dirtyTargets.Clear();
|
|
2005
|
+
_dirtyTypeSet.Clear();
|
|
2006
|
+
_dirtyTargetSets.Clear();
|
|
2007
|
+
_dirtyTargetHighWaterCounts.Clear();
|
|
2008
|
+
_contextMapHighWaterCounts.Clear();
|
|
2009
|
+
_dirtyHandlers.Clear();
|
|
2010
|
+
_dirtyHandlerSet.Clear();
|
|
2011
|
+
_dirtyHandlerTicks.Clear();
|
|
2012
|
+
_globalSlotSweepCandidate = false;
|
|
2013
|
+
_lastSweepSeconds = _clock.NowSeconds;
|
|
455
2014
|
|
|
456
2015
|
#if UNITY_2021_3_OR_NEWER
|
|
457
2016
|
_recipientCache.Clear();
|
|
@@ -465,13 +2024,93 @@ namespace DxMessaging.Core.MessageBus
|
|
|
465
2024
|
_emissionBuffer.Clear();
|
|
466
2025
|
}
|
|
467
2026
|
|
|
2027
|
+
private void ResetTypedSlotsForReferencedHandlers()
|
|
2028
|
+
{
|
|
2029
|
+
HashSet<MessageHandler> handlers = new HashSet<MessageHandler>();
|
|
2030
|
+
AddHandlersFromScalarSinks(handlers);
|
|
2031
|
+
AddHandlersFromContextSinks(handlers);
|
|
2032
|
+
|
|
2033
|
+
foreach (MessageHandler handler in _globalSlots.sharedHandlers.Keys)
|
|
2034
|
+
{
|
|
2035
|
+
handlers.Add(handler);
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
foreach (MessageHandler handler in handlers)
|
|
2039
|
+
{
|
|
2040
|
+
handler.ResetAllTypedSlotsForBusReset(this);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
private void AddHandlersFromScalarSinks(HashSet<MessageHandler> handlers)
|
|
2045
|
+
{
|
|
2046
|
+
foreach (MessageCache<HandlerCache<int, HandlerCache>> sink in _scalarSinks)
|
|
2047
|
+
{
|
|
2048
|
+
if (sink == null)
|
|
2049
|
+
{
|
|
2050
|
+
continue;
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
foreach (HandlerCache<int, HandlerCache> handlersByPriority in sink)
|
|
2054
|
+
{
|
|
2055
|
+
AddHandlersFromPriorityCache(handlersByPriority, handlers);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
private void AddHandlersFromContextSinks(HashSet<MessageHandler> handlers)
|
|
2061
|
+
{
|
|
2062
|
+
foreach (
|
|
2063
|
+
MessageCache<
|
|
2064
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
2065
|
+
> sink in _contextSinks
|
|
2066
|
+
)
|
|
2067
|
+
{
|
|
2068
|
+
foreach (
|
|
2069
|
+
Dictionary<
|
|
2070
|
+
InstanceId,
|
|
2071
|
+
HandlerCache<int, HandlerCache>
|
|
2072
|
+
> handlersByContext in sink
|
|
2073
|
+
)
|
|
2074
|
+
{
|
|
2075
|
+
foreach (
|
|
2076
|
+
HandlerCache<
|
|
2077
|
+
int,
|
|
2078
|
+
HandlerCache
|
|
2079
|
+
> handlersByPriority in handlersByContext.Values
|
|
2080
|
+
)
|
|
2081
|
+
{
|
|
2082
|
+
AddHandlersFromPriorityCache(handlersByPriority, handlers);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
private static void AddHandlersFromPriorityCache(
|
|
2089
|
+
HandlerCache<int, HandlerCache> handlersByPriority,
|
|
2090
|
+
HashSet<MessageHandler> handlers
|
|
2091
|
+
)
|
|
2092
|
+
{
|
|
2093
|
+
if (handlersByPriority == null)
|
|
2094
|
+
{
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
foreach (HandlerCache cache in handlersByPriority.handlers.Values)
|
|
2099
|
+
{
|
|
2100
|
+
foreach (MessageHandler handler in cache.handlers.Keys)
|
|
2101
|
+
{
|
|
2102
|
+
handlers.Add(handler);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
|
|
468
2107
|
/// <inheritdoc />
|
|
469
2108
|
public Action RegisterUntargeted<T>(MessageHandler messageHandler, int priority = 0)
|
|
470
2109
|
where T : IUntargetedMessage
|
|
471
2110
|
{
|
|
472
2111
|
return InternalRegisterUntargeted<T>(
|
|
473
2112
|
messageHandler,
|
|
474
|
-
|
|
2113
|
+
_scalarSinks[BusSinkIndex.UntargetedHandleDefault],
|
|
475
2114
|
RegistrationMethod.Untargeted,
|
|
476
2115
|
priority
|
|
477
2116
|
);
|
|
@@ -488,7 +2127,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
488
2127
|
return InternalRegisterWithContext<T>(
|
|
489
2128
|
target,
|
|
490
2129
|
messageHandler,
|
|
491
|
-
|
|
2130
|
+
_contextSinks[BusContextIndex.TargetedHandleDefault],
|
|
492
2131
|
RegistrationMethod.Targeted,
|
|
493
2132
|
priority
|
|
494
2133
|
);
|
|
@@ -505,7 +2144,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
505
2144
|
return InternalRegisterWithContext<T>(
|
|
506
2145
|
source,
|
|
507
2146
|
messageHandler,
|
|
508
|
-
|
|
2147
|
+
_contextSinks[BusContextIndex.BroadcastHandleDefault],
|
|
509
2148
|
RegistrationMethod.Broadcast,
|
|
510
2149
|
priority
|
|
511
2150
|
);
|
|
@@ -520,7 +2159,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
520
2159
|
{
|
|
521
2160
|
return InternalRegisterUntargeted<T>(
|
|
522
2161
|
messageHandler,
|
|
523
|
-
|
|
2162
|
+
_scalarSinks[BusSinkIndex.BroadcastHandleWithoutContext],
|
|
524
2163
|
RegistrationMethod.BroadcastWithoutSource,
|
|
525
2164
|
priority
|
|
526
2165
|
);
|
|
@@ -535,7 +2174,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
535
2174
|
{
|
|
536
2175
|
return InternalRegisterUntargeted<T>(
|
|
537
2176
|
messageHandler,
|
|
538
|
-
|
|
2177
|
+
_scalarSinks[BusSinkIndex.TargetedHandleWithoutContext],
|
|
539
2178
|
RegistrationMethod.TargetedWithoutTargeting,
|
|
540
2179
|
priority
|
|
541
2180
|
);
|
|
@@ -544,11 +2183,21 @@ namespace DxMessaging.Core.MessageBus
|
|
|
544
2183
|
/// <inheritdoc />
|
|
545
2184
|
public Action RegisterGlobalAcceptAll(MessageHandler messageHandler)
|
|
546
2185
|
{
|
|
547
|
-
|
|
548
|
-
|
|
2186
|
+
long touchTick = AdvanceTick();
|
|
2187
|
+
_globalSlots.lastTouchTicks = touchTick;
|
|
2188
|
+
_globalSlots.version++;
|
|
2189
|
+
int count = _globalSlots.sharedHandlers.GetValueOrDefault(messageHandler, 0);
|
|
549
2190
|
|
|
550
2191
|
Type type = typeof(IMessage);
|
|
551
|
-
|
|
2192
|
+
_globalSlots.sharedHandlers[messageHandler] = count + 1;
|
|
2193
|
+
// liveCount mirrors sharedHandlers.Count at every stable
|
|
2194
|
+
// observation point; only newly-inserted handlers (the 0 -> 1
|
|
2195
|
+
// transition in the per-handler refcount) advance it. See
|
|
2196
|
+
// BusGlobalSlot.liveCount xmldoc for the full invariant.
|
|
2197
|
+
if (count == 0)
|
|
2198
|
+
{
|
|
2199
|
+
_globalSlots.liveCount++;
|
|
2200
|
+
}
|
|
552
2201
|
_log.Log(
|
|
553
2202
|
new MessagingRegistration(
|
|
554
2203
|
messageHandler.owner,
|
|
@@ -560,23 +2209,37 @@ namespace DxMessaging.Core.MessageBus
|
|
|
560
2209
|
|
|
561
2210
|
StageGlobalDispatchSnapshot<IUntargetedMessage>(
|
|
562
2211
|
this,
|
|
563
|
-
|
|
564
|
-
|
|
2212
|
+
_globalSlots,
|
|
2213
|
+
DispatchKind.Untargeted
|
|
565
2214
|
);
|
|
566
2215
|
StageGlobalDispatchSnapshot<ITargetedMessage>(
|
|
567
2216
|
this,
|
|
568
|
-
|
|
569
|
-
|
|
2217
|
+
_globalSlots,
|
|
2218
|
+
DispatchKind.Targeted
|
|
570
2219
|
);
|
|
571
2220
|
StageGlobalDispatchSnapshot<IBroadcastMessage>(
|
|
572
2221
|
this,
|
|
573
|
-
|
|
574
|
-
|
|
2222
|
+
_globalSlots,
|
|
2223
|
+
DispatchKind.Broadcast
|
|
575
2224
|
);
|
|
2225
|
+
DebugAssertGlobalLiveCount();
|
|
576
2226
|
|
|
2227
|
+
long capturedGeneration = _resetGeneration;
|
|
2228
|
+
long capturedSweepGeneration = _globalSlotSweepGeneration;
|
|
577
2229
|
return () =>
|
|
578
2230
|
{
|
|
579
|
-
|
|
2231
|
+
// Generation guard: see InternalRegisterUntargeted for the
|
|
2232
|
+
// rationale. Skip silently when the closure outlived a Reset.
|
|
2233
|
+
if (
|
|
2234
|
+
capturedGeneration != _resetGeneration
|
|
2235
|
+
|| capturedSweepGeneration != _globalSlotSweepGeneration
|
|
2236
|
+
)
|
|
2237
|
+
{
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
long deregisterTouchTick = AdvanceTick();
|
|
2242
|
+
_globalSlots.version++;
|
|
580
2243
|
_log.Log(
|
|
581
2244
|
new MessagingRegistration(
|
|
582
2245
|
messageHandler.owner,
|
|
@@ -585,7 +2248,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
585
2248
|
RegistrationMethod.GlobalAcceptAll
|
|
586
2249
|
)
|
|
587
2250
|
);
|
|
588
|
-
if (!
|
|
2251
|
+
if (!_globalSlots.sharedHandlers.TryGetValue(messageHandler, out count))
|
|
589
2252
|
{
|
|
590
2253
|
if (MessagingDebug.enabled)
|
|
591
2254
|
{
|
|
@@ -599,30 +2262,39 @@ namespace DxMessaging.Core.MessageBus
|
|
|
599
2262
|
return;
|
|
600
2263
|
}
|
|
601
2264
|
|
|
2265
|
+
_globalSlots.lastTouchTicks = deregisterTouchTick;
|
|
602
2266
|
if (count <= 1)
|
|
603
2267
|
{
|
|
604
|
-
_ =
|
|
2268
|
+
_ = _globalSlots.sharedHandlers.Remove(messageHandler);
|
|
2269
|
+
MarkDirtyHandler(messageHandler);
|
|
2270
|
+
_globalSlotSweepCandidate = true;
|
|
2271
|
+
// Final-removal of this handler from sharedHandlers is the
|
|
2272
|
+
// 1 -> 0 transition that mirrors back into liveCount.
|
|
2273
|
+
// Partial deregistration (count > 1) leaves liveCount
|
|
2274
|
+
// alone -- the dictionary entry is still present.
|
|
2275
|
+
_globalSlots.liveCount--;
|
|
605
2276
|
}
|
|
606
2277
|
else
|
|
607
2278
|
{
|
|
608
|
-
|
|
2279
|
+
_globalSlots.sharedHandlers[messageHandler] = count - 1;
|
|
609
2280
|
}
|
|
610
2281
|
|
|
611
2282
|
StageGlobalDispatchSnapshot<IUntargetedMessage>(
|
|
612
2283
|
this,
|
|
613
|
-
|
|
614
|
-
|
|
2284
|
+
_globalSlots,
|
|
2285
|
+
DispatchKind.Untargeted
|
|
615
2286
|
);
|
|
616
2287
|
StageGlobalDispatchSnapshot<ITargetedMessage>(
|
|
617
2288
|
this,
|
|
618
|
-
|
|
619
|
-
|
|
2289
|
+
_globalSlots,
|
|
2290
|
+
DispatchKind.Targeted
|
|
620
2291
|
);
|
|
621
2292
|
StageGlobalDispatchSnapshot<IBroadcastMessage>(
|
|
622
2293
|
this,
|
|
623
|
-
|
|
624
|
-
|
|
2294
|
+
_globalSlots,
|
|
2295
|
+
DispatchKind.Broadcast
|
|
625
2296
|
);
|
|
2297
|
+
DebugAssertGlobalLiveCount();
|
|
626
2298
|
};
|
|
627
2299
|
}
|
|
628
2300
|
|
|
@@ -633,8 +2305,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
633
2305
|
)
|
|
634
2306
|
where T : IUntargetedMessage
|
|
635
2307
|
{
|
|
2308
|
+
_ = AdvanceTick();
|
|
636
2309
|
InterceptorCache<object> prioritizedInterceptors =
|
|
637
2310
|
_untargetedInterceptsByType.GetOrAdd<T>();
|
|
2311
|
+
InterceptorCache<object> capturedInterceptors = prioritizedInterceptors;
|
|
2312
|
+
prioritizedInterceptors.lastTouchTicks = _tickCounter;
|
|
2313
|
+
MarkDirtyType<T>();
|
|
638
2314
|
|
|
639
2315
|
if (
|
|
640
2316
|
!_uniqueInterceptorsAndPriorities.TryGetValue(
|
|
@@ -676,8 +2352,28 @@ namespace DxMessaging.Core.MessageBus
|
|
|
676
2352
|
)
|
|
677
2353
|
);
|
|
678
2354
|
|
|
2355
|
+
long capturedGeneration = _resetGeneration;
|
|
679
2356
|
return () =>
|
|
680
2357
|
{
|
|
2358
|
+
// Generation guard: see InternalRegisterUntargeted for the
|
|
2359
|
+
// rationale. Skip silently when the closure outlived a Reset.
|
|
2360
|
+
if (capturedGeneration != _resetGeneration)
|
|
2361
|
+
{
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
if (
|
|
2365
|
+
IsStaleInterceptorDeregisterAfterSweep<T>(
|
|
2366
|
+
_untargetedInterceptsByType,
|
|
2367
|
+
capturedInterceptors
|
|
2368
|
+
)
|
|
2369
|
+
)
|
|
2370
|
+
{
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
_ = AdvanceTick();
|
|
2375
|
+
prioritizedInterceptors.lastTouchTicks = _tickCounter;
|
|
2376
|
+
MarkDirtyType<T>();
|
|
681
2377
|
_log.Log(
|
|
682
2378
|
new MessagingRegistration(
|
|
683
2379
|
InstanceId.EmptyId,
|
|
@@ -755,8 +2451,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
755
2451
|
)
|
|
756
2452
|
where T : ITargetedMessage
|
|
757
2453
|
{
|
|
2454
|
+
_ = AdvanceTick();
|
|
758
2455
|
InterceptorCache<object> prioritizedInterceptors =
|
|
759
2456
|
_targetedInterceptsByType.GetOrAdd<T>();
|
|
2457
|
+
InterceptorCache<object> capturedInterceptors = prioritizedInterceptors;
|
|
2458
|
+
prioritizedInterceptors.lastTouchTicks = _tickCounter;
|
|
2459
|
+
MarkDirtyType<T>();
|
|
760
2460
|
|
|
761
2461
|
if (
|
|
762
2462
|
!_uniqueInterceptorsAndPriorities.TryGetValue(
|
|
@@ -798,8 +2498,28 @@ namespace DxMessaging.Core.MessageBus
|
|
|
798
2498
|
)
|
|
799
2499
|
);
|
|
800
2500
|
|
|
2501
|
+
long capturedGeneration = _resetGeneration;
|
|
801
2502
|
return () =>
|
|
802
2503
|
{
|
|
2504
|
+
// Generation guard: see InternalRegisterUntargeted for the
|
|
2505
|
+
// rationale. Skip silently when the closure outlived a Reset.
|
|
2506
|
+
if (capturedGeneration != _resetGeneration)
|
|
2507
|
+
{
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
if (
|
|
2511
|
+
IsStaleInterceptorDeregisterAfterSweep<T>(
|
|
2512
|
+
_targetedInterceptsByType,
|
|
2513
|
+
capturedInterceptors
|
|
2514
|
+
)
|
|
2515
|
+
)
|
|
2516
|
+
{
|
|
2517
|
+
return;
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
_ = AdvanceTick();
|
|
2521
|
+
prioritizedInterceptors.lastTouchTicks = _tickCounter;
|
|
2522
|
+
MarkDirtyType<T>();
|
|
803
2523
|
_log.Log(
|
|
804
2524
|
new MessagingRegistration(
|
|
805
2525
|
InstanceId.EmptyId,
|
|
@@ -877,8 +2597,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
877
2597
|
)
|
|
878
2598
|
where T : IBroadcastMessage
|
|
879
2599
|
{
|
|
2600
|
+
_ = AdvanceTick();
|
|
880
2601
|
InterceptorCache<object> prioritizedInterceptors =
|
|
881
2602
|
_broadcastInterceptsByType.GetOrAdd<T>();
|
|
2603
|
+
InterceptorCache<object> capturedInterceptors = prioritizedInterceptors;
|
|
2604
|
+
prioritizedInterceptors.lastTouchTicks = _tickCounter;
|
|
2605
|
+
MarkDirtyType<T>();
|
|
882
2606
|
|
|
883
2607
|
if (
|
|
884
2608
|
!_uniqueInterceptorsAndPriorities.TryGetValue(
|
|
@@ -920,8 +2644,28 @@ namespace DxMessaging.Core.MessageBus
|
|
|
920
2644
|
)
|
|
921
2645
|
);
|
|
922
2646
|
|
|
2647
|
+
long capturedGeneration = _resetGeneration;
|
|
923
2648
|
return () =>
|
|
924
2649
|
{
|
|
2650
|
+
// Generation guard: see InternalRegisterUntargeted for the
|
|
2651
|
+
// rationale. Skip silently when the closure outlived a Reset.
|
|
2652
|
+
if (capturedGeneration != _resetGeneration)
|
|
2653
|
+
{
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
|
+
if (
|
|
2657
|
+
IsStaleInterceptorDeregisterAfterSweep<T>(
|
|
2658
|
+
_broadcastInterceptsByType,
|
|
2659
|
+
capturedInterceptors
|
|
2660
|
+
)
|
|
2661
|
+
)
|
|
2662
|
+
{
|
|
2663
|
+
return;
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
_ = AdvanceTick();
|
|
2667
|
+
prioritizedInterceptors.lastTouchTicks = _tickCounter;
|
|
2668
|
+
MarkDirtyType<T>();
|
|
925
2669
|
_log.Log(
|
|
926
2670
|
new MessagingRegistration(
|
|
927
2671
|
InstanceId.EmptyId,
|
|
@@ -992,6 +2736,17 @@ namespace DxMessaging.Core.MessageBus
|
|
|
992
2736
|
};
|
|
993
2737
|
}
|
|
994
2738
|
|
|
2739
|
+
private bool IsStaleInterceptorDeregisterAfterSweep<T>(
|
|
2740
|
+
MessageCache<InterceptorCache<object>> interceptorsByType,
|
|
2741
|
+
InterceptorCache<object> capturedInterceptors
|
|
2742
|
+
)
|
|
2743
|
+
where T : IMessage
|
|
2744
|
+
{
|
|
2745
|
+
return !interceptorsByType.TryGetValue<T>(
|
|
2746
|
+
out InterceptorCache<object> currentInterceptors
|
|
2747
|
+
) || !ReferenceEquals(currentInterceptors, capturedInterceptors);
|
|
2748
|
+
}
|
|
2749
|
+
|
|
995
2750
|
/// <inheritdoc />
|
|
996
2751
|
public Action RegisterUntargetedPostProcessor<T>(
|
|
997
2752
|
MessageHandler messageHandler,
|
|
@@ -1001,7 +2756,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1001
2756
|
{
|
|
1002
2757
|
return InternalRegisterUntargeted<T>(
|
|
1003
2758
|
messageHandler,
|
|
1004
|
-
|
|
2759
|
+
_scalarSinks[BusSinkIndex.UntargetedPostProcessDefault],
|
|
1005
2760
|
RegistrationMethod.UntargetedPostProcessor,
|
|
1006
2761
|
priority
|
|
1007
2762
|
);
|
|
@@ -1018,7 +2773,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1018
2773
|
return InternalRegisterWithContext<T>(
|
|
1019
2774
|
target,
|
|
1020
2775
|
messageHandler,
|
|
1021
|
-
|
|
2776
|
+
_contextSinks[BusContextIndex.TargetedPostProcessDefault],
|
|
1022
2777
|
RegistrationMethod.TargetedPostProcessor,
|
|
1023
2778
|
priority
|
|
1024
2779
|
);
|
|
@@ -1033,7 +2788,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1033
2788
|
{
|
|
1034
2789
|
return InternalRegisterUntargeted<T>(
|
|
1035
2790
|
messageHandler,
|
|
1036
|
-
|
|
2791
|
+
_scalarSinks[BusSinkIndex.TargetedPostProcessWithoutContext],
|
|
1037
2792
|
RegistrationMethod.TargetedWithoutTargetingPostProcessor,
|
|
1038
2793
|
priority
|
|
1039
2794
|
);
|
|
@@ -1050,7 +2805,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1050
2805
|
return InternalRegisterWithContext<T>(
|
|
1051
2806
|
source,
|
|
1052
2807
|
messageHandler,
|
|
1053
|
-
|
|
2808
|
+
_contextSinks[BusContextIndex.BroadcastPostProcessDefault],
|
|
1054
2809
|
RegistrationMethod.BroadcastPostProcessor,
|
|
1055
2810
|
priority
|
|
1056
2811
|
);
|
|
@@ -1065,7 +2820,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1065
2820
|
{
|
|
1066
2821
|
return InternalRegisterUntargeted<T>(
|
|
1067
2822
|
messageHandler,
|
|
1068
|
-
|
|
2823
|
+
_scalarSinks[BusSinkIndex.BroadcastPostProcessWithoutContext],
|
|
1069
2824
|
RegistrationMethod.BroadcastWithoutSourcePostProcessor,
|
|
1070
2825
|
priority
|
|
1071
2826
|
);
|
|
@@ -1107,10 +2862,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1107
2862
|
public void UntargetedBroadcast<TMessage>(ref TMessage typedMessage)
|
|
1108
2863
|
where TMessage : IUntargetedMessage
|
|
1109
2864
|
{
|
|
2865
|
+
TrySweepIdle();
|
|
2866
|
+
using DispatchLease dispatchLease = EnterDispatch();
|
|
1110
2867
|
unchecked
|
|
1111
2868
|
{
|
|
1112
2869
|
_emissionId++;
|
|
1113
2870
|
}
|
|
2871
|
+
long touchTick = AdvanceTick();
|
|
1114
2872
|
if (_diagnosticsMode)
|
|
1115
2873
|
{
|
|
1116
2874
|
_emissionBuffer.Add(new MessageEmissionData(typedMessage));
|
|
@@ -1120,16 +2878,18 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1120
2878
|
// handlers/post-processors are not observed until the next emission.
|
|
1121
2879
|
DispatchSnapshot untargetedPostSnapshot = DispatchSnapshot.Empty;
|
|
1122
2880
|
if (
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
2881
|
+
_scalarSinks[BusSinkIndex.UntargetedPostProcessDefault]
|
|
2882
|
+
.TryGetValue<TMessage>(
|
|
2883
|
+
out HandlerCache<int, HandlerCache> untargetedPostHandlers
|
|
2884
|
+
)
|
|
1126
2885
|
&& untargetedPostHandlers.handlers.Count > 0
|
|
1127
2886
|
)
|
|
1128
2887
|
{
|
|
2888
|
+
Touch(untargetedPostHandlers, touchTick);
|
|
1129
2889
|
untargetedPostSnapshot = AcquireDispatchSnapshot<TMessage>(
|
|
1130
2890
|
this,
|
|
1131
2891
|
untargetedPostHandlers,
|
|
1132
|
-
|
|
2892
|
+
UntargetedPostSlot,
|
|
1133
2893
|
_emissionId
|
|
1134
2894
|
);
|
|
1135
2895
|
PrefreezeUntargetedPostSnapshot<TMessage>(untargetedPostSnapshot);
|
|
@@ -1140,7 +2900,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1140
2900
|
return;
|
|
1141
2901
|
}
|
|
1142
2902
|
|
|
1143
|
-
if (0 <
|
|
2903
|
+
if (0 < _globalSlots.sharedHandlers.Count)
|
|
1144
2904
|
{
|
|
1145
2905
|
IUntargetedMessage untargetedMessage = typedMessage;
|
|
1146
2906
|
BroadcastGlobalUntargeted(ref untargetedMessage);
|
|
@@ -1149,17 +2909,17 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1149
2909
|
bool foundAnyHandlers = InternalUntargetedBroadcast(ref typedMessage);
|
|
1150
2910
|
|
|
1151
2911
|
if (
|
|
1152
|
-
|
|
1153
|
-
out HandlerCache<int, HandlerCache> sortedHandlers
|
|
1154
|
-
)
|
|
2912
|
+
_scalarSinks[BusSinkIndex.UntargetedPostProcessDefault]
|
|
2913
|
+
.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> sortedHandlers)
|
|
1155
2914
|
&& 0 < sortedHandlers.handlers.Count
|
|
1156
2915
|
)
|
|
1157
2916
|
{
|
|
2917
|
+
Touch(sortedHandlers, touchTick);
|
|
1158
2918
|
DispatchSnapshot snapshot = untargetedPostSnapshot.IsEmpty
|
|
1159
2919
|
? AcquireDispatchSnapshot<TMessage>(
|
|
1160
2920
|
this,
|
|
1161
2921
|
sortedHandlers,
|
|
1162
|
-
|
|
2922
|
+
UntargetedPostSlot,
|
|
1163
2923
|
_emissionId
|
|
1164
2924
|
)
|
|
1165
2925
|
: untargetedPostSnapshot;
|
|
@@ -1267,10 +3027,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1267
3027
|
public void TargetedBroadcast<TMessage>(ref InstanceId target, ref TMessage typedMessage)
|
|
1268
3028
|
where TMessage : ITargetedMessage
|
|
1269
3029
|
{
|
|
3030
|
+
TrySweepIdle();
|
|
3031
|
+
using DispatchLease dispatchLease = EnterDispatch();
|
|
1270
3032
|
unchecked
|
|
1271
3033
|
{
|
|
1272
3034
|
_emissionId++;
|
|
1273
3035
|
}
|
|
3036
|
+
long touchTick = AdvanceTick();
|
|
1274
3037
|
if (_diagnosticsMode)
|
|
1275
3038
|
{
|
|
1276
3039
|
_emissionBuffer.Add(new MessageEmissionData(typedMessage, target));
|
|
@@ -1280,9 +3043,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1280
3043
|
DispatchSnapshot targetedPostSnapshot = DispatchSnapshot.Empty;
|
|
1281
3044
|
DispatchSnapshot targetedWithoutTargetingPostSnapshot = DispatchSnapshot.Empty;
|
|
1282
3045
|
if (
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
3046
|
+
_contextSinks[BusContextIndex.TargetedPostProcessDefault]
|
|
3047
|
+
.TryGetValue<TMessage>(
|
|
3048
|
+
out Dictionary<
|
|
3049
|
+
InstanceId,
|
|
3050
|
+
HandlerCache<int, HandlerCache>
|
|
3051
|
+
> targetedPostHandlers
|
|
3052
|
+
)
|
|
1286
3053
|
&& targetedPostHandlers.TryGetValue(
|
|
1287
3054
|
target,
|
|
1288
3055
|
out HandlerCache<int, HandlerCache> targetedPostByPriority
|
|
@@ -1290,25 +3057,28 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1290
3057
|
&& targetedPostByPriority.handlers.Count > 0
|
|
1291
3058
|
)
|
|
1292
3059
|
{
|
|
3060
|
+
Touch(targetedPostByPriority, touchTick);
|
|
1293
3061
|
targetedPostSnapshot = AcquireDispatchSnapshot<TMessage>(
|
|
1294
3062
|
this,
|
|
1295
3063
|
targetedPostByPriority,
|
|
1296
|
-
|
|
3064
|
+
TargetedPostSlot,
|
|
1297
3065
|
_emissionId
|
|
1298
3066
|
);
|
|
1299
3067
|
PrefreezeTargetedPostSnapshot<TMessage>(ref target, targetedPostSnapshot);
|
|
1300
3068
|
}
|
|
1301
3069
|
if (
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
3070
|
+
_scalarSinks[BusSinkIndex.TargetedPostProcessWithoutContext]
|
|
3071
|
+
.TryGetValue<TMessage>(
|
|
3072
|
+
out HandlerCache<int, HandlerCache> targetedWithoutTargetingHandlers
|
|
3073
|
+
)
|
|
1305
3074
|
&& targetedWithoutTargetingHandlers.handlers.Count > 0
|
|
1306
3075
|
)
|
|
1307
3076
|
{
|
|
3077
|
+
Touch(targetedWithoutTargetingHandlers, touchTick);
|
|
1308
3078
|
targetedWithoutTargetingPostSnapshot = AcquireDispatchSnapshot<TMessage>(
|
|
1309
3079
|
this,
|
|
1310
3080
|
targetedWithoutTargetingHandlers,
|
|
1311
|
-
|
|
3081
|
+
TargetedWithoutContextPostSlot,
|
|
1312
3082
|
_emissionId
|
|
1313
3083
|
);
|
|
1314
3084
|
PrefreezeTargetedWithoutTargetingPostSnapshot<TMessage>(
|
|
@@ -1321,7 +3091,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1321
3091
|
return;
|
|
1322
3092
|
}
|
|
1323
3093
|
|
|
1324
|
-
if (0 <
|
|
3094
|
+
if (0 < _globalSlots.sharedHandlers.Count)
|
|
1325
3095
|
{
|
|
1326
3096
|
ITargetedMessage targetedMessage = typedMessage;
|
|
1327
3097
|
BroadcastGlobalTargeted(ref target, ref targetedMessage);
|
|
@@ -1538,9 +3308,10 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1538
3308
|
}
|
|
1539
3309
|
|
|
1540
3310
|
if (
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
3311
|
+
_contextSinks[BusContextIndex.TargetedHandleDefault]
|
|
3312
|
+
.TryGetValue<TMessage>(
|
|
3313
|
+
out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> targetedHandlers
|
|
3314
|
+
)
|
|
1544
3315
|
&& targetedHandlers.TryGetValue(
|
|
1545
3316
|
target,
|
|
1546
3317
|
out HandlerCache<int, HandlerCache> sortedHandlers
|
|
@@ -1548,12 +3319,17 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1548
3319
|
&& sortedHandlers.handlers.Count > 0
|
|
1549
3320
|
)
|
|
1550
3321
|
{
|
|
3322
|
+
Touch(sortedHandlers, touchTick);
|
|
1551
3323
|
DispatchSnapshot snapshot = AcquireDispatchSnapshot<TMessage>(
|
|
1552
3324
|
this,
|
|
1553
3325
|
sortedHandlers,
|
|
1554
|
-
|
|
3326
|
+
TargetedHandleSlot,
|
|
1555
3327
|
_emissionId
|
|
1556
3328
|
);
|
|
3329
|
+
// Pre-freeze the typed-handler caches across every priority bucket so
|
|
3330
|
+
// deregistrations performed by an earlier priority's handler cannot
|
|
3331
|
+
// empty a later priority's stack mid-emission.
|
|
3332
|
+
PrefreezeTargetedSnapshot<TMessage>(ref target, snapshot);
|
|
1557
3333
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
1558
3334
|
int bucketCount = snapshot.bucketCount;
|
|
1559
3335
|
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
@@ -1625,7 +3401,8 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1625
3401
|
}
|
|
1626
3402
|
|
|
1627
3403
|
if (
|
|
1628
|
-
|
|
3404
|
+
_contextSinks[BusContextIndex.TargetedPostProcessDefault]
|
|
3405
|
+
.TryGetValue<TMessage>(out targetedHandlers)
|
|
1629
3406
|
&& targetedHandlers.TryGetValue(target, out sortedHandlers)
|
|
1630
3407
|
&& sortedHandlers.handlers.Count > 0
|
|
1631
3408
|
)
|
|
@@ -1634,7 +3411,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1634
3411
|
? AcquireDispatchSnapshot<TMessage>(
|
|
1635
3412
|
this,
|
|
1636
3413
|
sortedHandlers,
|
|
1637
|
-
|
|
3414
|
+
TargetedPostSlot,
|
|
1638
3415
|
_emissionId
|
|
1639
3416
|
)
|
|
1640
3417
|
: targetedPostSnapshot;
|
|
@@ -1779,9 +3556,8 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1779
3556
|
}
|
|
1780
3557
|
|
|
1781
3558
|
if (
|
|
1782
|
-
|
|
1783
|
-
out HandlerCache<int, HandlerCache> postTwt
|
|
1784
|
-
)
|
|
3559
|
+
_scalarSinks[BusSinkIndex.TargetedPostProcessWithoutContext]
|
|
3560
|
+
.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> postTwt)
|
|
1785
3561
|
&& postTwt.handlers.Count > 0
|
|
1786
3562
|
)
|
|
1787
3563
|
{
|
|
@@ -1789,7 +3565,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1789
3565
|
? AcquireDispatchSnapshot<TMessage>(
|
|
1790
3566
|
this,
|
|
1791
3567
|
postTwt,
|
|
1792
|
-
|
|
3568
|
+
TargetedWithoutContextPostSlot,
|
|
1793
3569
|
_emissionId
|
|
1794
3570
|
)
|
|
1795
3571
|
: targetedWithoutTargetingPostSnapshot;
|
|
@@ -1952,13 +3728,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1952
3728
|
)
|
|
1953
3729
|
where TMessage : ITargetedMessage
|
|
1954
3730
|
{
|
|
1955
|
-
|
|
3731
|
+
// Snapshot semantics: see comment on RunBroadcast.
|
|
3732
|
+
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
3733
|
+
int messageHandlersCount = messageHandlers.Count;
|
|
3734
|
+
if (messageHandlersCount == 0)
|
|
1956
3735
|
{
|
|
1957
3736
|
return;
|
|
1958
3737
|
}
|
|
1959
|
-
|
|
1960
|
-
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
1961
|
-
int messageHandlersCount = messageHandlers.Count;
|
|
1962
3738
|
switch (messageHandlersCount)
|
|
1963
3739
|
{
|
|
1964
3740
|
case 1:
|
|
@@ -2108,13 +3884,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2108
3884
|
)
|
|
2109
3885
|
where TMessage : ITargetedMessage
|
|
2110
3886
|
{
|
|
2111
|
-
|
|
3887
|
+
// Snapshot semantics: see comment on RunBroadcast.
|
|
3888
|
+
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
3889
|
+
int messageHandlersCount = messageHandlers.Count;
|
|
3890
|
+
if (messageHandlersCount == 0)
|
|
2112
3891
|
{
|
|
2113
3892
|
return;
|
|
2114
3893
|
}
|
|
2115
|
-
|
|
2116
|
-
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
2117
|
-
int messageHandlersCount = messageHandlers.Count;
|
|
2118
3894
|
switch (messageHandlersCount)
|
|
2119
3895
|
{
|
|
2120
3896
|
case 1:
|
|
@@ -2184,13 +3960,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2184
3960
|
)
|
|
2185
3961
|
where TMessage : ITargetedMessage
|
|
2186
3962
|
{
|
|
2187
|
-
|
|
3963
|
+
// Snapshot semantics: see comment on RunBroadcast.
|
|
3964
|
+
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
3965
|
+
int messageHandlersCount = messageHandlers.Count;
|
|
3966
|
+
if (messageHandlersCount == 0)
|
|
2188
3967
|
{
|
|
2189
3968
|
return;
|
|
2190
3969
|
}
|
|
2191
|
-
|
|
2192
|
-
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
2193
|
-
int messageHandlersCount = messageHandlers.Count;
|
|
2194
3970
|
switch (messageHandlersCount)
|
|
2195
3971
|
{
|
|
2196
3972
|
case 1:
|
|
@@ -2274,10 +4050,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2274
4050
|
public void SourcedBroadcast<TMessage>(ref InstanceId source, ref TMessage typedMessage)
|
|
2275
4051
|
where TMessage : IBroadcastMessage
|
|
2276
4052
|
{
|
|
4053
|
+
TrySweepIdle();
|
|
4054
|
+
using DispatchLease dispatchLease = EnterDispatch();
|
|
2277
4055
|
unchecked
|
|
2278
4056
|
{
|
|
2279
4057
|
_emissionId++;
|
|
2280
4058
|
}
|
|
4059
|
+
long touchTick = AdvanceTick();
|
|
2281
4060
|
if (_diagnosticsMode)
|
|
2282
4061
|
{
|
|
2283
4062
|
_emissionBuffer.Add(new MessageEmissionData(typedMessage, source));
|
|
@@ -2287,12 +4066,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2287
4066
|
DispatchSnapshot broadcastPostSnapshot = DispatchSnapshot.Empty;
|
|
2288
4067
|
DispatchSnapshot broadcastWithoutSourcePostSnapshot = DispatchSnapshot.Empty;
|
|
2289
4068
|
if (
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
4069
|
+
_contextSinks[BusContextIndex.BroadcastPostProcessDefault]
|
|
4070
|
+
.TryGetValue<TMessage>(
|
|
4071
|
+
out Dictionary<
|
|
4072
|
+
InstanceId,
|
|
4073
|
+
HandlerCache<int, HandlerCache>
|
|
4074
|
+
> broadcastPostHandlers
|
|
4075
|
+
)
|
|
2296
4076
|
&& broadcastPostHandlers.TryGetValue(
|
|
2297
4077
|
source,
|
|
2298
4078
|
out HandlerCache<int, HandlerCache> broadcastPostByPriority
|
|
@@ -2300,25 +4080,28 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2300
4080
|
&& broadcastPostByPriority.handlers.Count > 0
|
|
2301
4081
|
)
|
|
2302
4082
|
{
|
|
4083
|
+
Touch(broadcastPostByPriority, touchTick);
|
|
2303
4084
|
broadcastPostSnapshot = AcquireDispatchSnapshot<TMessage>(
|
|
2304
4085
|
this,
|
|
2305
4086
|
broadcastPostByPriority,
|
|
2306
|
-
|
|
4087
|
+
BroadcastPostSlot,
|
|
2307
4088
|
_emissionId
|
|
2308
4089
|
);
|
|
2309
4090
|
PrefreezeBroadcastPostSnapshot<TMessage>(ref source, broadcastPostSnapshot);
|
|
2310
4091
|
}
|
|
2311
4092
|
if (
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
4093
|
+
_scalarSinks[BusSinkIndex.BroadcastPostProcessWithoutContext]
|
|
4094
|
+
.TryGetValue<TMessage>(
|
|
4095
|
+
out HandlerCache<int, HandlerCache> broadcastWithoutSourceHandlers
|
|
4096
|
+
)
|
|
2315
4097
|
&& broadcastWithoutSourceHandlers.handlers.Count > 0
|
|
2316
4098
|
)
|
|
2317
4099
|
{
|
|
4100
|
+
Touch(broadcastWithoutSourceHandlers, touchTick);
|
|
2318
4101
|
broadcastWithoutSourcePostSnapshot = AcquireDispatchSnapshot<TMessage>(
|
|
2319
4102
|
this,
|
|
2320
4103
|
broadcastWithoutSourceHandlers,
|
|
2321
|
-
|
|
4104
|
+
BroadcastWithoutContextPostSlot,
|
|
2322
4105
|
_emissionId
|
|
2323
4106
|
);
|
|
2324
4107
|
PrefreezeBroadcastWithoutSourcePostSnapshot<TMessage>(
|
|
@@ -2331,46 +4114,65 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2331
4114
|
return;
|
|
2332
4115
|
}
|
|
2333
4116
|
|
|
2334
|
-
if (0 <
|
|
4117
|
+
if (0 < _globalSlots.sharedHandlers.Count)
|
|
2335
4118
|
{
|
|
2336
4119
|
IBroadcastMessage broadcastMessage = typedMessage;
|
|
2337
4120
|
BroadcastGlobalSourcedBroadcast(ref source, ref broadcastMessage);
|
|
2338
4121
|
}
|
|
2339
4122
|
|
|
2340
|
-
// Pre-freeze broadcast-without-source handler stacks for this emission
|
|
4123
|
+
// Pre-freeze broadcast-without-source handler stacks for this emission.
|
|
4124
|
+
// Skip the prefreeze pass entirely when there is exactly one priority
|
|
4125
|
+
// bucket with at most one MessageHandler entry; see the rationale on
|
|
4126
|
+
// the snapshot-level Prefreeze*Snapshot fast-path short-circuit.
|
|
2341
4127
|
if (
|
|
2342
|
-
|
|
4128
|
+
_scalarSinks[BusSinkIndex.BroadcastHandleWithoutContext]
|
|
4129
|
+
.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> bwsHandlers)
|
|
2343
4130
|
&& bwsHandlers.handlers.Count > 0
|
|
2344
4131
|
)
|
|
2345
4132
|
{
|
|
4133
|
+
Touch(bwsHandlers, touchTick);
|
|
2346
4134
|
List<KeyValuePair<int, HandlerCache>> frozen = GetOrAddMessageHandlerStack(
|
|
2347
4135
|
bwsHandlers,
|
|
2348
4136
|
_emissionId
|
|
2349
4137
|
);
|
|
2350
4138
|
int frozenCount = frozen.Count;
|
|
2351
|
-
|
|
4139
|
+
bool needsBwsPrefreeze = frozenCount > 1;
|
|
4140
|
+
List<MessageHandler> singleBucketBwsHandlers = null;
|
|
4141
|
+
if (!needsBwsPrefreeze && frozenCount == 1)
|
|
2352
4142
|
{
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
entry.Value,
|
|
4143
|
+
singleBucketBwsHandlers = GetOrAddMessageHandlerStack(
|
|
4144
|
+
frozen[0].Value,
|
|
2356
4145
|
_emissionId
|
|
2357
4146
|
);
|
|
2358
|
-
|
|
4147
|
+
needsBwsPrefreeze = singleBucketBwsHandlers.Count > 1;
|
|
4148
|
+
}
|
|
4149
|
+
if (needsBwsPrefreeze)
|
|
4150
|
+
{
|
|
4151
|
+
for (int i = 0; i < frozenCount; ++i)
|
|
2359
4152
|
{
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
4153
|
+
KeyValuePair<int, HandlerCache> entry = frozen[i];
|
|
4154
|
+
List<MessageHandler> mhList =
|
|
4155
|
+
(i == 0 && singleBucketBwsHandlers != null)
|
|
4156
|
+
? singleBucketBwsHandlers
|
|
4157
|
+
: GetOrAddMessageHandlerStack(entry.Value, _emissionId);
|
|
4158
|
+
for (int h = 0; h < mhList.Count; ++h)
|
|
4159
|
+
{
|
|
4160
|
+
mhList[h]
|
|
4161
|
+
.PrefreezeBroadcastWithoutSourceHandlersForEmission<TMessage>(
|
|
4162
|
+
entry.Key,
|
|
4163
|
+
_emissionId,
|
|
4164
|
+
this
|
|
4165
|
+
);
|
|
4166
|
+
}
|
|
2366
4167
|
}
|
|
2367
4168
|
}
|
|
2368
4169
|
}
|
|
2369
4170
|
|
|
2370
4171
|
bool foundAnyHandlers = false;
|
|
2371
|
-
_ =
|
|
2372
|
-
|
|
2373
|
-
|
|
4172
|
+
_ = _contextSinks[BusContextIndex.BroadcastHandleDefault]
|
|
4173
|
+
.TryGetValue<TMessage>(
|
|
4174
|
+
out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> broadcastHandlers
|
|
4175
|
+
);
|
|
2374
4176
|
if (
|
|
2375
4177
|
broadcastHandlers != null
|
|
2376
4178
|
&& broadcastHandlers.TryGetValue(
|
|
@@ -2380,12 +4182,63 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2380
4182
|
&& 0 < sortedHandlers.handlers.Count
|
|
2381
4183
|
)
|
|
2382
4184
|
{
|
|
4185
|
+
Touch(sortedHandlers, touchTick);
|
|
2383
4186
|
foundAnyHandlers = true;
|
|
2384
4187
|
List<KeyValuePair<int, HandlerCache>> handlerList = GetOrAddMessageHandlerStack(
|
|
2385
4188
|
sortedHandlers,
|
|
2386
4189
|
_emissionId
|
|
2387
4190
|
);
|
|
2388
4191
|
int handlerListCount = handlerList.Count;
|
|
4192
|
+
// Pre-freeze the typed-handler caches across every priority bucket so
|
|
4193
|
+
// deregistrations performed by an earlier priority's handler cannot
|
|
4194
|
+
// empty a later priority's stack mid-emission. The prefreeze pass is
|
|
4195
|
+
// only required when at least one later-running handler reads from a
|
|
4196
|
+
// cache that an earlier-running handler can mutate. That is the case
|
|
4197
|
+
// when there are multiple priority buckets, OR when the single bucket
|
|
4198
|
+
// holds more than one MessageHandler (each MessageHandler owns its
|
|
4199
|
+
// own typed-handler cache, so a removal in one can blank another).
|
|
4200
|
+
// Single-priority single-MessageHandler dispatch is already protected
|
|
4201
|
+
// by the lazy GetOrAddNewHandlerStack inside the dispatch path;
|
|
4202
|
+
// multiple delegate registrations within the same priority on the
|
|
4203
|
+
// same MessageHandler share a HandlerActionCache that is frozen on
|
|
4204
|
+
// first read by RunFastHandlersWithContext / RunHandlersWithContext.
|
|
4205
|
+
bool needsPrefreeze = handlerListCount > 1;
|
|
4206
|
+
List<MessageHandler> singleBucketFrozenHandlers = null;
|
|
4207
|
+
if (!needsPrefreeze && handlerListCount == 1)
|
|
4208
|
+
{
|
|
4209
|
+
// For the single-bucket case, count entries in the FROZEN
|
|
4210
|
+
// MessageHandler stack (not the live dict, which a concurrent
|
|
4211
|
+
// global/interceptor deregistration could shrink between snapshot
|
|
4212
|
+
// acquisition and this read). Reusing the frozen list also avoids
|
|
4213
|
+
// re-acquiring it inside the prefreeze loop below.
|
|
4214
|
+
singleBucketFrozenHandlers = GetOrAddMessageHandlerStack(
|
|
4215
|
+
handlerList[0].Value,
|
|
4216
|
+
_emissionId
|
|
4217
|
+
);
|
|
4218
|
+
needsPrefreeze = singleBucketFrozenHandlers.Count > 1;
|
|
4219
|
+
}
|
|
4220
|
+
if (needsPrefreeze)
|
|
4221
|
+
{
|
|
4222
|
+
for (int i = 0; i < handlerListCount; ++i)
|
|
4223
|
+
{
|
|
4224
|
+
KeyValuePair<int, HandlerCache> prefreezeEntry = handlerList[i];
|
|
4225
|
+
List<MessageHandler> prefreezeHandlers =
|
|
4226
|
+
(i == 0 && singleBucketFrozenHandlers != null)
|
|
4227
|
+
? singleBucketFrozenHandlers
|
|
4228
|
+
: GetOrAddMessageHandlerStack(prefreezeEntry.Value, _emissionId);
|
|
4229
|
+
int prefreezeHandlerCount = prefreezeHandlers.Count;
|
|
4230
|
+
for (int h = 0; h < prefreezeHandlerCount; ++h)
|
|
4231
|
+
{
|
|
4232
|
+
prefreezeHandlers[h]
|
|
4233
|
+
.PrefreezeBroadcastHandlersForEmission<TMessage>(
|
|
4234
|
+
source,
|
|
4235
|
+
prefreezeEntry.Key,
|
|
4236
|
+
_emissionId,
|
|
4237
|
+
this
|
|
4238
|
+
);
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
2389
4242
|
switch (handlerListCount)
|
|
2390
4243
|
{
|
|
2391
4244
|
case 1:
|
|
@@ -2451,23 +4304,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2451
4304
|
}
|
|
2452
4305
|
}
|
|
2453
4306
|
|
|
2454
|
-
bool bwsFound = InternalBroadcastWithoutSource(ref source, ref typedMessage);
|
|
2455
|
-
|
|
2456
|
-
if (
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
{
|
|
2462
|
-
foundAnyHandlers = true;
|
|
2463
|
-
DispatchSnapshot snapshot = AcquireDispatchSnapshot<TMessage>(
|
|
2464
|
-
this,
|
|
2465
|
-
sortedHandlers,
|
|
2466
|
-
DispatchCategory.BroadcastPost,
|
|
2467
|
-
_emissionId
|
|
2468
|
-
);
|
|
2469
|
-
DispatchBucket[] buckets = snapshot.buckets;
|
|
2470
|
-
int bucketCount = snapshot.bucketCount;
|
|
4307
|
+
bool bwsFound = InternalBroadcastWithoutSource(ref source, ref typedMessage);
|
|
4308
|
+
|
|
4309
|
+
if (!broadcastPostSnapshot.IsEmpty)
|
|
4310
|
+
{
|
|
4311
|
+
foundAnyHandlers = true;
|
|
4312
|
+
DispatchBucket[] buckets = broadcastPostSnapshot.buckets;
|
|
4313
|
+
int bucketCount = broadcastPostSnapshot.bucketCount;
|
|
2471
4314
|
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
2472
4315
|
{
|
|
2473
4316
|
DispatchBucket bucket = buckets[bucketIndex];
|
|
@@ -2606,19 +4449,10 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2606
4449
|
}
|
|
2607
4450
|
}
|
|
2608
4451
|
|
|
2609
|
-
if (
|
|
2610
|
-
_postProcessingBroadcastWithoutSourceSinks.TryGetValue<TMessage>(out sortedHandlers)
|
|
2611
|
-
&& 0 < sortedHandlers.handlers.Count
|
|
2612
|
-
)
|
|
4452
|
+
if (!broadcastWithoutSourcePostSnapshot.IsEmpty)
|
|
2613
4453
|
{
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
sortedHandlers,
|
|
2617
|
-
DispatchCategory.BroadcastWithoutSourcePost,
|
|
2618
|
-
_emissionId
|
|
2619
|
-
);
|
|
2620
|
-
DispatchBucket[] buckets = snapshot.buckets;
|
|
2621
|
-
int bucketCount = snapshot.bucketCount;
|
|
4454
|
+
DispatchBucket[] buckets = broadcastWithoutSourcePostSnapshot.buckets;
|
|
4455
|
+
int bucketCount = broadcastWithoutSourcePostSnapshot.bucketCount;
|
|
2622
4456
|
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
2623
4457
|
{
|
|
2624
4458
|
DispatchBucket bucket = buckets[bucketIndex];
|
|
@@ -2776,12 +4610,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2776
4610
|
)
|
|
2777
4611
|
where TMessage : IBroadcastMessage
|
|
2778
4612
|
{
|
|
2779
|
-
|
|
4613
|
+
// Snapshot semantics: see comment on RunBroadcast.
|
|
4614
|
+
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
4615
|
+
int messageHandlersCount = messageHandlers.Count;
|
|
4616
|
+
if (messageHandlersCount == 0)
|
|
2780
4617
|
{
|
|
2781
4618
|
return;
|
|
2782
4619
|
}
|
|
2783
|
-
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
2784
|
-
int messageHandlersCount = messageHandlers.Count;
|
|
2785
4620
|
switch (messageHandlersCount)
|
|
2786
4621
|
{
|
|
2787
4622
|
case 1:
|
|
@@ -2931,13 +4766,19 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2931
4766
|
)
|
|
2932
4767
|
where TMessage : IBroadcastMessage
|
|
2933
4768
|
{
|
|
2934
|
-
|
|
4769
|
+
// Snapshot semantics: dispatch must respect the per-emission frozen
|
|
4770
|
+
// MessageHandler list, even if a handler running earlier in the same
|
|
4771
|
+
// emission has emptied the live cache.handlers dictionary by removing
|
|
4772
|
+
// its own (or a sibling priority's) registration. Reading the live
|
|
4773
|
+
// dict here would skip handlers that the snapshot still includes.
|
|
4774
|
+
// GetOrAddMessageHandlerStack returns the snapshot list; bail only
|
|
4775
|
+
// when that snapshot is empty.
|
|
4776
|
+
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
4777
|
+
int messageHandlersCount = messageHandlers.Count;
|
|
4778
|
+
if (messageHandlersCount == 0)
|
|
2935
4779
|
{
|
|
2936
4780
|
return;
|
|
2937
4781
|
}
|
|
2938
|
-
|
|
2939
|
-
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
2940
|
-
int messageHandlersCount = messageHandlers.Count;
|
|
2941
4782
|
switch (messageHandlersCount)
|
|
2942
4783
|
{
|
|
2943
4784
|
case 1:
|
|
@@ -3003,8 +4844,8 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3003
4844
|
{
|
|
3004
4845
|
DispatchSnapshot snapshot = AcquireGlobalDispatchSnapshot<IUntargetedMessage>(
|
|
3005
4846
|
this,
|
|
3006
|
-
|
|
3007
|
-
|
|
4847
|
+
_globalSlots,
|
|
4848
|
+
DispatchKind.Untargeted,
|
|
3008
4849
|
_emissionId
|
|
3009
4850
|
);
|
|
3010
4851
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
@@ -3074,8 +4915,8 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3074
4915
|
{
|
|
3075
4916
|
DispatchSnapshot snapshot = AcquireGlobalDispatchSnapshot<ITargetedMessage>(
|
|
3076
4917
|
this,
|
|
3077
|
-
|
|
3078
|
-
|
|
4918
|
+
_globalSlots,
|
|
4919
|
+
DispatchKind.Targeted,
|
|
3079
4920
|
_emissionId
|
|
3080
4921
|
);
|
|
3081
4922
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
@@ -3148,8 +4989,8 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3148
4989
|
{
|
|
3149
4990
|
DispatchSnapshot snapshot = AcquireGlobalDispatchSnapshot<IBroadcastMessage>(
|
|
3150
4991
|
this,
|
|
3151
|
-
|
|
3152
|
-
|
|
4992
|
+
_globalSlots,
|
|
4993
|
+
DispatchKind.Broadcast,
|
|
3153
4994
|
_emissionId
|
|
3154
4995
|
);
|
|
3155
4996
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
@@ -3426,10 +5267,11 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3426
5267
|
}
|
|
3427
5268
|
|
|
3428
5269
|
private bool InternalUntargetedBroadcast<TMessage>(ref TMessage message)
|
|
3429
|
-
where TMessage :
|
|
5270
|
+
where TMessage : IUntargetedMessage
|
|
3430
5271
|
{
|
|
3431
5272
|
if (
|
|
3432
|
-
!
|
|
5273
|
+
!_scalarSinks[BusSinkIndex.UntargetedHandleDefault]
|
|
5274
|
+
.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> sortedHandlers)
|
|
3433
5275
|
|| sortedHandlers.handlers.Count == 0
|
|
3434
5276
|
)
|
|
3435
5277
|
{
|
|
@@ -3439,7 +5281,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3439
5281
|
DispatchSnapshot snapshot = AcquireDispatchSnapshot<TMessage>(
|
|
3440
5282
|
this,
|
|
3441
5283
|
sortedHandlers,
|
|
3442
|
-
|
|
5284
|
+
UntargetedHandleSlot,
|
|
3443
5285
|
_emissionId
|
|
3444
5286
|
);
|
|
3445
5287
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
@@ -3450,6 +5292,11 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3450
5292
|
return false;
|
|
3451
5293
|
}
|
|
3452
5294
|
|
|
5295
|
+
// Pre-freeze the typed-handler caches across every priority bucket so
|
|
5296
|
+
// deregistrations performed by an earlier priority's handler cannot
|
|
5297
|
+
// empty a later priority's stack mid-emission.
|
|
5298
|
+
PrefreezeUntargetedSnapshot<TMessage>(snapshot);
|
|
5299
|
+
|
|
3453
5300
|
bool invoked = false;
|
|
3454
5301
|
|
|
3455
5302
|
for (int i = 0; i < bucketCount; ++i)
|
|
@@ -3559,6 +5406,18 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3559
5406
|
return;
|
|
3560
5407
|
}
|
|
3561
5408
|
|
|
5409
|
+
// No fast-path short-circuit for post-processor prefreeze.
|
|
5410
|
+
//
|
|
5411
|
+
// The single-bucket/single-entry fast-path used by handler prefreeze
|
|
5412
|
+
// (see PrefreezeUntargetedSnapshot) is unsafe for post-processors:
|
|
5413
|
+
// post-processors run AFTER regular handlers, and a regular handler
|
|
5414
|
+
// is allowed to register a NEW post-processor (or a new delegate on
|
|
5415
|
+
// an existing post-processor cache) during its own execution. Without
|
|
5416
|
+
// an unconditional prefreeze, the post-processor cache's first read
|
|
5417
|
+
// happens lazily inside the post-processor dispatch; by which time
|
|
5418
|
+
// the version has been bumped and the cache will be rebuilt with the
|
|
5419
|
+
// newly-registered entry visible. Always prefreezing pins the
|
|
5420
|
+
// emission-start snapshot before any handler can mutate it.
|
|
3562
5421
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
3563
5422
|
int bucketCount = snapshot.bucketCount;
|
|
3564
5423
|
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
@@ -3584,6 +5443,55 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3584
5443
|
}
|
|
3585
5444
|
}
|
|
3586
5445
|
|
|
5446
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
5447
|
+
private void PrefreezeUntargetedSnapshot<TMessage>(DispatchSnapshot snapshot)
|
|
5448
|
+
where TMessage : IUntargetedMessage
|
|
5449
|
+
{
|
|
5450
|
+
if (snapshot.IsEmpty)
|
|
5451
|
+
{
|
|
5452
|
+
return;
|
|
5453
|
+
}
|
|
5454
|
+
|
|
5455
|
+
// Prefreeze fast-path short-circuit: if there is exactly one priority
|
|
5456
|
+
// bucket with at most one MessageHandler entry, no later handler can
|
|
5457
|
+
// observe a removal performed by an earlier one, so the inline lazy
|
|
5458
|
+
// freeze inside the dispatch path is sufficient. Note: a single
|
|
5459
|
+
// MessageHandler may still register multiple delegates at the same
|
|
5460
|
+
// priority; those share a HandlerActionCache that is frozen on first
|
|
5461
|
+
// read by the per-priority RunFastHandlers/RunHandlers, so the lazy
|
|
5462
|
+
// freeze covers same-priority same-MessageHandler removals correctly.
|
|
5463
|
+
// See the longer rationale on the broadcast inline prefreeze block
|
|
5464
|
+
// in SourcedBroadcast.
|
|
5465
|
+
if (snapshot.bucketCount == 1 && snapshot.buckets[0].entryCount <= 1)
|
|
5466
|
+
{
|
|
5467
|
+
return;
|
|
5468
|
+
}
|
|
5469
|
+
|
|
5470
|
+
DispatchBucket[] buckets = snapshot.buckets;
|
|
5471
|
+
int bucketCount = snapshot.bucketCount;
|
|
5472
|
+
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
5473
|
+
{
|
|
5474
|
+
DispatchBucket bucket = buckets[bucketIndex];
|
|
5475
|
+
DispatchEntry[] entries = bucket.entries;
|
|
5476
|
+
int entryCount = bucket.entryCount;
|
|
5477
|
+
if (entryCount == 0)
|
|
5478
|
+
{
|
|
5479
|
+
continue;
|
|
5480
|
+
}
|
|
5481
|
+
|
|
5482
|
+
int priority = bucket.priority;
|
|
5483
|
+
for (int entryIndex = 0; entryIndex < entryCount; ++entryIndex)
|
|
5484
|
+
{
|
|
5485
|
+
entries[entryIndex]
|
|
5486
|
+
.handler.PrefreezeUntargetedHandlersForEmission<TMessage>(
|
|
5487
|
+
priority,
|
|
5488
|
+
_emissionId,
|
|
5489
|
+
this
|
|
5490
|
+
);
|
|
5491
|
+
}
|
|
5492
|
+
}
|
|
5493
|
+
}
|
|
5494
|
+
|
|
3587
5495
|
private bool InternalTargetedWithoutTargetingBroadcast<TMessage>(
|
|
3588
5496
|
ref InstanceId target,
|
|
3589
5497
|
ref TMessage message
|
|
@@ -3591,7 +5499,8 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3591
5499
|
where TMessage : ITargetedMessage
|
|
3592
5500
|
{
|
|
3593
5501
|
if (
|
|
3594
|
-
!
|
|
5502
|
+
!_scalarSinks[BusSinkIndex.TargetedHandleWithoutContext]
|
|
5503
|
+
.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> sortedHandlers)
|
|
3595
5504
|
|| sortedHandlers.handlers.Count == 0
|
|
3596
5505
|
)
|
|
3597
5506
|
{
|
|
@@ -3601,13 +5510,48 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3601
5510
|
DispatchSnapshot snapshot = AcquireDispatchSnapshot<TMessage>(
|
|
3602
5511
|
this,
|
|
3603
5512
|
sortedHandlers,
|
|
3604
|
-
|
|
5513
|
+
TargetedWithoutContextHandleSlot,
|
|
3605
5514
|
_emissionId
|
|
3606
5515
|
);
|
|
3607
5516
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
3608
5517
|
int bucketCount = snapshot.bucketCount;
|
|
3609
5518
|
bool invoked = false;
|
|
3610
5519
|
|
|
5520
|
+
// Hoist per-MessageHandler prefreeze across ALL priority buckets
|
|
5521
|
+
// when there is more than one bucket. A handler running in an
|
|
5522
|
+
// earlier bucket can deregister a delegate that lives in a later
|
|
5523
|
+
// bucket's typed cache; if the later bucket's snapshot is taken
|
|
5524
|
+
// lazily inside its own dispatch (after the deregistration), the
|
|
5525
|
+
// rebuild will observe the mutation and the handler will be
|
|
5526
|
+
// skipped, violating snapshot semantics. The single-bucket case
|
|
5527
|
+
// is unchanged; no later bucket exists to be polluted, and the
|
|
5528
|
+
// inline per-bucket prefreeze below covers it.
|
|
5529
|
+
if (bucketCount > 1)
|
|
5530
|
+
{
|
|
5531
|
+
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
5532
|
+
{
|
|
5533
|
+
DispatchBucket prefreezeBucket = buckets[bucketIndex];
|
|
5534
|
+
DispatchEntry[] prefreezeEntries = prefreezeBucket.entries;
|
|
5535
|
+
int prefreezeEntryCount = prefreezeBucket.entryCount;
|
|
5536
|
+
if (prefreezeEntryCount == 0)
|
|
5537
|
+
{
|
|
5538
|
+
continue;
|
|
5539
|
+
}
|
|
5540
|
+
|
|
5541
|
+
if (
|
|
5542
|
+
prefreezeEntries[0].prefreeze.kind
|
|
5543
|
+
== PrefreezeKindTargetedWithoutTargetingHandlers
|
|
5544
|
+
)
|
|
5545
|
+
{
|
|
5546
|
+
PrefreezeTargetedWithoutTargetingEntries<TMessage>(
|
|
5547
|
+
prefreezeEntries,
|
|
5548
|
+
prefreezeEntryCount,
|
|
5549
|
+
prefreezeBucket.priority
|
|
5550
|
+
);
|
|
5551
|
+
}
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
|
|
3611
5555
|
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
3612
5556
|
{
|
|
3613
5557
|
DispatchBucket bucket = buckets[bucketIndex];
|
|
@@ -3620,7 +5564,14 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3620
5564
|
|
|
3621
5565
|
invoked = true;
|
|
3622
5566
|
int priority = bucket.priority;
|
|
3623
|
-
|
|
5567
|
+
// Inline per-bucket prefreeze for the single-bucket case only.
|
|
5568
|
+
// When bucketCount > 1 the hoisted pass above has already
|
|
5569
|
+
// prefrozen every bucket; running it again here would be
|
|
5570
|
+
// harmless but redundant.
|
|
5571
|
+
if (
|
|
5572
|
+
bucketCount == 1
|
|
5573
|
+
&& entries[0].prefreeze.kind == PrefreezeKindTargetedWithoutTargetingHandlers
|
|
5574
|
+
)
|
|
3624
5575
|
{
|
|
3625
5576
|
PrefreezeTargetedWithoutTargetingEntries<TMessage>(
|
|
3626
5577
|
entries,
|
|
@@ -3764,14 +5715,15 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3764
5715
|
)
|
|
3765
5716
|
where TMessage : ITargetedMessage
|
|
3766
5717
|
{
|
|
3767
|
-
|
|
5718
|
+
// Snapshot semantics: see comment on RunBroadcast.
|
|
5719
|
+
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
5720
|
+
int messageHandlersCount = messageHandlers.Count;
|
|
5721
|
+
if (messageHandlersCount == 0)
|
|
3768
5722
|
{
|
|
3769
5723
|
return;
|
|
3770
5724
|
}
|
|
3771
|
-
|
|
3772
|
-
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
3773
5725
|
// Freeze each handler's typed caches for this emission/priority to ensure snapshot semantics
|
|
3774
|
-
for (int j = 0; j <
|
|
5726
|
+
for (int j = 0; j < messageHandlersCount; ++j)
|
|
3775
5727
|
{
|
|
3776
5728
|
messageHandlers[j]
|
|
3777
5729
|
.PrefreezeTargetedWithoutTargetingHandlersForEmission<TMessage>(
|
|
@@ -3780,7 +5732,6 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3780
5732
|
this
|
|
3781
5733
|
);
|
|
3782
5734
|
}
|
|
3783
|
-
int messageHandlersCount = messageHandlers.Count;
|
|
3784
5735
|
switch (messageHandlersCount)
|
|
3785
5736
|
{
|
|
3786
5737
|
case 1:
|
|
@@ -3849,7 +5800,8 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3849
5800
|
where TMessage : IBroadcastMessage
|
|
3850
5801
|
{
|
|
3851
5802
|
if (
|
|
3852
|
-
!
|
|
5803
|
+
!_scalarSinks[BusSinkIndex.BroadcastHandleWithoutContext]
|
|
5804
|
+
.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> sortedHandlers)
|
|
3853
5805
|
|| sortedHandlers.handlers.Count == 0
|
|
3854
5806
|
)
|
|
3855
5807
|
{
|
|
@@ -3861,6 +5813,36 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3861
5813
|
_emissionId
|
|
3862
5814
|
);
|
|
3863
5815
|
int handlerListCount = handlerList.Count;
|
|
5816
|
+
// Hoist per-MessageHandler prefreeze across ALL priority buckets
|
|
5817
|
+
// when there is more than one bucket. A handler running in an
|
|
5818
|
+
// earlier bucket can deregister a delegate that lives in a later
|
|
5819
|
+
// bucket's typed cache; if the later bucket's snapshot is taken
|
|
5820
|
+
// lazily inside RunBroadcastWithoutSource (after the
|
|
5821
|
+
// deregistration), the rebuild will observe the mutation and
|
|
5822
|
+
// skip the handler, violating snapshot semantics. The
|
|
5823
|
+
// single-bucket case is unchanged; RunBroadcastWithoutSource's
|
|
5824
|
+
// inline prefreeze covers it.
|
|
5825
|
+
if (handlerListCount > 1)
|
|
5826
|
+
{
|
|
5827
|
+
for (int i = 0; i < handlerListCount; ++i)
|
|
5828
|
+
{
|
|
5829
|
+
KeyValuePair<int, HandlerCache> prefreezeEntry = handlerList[i];
|
|
5830
|
+
List<MessageHandler> mhList = GetOrAddMessageHandlerStack(
|
|
5831
|
+
prefreezeEntry.Value,
|
|
5832
|
+
_emissionId
|
|
5833
|
+
);
|
|
5834
|
+
int mhCount = mhList.Count;
|
|
5835
|
+
for (int h = 0; h < mhCount; ++h)
|
|
5836
|
+
{
|
|
5837
|
+
mhList[h]
|
|
5838
|
+
.PrefreezeBroadcastWithoutSourceHandlersForEmission<TMessage>(
|
|
5839
|
+
prefreezeEntry.Key,
|
|
5840
|
+
_emissionId,
|
|
5841
|
+
this
|
|
5842
|
+
);
|
|
5843
|
+
}
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
3864
5846
|
switch (handlerListCount)
|
|
3865
5847
|
{
|
|
3866
5848
|
case 1:
|
|
@@ -3932,13 +5914,19 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3932
5914
|
)
|
|
3933
5915
|
where TMessage : IBroadcastMessage
|
|
3934
5916
|
{
|
|
3935
|
-
|
|
5917
|
+
// Snapshot semantics: dispatch must respect the per-emission frozen
|
|
5918
|
+
// MessageHandler list, even if a handler running earlier in the same
|
|
5919
|
+
// emission has emptied the live cache.handlers dictionary by removing
|
|
5920
|
+
// its own (or a sibling priority's) registration. Reading the live
|
|
5921
|
+
// dict here would skip handlers that the snapshot still includes.
|
|
5922
|
+
// GetOrAddMessageHandlerStack returns the snapshot list; bail only
|
|
5923
|
+
// when that snapshot is empty.
|
|
5924
|
+
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
5925
|
+
int messageHandlersCount = messageHandlers.Count;
|
|
5926
|
+
if (messageHandlersCount == 0)
|
|
3936
5927
|
{
|
|
3937
5928
|
return;
|
|
3938
5929
|
}
|
|
3939
|
-
|
|
3940
|
-
List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(cache, _emissionId);
|
|
3941
|
-
int messageHandlersCount = messageHandlers.Count;
|
|
3942
5930
|
// Ensure each handler's typed no-source caches are frozen for this emission/priority
|
|
3943
5931
|
for (int j = 0; j < messageHandlersCount; ++j)
|
|
3944
5932
|
{
|
|
@@ -4103,10 +6091,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4103
6091
|
throw new ArgumentNullException(nameof(messageHandler));
|
|
4104
6092
|
}
|
|
4105
6093
|
|
|
6094
|
+
long touchTick = AdvanceTick();
|
|
4106
6095
|
InstanceId handlerOwnerId = messageHandler.owner;
|
|
4107
6096
|
HandlerCache<int, HandlerCache> handlers = sinks.GetOrAdd<T>();
|
|
6097
|
+
Touch(handlers, touchTick);
|
|
4108
6098
|
HandlerCache<int, HandlerCache> capturedHandlers = handlers;
|
|
4109
|
-
|
|
6099
|
+
SlotKey slotKey = RegistrationMethodAxes.GetSlotKey(registrationMethod);
|
|
4110
6100
|
|
|
4111
6101
|
if (!handlers.handlers.TryGetValue(priority, out HandlerCache cache))
|
|
4112
6102
|
{
|
|
@@ -4128,7 +6118,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4128
6118
|
int count = handler.GetValueOrDefault(messageHandler, 0);
|
|
4129
6119
|
|
|
4130
6120
|
handler[messageHandler] = count + 1;
|
|
4131
|
-
StageDispatchSnapshot<T>(this, capturedHandlers,
|
|
6121
|
+
StageDispatchSnapshot<T>(this, capturedHandlers, slotKey);
|
|
4132
6122
|
Type type = typeof(T);
|
|
4133
6123
|
_log.Log(
|
|
4134
6124
|
new MessagingRegistration(
|
|
@@ -4139,23 +6129,35 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4139
6129
|
)
|
|
4140
6130
|
);
|
|
4141
6131
|
|
|
6132
|
+
long capturedGeneration = _resetGeneration;
|
|
4142
6133
|
return () =>
|
|
4143
6134
|
{
|
|
6135
|
+
// Generation guard: if ResetState() ran after this closure was
|
|
6136
|
+
// captured (e.g. a deferred Object.Destroy fires after a
|
|
6137
|
+
// domain-reload-style reset), silently no-op rather than
|
|
6138
|
+
// logging a misleading over-deregistration error.
|
|
6139
|
+
if (capturedGeneration != _resetGeneration)
|
|
6140
|
+
{
|
|
6141
|
+
return;
|
|
6142
|
+
}
|
|
6143
|
+
|
|
6144
|
+
long deregisterTouchTick = AdvanceTick();
|
|
4144
6145
|
cache.version++;
|
|
4145
|
-
_log.Log(
|
|
4146
|
-
new MessagingRegistration(
|
|
4147
|
-
handlerOwnerId,
|
|
4148
|
-
type,
|
|
4149
|
-
RegistrationType.Deregister,
|
|
4150
|
-
registrationMethod
|
|
4151
|
-
)
|
|
4152
|
-
);
|
|
4153
6146
|
if (
|
|
4154
6147
|
!sinks.TryGetValue<T>(out handlers)
|
|
6148
|
+
|| !ReferenceEquals(handlers, capturedHandlers)
|
|
4155
6149
|
|| !handlers.handlers.TryGetValue(priority, out cache)
|
|
4156
6150
|
|| !cache.handlers.TryGetValue(messageHandler, out count)
|
|
4157
6151
|
)
|
|
4158
6152
|
{
|
|
6153
|
+
if (
|
|
6154
|
+
capturedHandlers.handlers.Count == 0
|
|
6155
|
+
&& !ReferenceEquals(handlers, capturedHandlers)
|
|
6156
|
+
)
|
|
6157
|
+
{
|
|
6158
|
+
return;
|
|
6159
|
+
}
|
|
6160
|
+
|
|
4159
6161
|
if (MessagingDebug.enabled)
|
|
4160
6162
|
{
|
|
4161
6163
|
MessagingDebug.Log(
|
|
@@ -4169,11 +6171,21 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4169
6171
|
return;
|
|
4170
6172
|
}
|
|
4171
6173
|
|
|
6174
|
+
_log.Log(
|
|
6175
|
+
new MessagingRegistration(
|
|
6176
|
+
handlerOwnerId,
|
|
6177
|
+
type,
|
|
6178
|
+
RegistrationType.Deregister,
|
|
6179
|
+
registrationMethod
|
|
6180
|
+
)
|
|
6181
|
+
);
|
|
6182
|
+
Touch(handlers, deregisterTouchTick);
|
|
4172
6183
|
handlers.version++;
|
|
4173
6184
|
handler = cache.handlers;
|
|
4174
6185
|
if (count <= 1)
|
|
4175
6186
|
{
|
|
4176
6187
|
bool complete = handler.Remove(messageHandler);
|
|
6188
|
+
MarkDirtyHandler(messageHandler);
|
|
4177
6189
|
cache.version++;
|
|
4178
6190
|
// do not mutate cache.cache here; let next read rebuild from handlers
|
|
4179
6191
|
|
|
@@ -4191,7 +6203,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4191
6203
|
|
|
4192
6204
|
if (handlers.handlers.Count == 0)
|
|
4193
6205
|
{
|
|
4194
|
-
|
|
6206
|
+
MarkDirtyType<T>();
|
|
4195
6207
|
}
|
|
4196
6208
|
|
|
4197
6209
|
if (!complete && MessagingDebug.enabled)
|
|
@@ -4208,7 +6220,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4208
6220
|
{
|
|
4209
6221
|
handler[messageHandler] = count - 1;
|
|
4210
6222
|
}
|
|
4211
|
-
StageDispatchSnapshot<T>(this, handlers,
|
|
6223
|
+
StageDispatchSnapshot<T>(this, handlers, slotKey);
|
|
4212
6224
|
};
|
|
4213
6225
|
}
|
|
4214
6226
|
|
|
@@ -4226,9 +6238,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4226
6238
|
throw new ArgumentNullException(nameof(messageHandler));
|
|
4227
6239
|
}
|
|
4228
6240
|
|
|
6241
|
+
long touchTick = AdvanceTick();
|
|
4229
6242
|
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> broadcastHandlers =
|
|
4230
|
-
|
|
4231
|
-
|
|
6243
|
+
GetOrRentContextMap<T>(sinks);
|
|
6244
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> capturedBroadcastHandlers =
|
|
6245
|
+
broadcastHandlers;
|
|
6246
|
+
SlotKey slotKey = RegistrationMethodAxes.GetSlotKey(registrationMethod);
|
|
4232
6247
|
|
|
4233
6248
|
if (
|
|
4234
6249
|
!broadcastHandlers.TryGetValue(
|
|
@@ -4239,7 +6254,10 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4239
6254
|
{
|
|
4240
6255
|
handlers = new HandlerCache<int, HandlerCache>();
|
|
4241
6256
|
broadcastHandlers[context] = handlers;
|
|
6257
|
+
TrackContextMapHighWater(broadcastHandlers);
|
|
4242
6258
|
}
|
|
6259
|
+
Touch(handlers, touchTick);
|
|
6260
|
+
HandlerCache<int, HandlerCache> capturedHandlers = handlers;
|
|
4243
6261
|
|
|
4244
6262
|
if (!handlers.handlers.TryGetValue(priority, out HandlerCache cache))
|
|
4245
6263
|
{
|
|
@@ -4271,26 +6289,34 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4271
6289
|
registrationMethod
|
|
4272
6290
|
)
|
|
4273
6291
|
);
|
|
4274
|
-
StageDispatchSnapshot<T>(this, handlers,
|
|
6292
|
+
StageDispatchSnapshot<T>(this, handlers, slotKey);
|
|
4275
6293
|
|
|
6294
|
+
long capturedGeneration = _resetGeneration;
|
|
4276
6295
|
return () =>
|
|
4277
6296
|
{
|
|
6297
|
+
// Generation guard: see InternalRegisterUntargeted for the
|
|
6298
|
+
// rationale. Skip silently when the closure outlived a Reset.
|
|
6299
|
+
if (capturedGeneration != _resetGeneration)
|
|
6300
|
+
{
|
|
6301
|
+
return;
|
|
6302
|
+
}
|
|
6303
|
+
|
|
6304
|
+
long deregisterTouchTick = AdvanceTick();
|
|
4278
6305
|
cache.version++;
|
|
4279
|
-
_log.Log(
|
|
4280
|
-
new MessagingRegistration(
|
|
4281
|
-
context,
|
|
4282
|
-
type,
|
|
4283
|
-
RegistrationType.Deregister,
|
|
4284
|
-
registrationMethod
|
|
4285
|
-
)
|
|
4286
|
-
);
|
|
4287
6306
|
if (
|
|
4288
6307
|
!sinks.TryGetValue<T>(out broadcastHandlers)
|
|
6308
|
+
|| !ReferenceEquals(broadcastHandlers, capturedBroadcastHandlers)
|
|
4289
6309
|
|| !broadcastHandlers.TryGetValue(context, out handlers)
|
|
6310
|
+
|| !ReferenceEquals(handlers, capturedHandlers)
|
|
4290
6311
|
|| !handlers.handlers.TryGetValue(priority, out cache)
|
|
4291
6312
|
|| !cache.handlers.TryGetValue(messageHandler, out count)
|
|
4292
6313
|
)
|
|
4293
6314
|
{
|
|
6315
|
+
if (IsStaleContextDeregisterAfterSweep<T>(sinks, context, capturedHandlers))
|
|
6316
|
+
{
|
|
6317
|
+
return;
|
|
6318
|
+
}
|
|
6319
|
+
|
|
4294
6320
|
if (MessagingDebug.enabled)
|
|
4295
6321
|
{
|
|
4296
6322
|
MessagingDebug.Log(
|
|
@@ -4304,10 +6330,20 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4304
6330
|
return;
|
|
4305
6331
|
}
|
|
4306
6332
|
|
|
6333
|
+
_log.Log(
|
|
6334
|
+
new MessagingRegistration(
|
|
6335
|
+
context,
|
|
6336
|
+
type,
|
|
6337
|
+
RegistrationType.Deregister,
|
|
6338
|
+
registrationMethod
|
|
6339
|
+
)
|
|
6340
|
+
);
|
|
6341
|
+
Touch(handlers, deregisterTouchTick);
|
|
4307
6342
|
handler = cache.handlers;
|
|
4308
6343
|
if (count <= 1)
|
|
4309
6344
|
{
|
|
4310
6345
|
bool complete = handler.Remove(messageHandler);
|
|
6346
|
+
MarkDirtyHandler(messageHandler);
|
|
4311
6347
|
cache.version++;
|
|
4312
6348
|
// do not mutate cache.cache here; let next read rebuild from handlers
|
|
4313
6349
|
if (handler.Count == 0)
|
|
@@ -4325,12 +6361,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4325
6361
|
|
|
4326
6362
|
if (handlers.handlers.Count == 0)
|
|
4327
6363
|
{
|
|
4328
|
-
|
|
4329
|
-
}
|
|
4330
|
-
|
|
4331
|
-
if (broadcastHandlers.Count == 0)
|
|
4332
|
-
{
|
|
4333
|
-
sinks.Remove<T>();
|
|
6364
|
+
MarkDirtyTarget<T>(context);
|
|
4334
6365
|
}
|
|
4335
6366
|
|
|
4336
6367
|
if (!complete && MessagingDebug.enabled)
|
|
@@ -4347,26 +6378,44 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4347
6378
|
{
|
|
4348
6379
|
handler[messageHandler] = count - 1;
|
|
4349
6380
|
}
|
|
4350
|
-
StageDispatchSnapshot<T>(this, handlers,
|
|
6381
|
+
StageDispatchSnapshot<T>(this, handlers, slotKey);
|
|
4351
6382
|
};
|
|
4352
6383
|
}
|
|
4353
6384
|
|
|
6385
|
+
private static bool IsStaleContextDeregisterAfterSweep<T>(
|
|
6386
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sinks,
|
|
6387
|
+
InstanceId context,
|
|
6388
|
+
HandlerCache<int, HandlerCache> capturedHandlers
|
|
6389
|
+
)
|
|
6390
|
+
where T : IMessage
|
|
6391
|
+
{
|
|
6392
|
+
return capturedHandlers.handlers.Count == 0
|
|
6393
|
+
&& (
|
|
6394
|
+
!sinks.TryGetValue<T>(
|
|
6395
|
+
out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> currentByContext
|
|
6396
|
+
)
|
|
6397
|
+
|| !currentByContext.TryGetValue(
|
|
6398
|
+
context,
|
|
6399
|
+
out HandlerCache<int, HandlerCache> currentHandlers
|
|
6400
|
+
)
|
|
6401
|
+
|| !ReferenceEquals(currentHandlers, capturedHandlers)
|
|
6402
|
+
);
|
|
6403
|
+
}
|
|
6404
|
+
|
|
4354
6405
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
4355
6406
|
private static void StageDispatchSnapshot<TMessage>(
|
|
4356
6407
|
MessageBus messageBus,
|
|
4357
6408
|
HandlerCache<int, HandlerCache> handlers,
|
|
4358
|
-
|
|
6409
|
+
SlotKey slotKey
|
|
4359
6410
|
)
|
|
4360
6411
|
where TMessage : IMessage
|
|
4361
6412
|
{
|
|
4362
|
-
if (handlers == null ||
|
|
6413
|
+
if (handlers == null || slotKey == SlotKey.None)
|
|
4363
6414
|
{
|
|
4364
6415
|
return;
|
|
4365
6416
|
}
|
|
4366
6417
|
|
|
4367
|
-
|
|
4368
|
-
category
|
|
4369
|
-
);
|
|
6418
|
+
DispatchState state = handlers.dispatchState ??= new DispatchState();
|
|
4370
6419
|
if (state.hasPending)
|
|
4371
6420
|
{
|
|
4372
6421
|
ReleaseSnapshot(ref state.pending);
|
|
@@ -4378,17 +6427,23 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4378
6427
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
4379
6428
|
private static void StageGlobalDispatchSnapshot<TMessage>(
|
|
4380
6429
|
MessageBus messageBus,
|
|
4381
|
-
|
|
4382
|
-
|
|
6430
|
+
BusGlobalSlot handlers,
|
|
6431
|
+
DispatchKind kind
|
|
4383
6432
|
)
|
|
4384
6433
|
where TMessage : IMessage
|
|
4385
6434
|
{
|
|
4386
|
-
|
|
6435
|
+
// DispatchKind has no None sentinel; the bus only reaches this path
|
|
6436
|
+
// through register sites that pass a valid kind, so the legacy
|
|
6437
|
+
// category-None short-circuit is no longer needed -- the
|
|
6438
|
+
// `handlers == null` guard alone suffices.
|
|
6439
|
+
if (handlers == null)
|
|
4387
6440
|
{
|
|
4388
6441
|
return;
|
|
4389
6442
|
}
|
|
4390
6443
|
|
|
4391
|
-
|
|
6444
|
+
ref DispatchState slotState = ref SelectGlobalDispatchState(handlers, kind);
|
|
6445
|
+
slotState ??= new DispatchState();
|
|
6446
|
+
DispatchState state = slotState;
|
|
4392
6447
|
if (state.hasPending)
|
|
4393
6448
|
{
|
|
4394
6449
|
ReleaseSnapshot(ref state.pending);
|
|
@@ -4398,6 +6453,29 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4398
6453
|
state.pendingDirty = true;
|
|
4399
6454
|
}
|
|
4400
6455
|
|
|
6456
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
6457
|
+
private static ref DispatchState SelectGlobalDispatchState(
|
|
6458
|
+
BusGlobalSlot slot,
|
|
6459
|
+
DispatchKind kind
|
|
6460
|
+
)
|
|
6461
|
+
{
|
|
6462
|
+
switch (kind)
|
|
6463
|
+
{
|
|
6464
|
+
case DispatchKind.Untargeted:
|
|
6465
|
+
return ref slot.untargetedDispatchState;
|
|
6466
|
+
case DispatchKind.Targeted:
|
|
6467
|
+
return ref slot.targetedDispatchState;
|
|
6468
|
+
case DispatchKind.Broadcast:
|
|
6469
|
+
return ref slot.broadcastDispatchState;
|
|
6470
|
+
default:
|
|
6471
|
+
throw new ArgumentOutOfRangeException(
|
|
6472
|
+
nameof(kind),
|
|
6473
|
+
kind,
|
|
6474
|
+
"SelectGlobalDispatchState only supports Untargeted, Targeted, Broadcast."
|
|
6475
|
+
);
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
|
|
4401
6479
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
4402
6480
|
private static void ReleaseSnapshot(ref DispatchSnapshot snapshot)
|
|
4403
6481
|
{
|
|
@@ -4414,7 +6492,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4414
6492
|
private static DispatchSnapshot AcquireDispatchSnapshot<TMessage>(
|
|
4415
6493
|
MessageBus messageBus,
|
|
4416
6494
|
HandlerCache<int, HandlerCache> handlers,
|
|
4417
|
-
|
|
6495
|
+
SlotKey slotKey,
|
|
4418
6496
|
long emissionId
|
|
4419
6497
|
)
|
|
4420
6498
|
where TMessage : IMessage
|
|
@@ -4424,14 +6502,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4424
6502
|
return DispatchSnapshot.Empty;
|
|
4425
6503
|
}
|
|
4426
6504
|
|
|
4427
|
-
if (
|
|
6505
|
+
if (slotKey == SlotKey.None)
|
|
4428
6506
|
{
|
|
4429
6507
|
return DispatchSnapshot.Empty;
|
|
4430
6508
|
}
|
|
4431
6509
|
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
);
|
|
6510
|
+
Touch(handlers, messageBus._tickCounter);
|
|
6511
|
+
DispatchState state = handlers.dispatchState ??= new DispatchState();
|
|
4435
6512
|
|
|
4436
6513
|
bool hasHandlers = handlers.handlers.Count > 0;
|
|
4437
6514
|
|
|
@@ -4441,7 +6518,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4441
6518
|
{
|
|
4442
6519
|
ReleaseSnapshot(ref state.pending);
|
|
4443
6520
|
state.pending = hasHandlers
|
|
4444
|
-
? BuildDispatchSnapshot<TMessage>(messageBus, handlers,
|
|
6521
|
+
? BuildDispatchSnapshot<TMessage>(messageBus, handlers, slotKey)
|
|
4445
6522
|
: DispatchSnapshot.Empty;
|
|
4446
6523
|
|
|
4447
6524
|
state.pendingDirty = false;
|
|
@@ -4450,7 +6527,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4450
6527
|
else if (state.active.IsEmpty && hasHandlers)
|
|
4451
6528
|
{
|
|
4452
6529
|
ReleaseSnapshot(ref state.pending);
|
|
4453
|
-
state.pending = BuildDispatchSnapshot<TMessage>(messageBus, handlers,
|
|
6530
|
+
state.pending = BuildDispatchSnapshot<TMessage>(messageBus, handlers, slotKey);
|
|
4454
6531
|
state.hasPending = true;
|
|
4455
6532
|
state.pendingDirty = false;
|
|
4456
6533
|
}
|
|
@@ -4464,7 +6541,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4464
6541
|
{
|
|
4465
6542
|
ReleaseSnapshot(ref state.pending);
|
|
4466
6543
|
state.pending = hasHandlers
|
|
4467
|
-
? BuildDispatchSnapshot<TMessage>(messageBus, handlers,
|
|
6544
|
+
? BuildDispatchSnapshot<TMessage>(messageBus, handlers, slotKey)
|
|
4468
6545
|
: DispatchSnapshot.Empty;
|
|
4469
6546
|
|
|
4470
6547
|
state.pendingDirty = false;
|
|
@@ -4490,7 +6567,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4490
6567
|
private static DispatchSnapshot BuildDispatchSnapshot<TMessage>(
|
|
4491
6568
|
MessageBus messageBus,
|
|
4492
6569
|
HandlerCache<int, HandlerCache> handlers,
|
|
4493
|
-
|
|
6570
|
+
SlotKey slotKey
|
|
4494
6571
|
)
|
|
4495
6572
|
where TMessage : IMessage
|
|
4496
6573
|
{
|
|
@@ -4527,7 +6604,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4527
6604
|
FillDispatchEntries<TMessage>(
|
|
4528
6605
|
messageBus,
|
|
4529
6606
|
handlerLookup,
|
|
4530
|
-
|
|
6607
|
+
slotKey,
|
|
4531
6608
|
priority,
|
|
4532
6609
|
entries
|
|
4533
6610
|
);
|
|
@@ -4540,7 +6617,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4540
6617
|
private static void FillDispatchEntries<TMessage>(
|
|
4541
6618
|
MessageBus messageBus,
|
|
4542
6619
|
Dictionary<MessageHandler, int> handlerLookup,
|
|
4543
|
-
|
|
6620
|
+
SlotKey slotKey,
|
|
4544
6621
|
int priority,
|
|
4545
6622
|
DispatchEntry[] entries
|
|
4546
6623
|
)
|
|
@@ -4551,12 +6628,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4551
6628
|
return;
|
|
4552
6629
|
}
|
|
4553
6630
|
|
|
4554
|
-
PrefreezeDescriptor prefreeze = CreatePrefreezeDescriptor(
|
|
6631
|
+
PrefreezeDescriptor prefreeze = CreatePrefreezeDescriptor(slotKey, priority);
|
|
4555
6632
|
int index = 0;
|
|
4556
6633
|
foreach (KeyValuePair<MessageHandler, int> kvp in handlerLookup)
|
|
4557
6634
|
{
|
|
4558
6635
|
MessageHandler messageHandler = kvp.Key;
|
|
4559
|
-
object dispatch = GetDispatchLink<TMessage>(messageBus, messageHandler,
|
|
6636
|
+
object dispatch = GetDispatchLink<TMessage>(messageBus, messageHandler, slotKey);
|
|
4560
6637
|
entries[index++] = new DispatchEntry(messageHandler, dispatch, prefreeze);
|
|
4561
6638
|
}
|
|
4562
6639
|
if (index < entries.Length)
|
|
@@ -4568,19 +6645,22 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4568
6645
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
4569
6646
|
private static DispatchSnapshot AcquireGlobalDispatchSnapshot<TMessage>(
|
|
4570
6647
|
MessageBus messageBus,
|
|
4571
|
-
|
|
4572
|
-
|
|
6648
|
+
BusGlobalSlot handlers,
|
|
6649
|
+
DispatchKind kind,
|
|
4573
6650
|
long emissionId
|
|
4574
6651
|
)
|
|
4575
6652
|
where TMessage : IMessage
|
|
4576
6653
|
{
|
|
4577
|
-
if (handlers == null
|
|
6654
|
+
if (handlers == null)
|
|
4578
6655
|
{
|
|
4579
6656
|
return DispatchSnapshot.Empty;
|
|
4580
6657
|
}
|
|
4581
6658
|
|
|
4582
|
-
|
|
4583
|
-
|
|
6659
|
+
handlers.lastTouchTicks = messageBus._tickCounter;
|
|
6660
|
+
ref DispatchState slotState = ref SelectGlobalDispatchState(handlers, kind);
|
|
6661
|
+
slotState ??= new DispatchState();
|
|
6662
|
+
DispatchState state = slotState;
|
|
6663
|
+
bool hasHandlers = handlers.sharedHandlers.Count > 0;
|
|
4584
6664
|
|
|
4585
6665
|
if (state.hasPending)
|
|
4586
6666
|
{
|
|
@@ -4592,7 +6672,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4592
6672
|
state.pending = BuildGlobalDispatchSnapshot<TMessage>(
|
|
4593
6673
|
messageBus,
|
|
4594
6674
|
handlers,
|
|
4595
|
-
|
|
6675
|
+
kind
|
|
4596
6676
|
);
|
|
4597
6677
|
}
|
|
4598
6678
|
else
|
|
@@ -4606,11 +6686,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4606
6686
|
else if (state.active.IsEmpty && hasHandlers)
|
|
4607
6687
|
{
|
|
4608
6688
|
ReleaseSnapshot(ref state.pending);
|
|
4609
|
-
state.pending = BuildGlobalDispatchSnapshot<TMessage>(
|
|
4610
|
-
messageBus,
|
|
4611
|
-
handlers,
|
|
4612
|
-
category
|
|
4613
|
-
);
|
|
6689
|
+
state.pending = BuildGlobalDispatchSnapshot<TMessage>(messageBus, handlers, kind);
|
|
4614
6690
|
state.hasPending = true;
|
|
4615
6691
|
state.pendingDirty = false;
|
|
4616
6692
|
}
|
|
@@ -4624,7 +6700,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4624
6700
|
{
|
|
4625
6701
|
ReleaseSnapshot(ref state.pending);
|
|
4626
6702
|
state.pending = hasHandlers
|
|
4627
|
-
? BuildGlobalDispatchSnapshot<TMessage>(messageBus, handlers,
|
|
6703
|
+
? BuildGlobalDispatchSnapshot<TMessage>(messageBus, handlers, kind)
|
|
4628
6704
|
: DispatchSnapshot.Empty;
|
|
4629
6705
|
|
|
4630
6706
|
state.pendingDirty = false;
|
|
@@ -4649,26 +6725,31 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4649
6725
|
|
|
4650
6726
|
private static DispatchSnapshot BuildGlobalDispatchSnapshot<TMessage>(
|
|
4651
6727
|
MessageBus messageBus,
|
|
4652
|
-
|
|
4653
|
-
|
|
6728
|
+
BusGlobalSlot handlers,
|
|
6729
|
+
DispatchKind kind
|
|
4654
6730
|
)
|
|
4655
6731
|
where TMessage : IMessage
|
|
4656
6732
|
{
|
|
4657
|
-
if (handlers == null || handlers.
|
|
6733
|
+
if (handlers == null || handlers.sharedHandlers.Count == 0)
|
|
4658
6734
|
{
|
|
4659
6735
|
return DispatchSnapshot.Empty;
|
|
4660
6736
|
}
|
|
4661
6737
|
|
|
4662
6738
|
DispatchBucket[] buckets = DispatchBucketPool.Rent(1);
|
|
4663
|
-
Dictionary<MessageHandler, int> handlerLookup = handlers.
|
|
6739
|
+
Dictionary<MessageHandler, int> handlerLookup = handlers.sharedHandlers;
|
|
4664
6740
|
int entryCount = handlerLookup.Count;
|
|
4665
6741
|
DispatchEntry[] entries = DispatchEntryPool.Rent(entryCount);
|
|
4666
|
-
PrefreezeDescriptor prefreeze =
|
|
6742
|
+
PrefreezeDescriptor prefreeze = CreateGlobalPrefreezeDescriptor(kind, 0);
|
|
4667
6743
|
int index = 0;
|
|
4668
6744
|
foreach (KeyValuePair<MessageHandler, int> kvp in handlerLookup)
|
|
4669
6745
|
{
|
|
4670
6746
|
MessageHandler messageHandler = kvp.Key;
|
|
4671
|
-
|
|
6747
|
+
// Global dispatch paths intentionally pass null for the
|
|
6748
|
+
// dispatch-link argument. GetDispatchLink is no longer reached
|
|
6749
|
+
// from this code path; inlining null here matches what the
|
|
6750
|
+
// legacy switch returned for all three Global cases and avoids
|
|
6751
|
+
// a per-entry call.
|
|
6752
|
+
object dispatch = null;
|
|
4672
6753
|
entries[index++] = new DispatchEntry(messageHandler, dispatch, prefreeze);
|
|
4673
6754
|
}
|
|
4674
6755
|
|
|
@@ -4677,108 +6758,100 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4677
6758
|
}
|
|
4678
6759
|
|
|
4679
6760
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
4680
|
-
private static PrefreezeDescriptor CreatePrefreezeDescriptor(
|
|
4681
|
-
DispatchCategory category,
|
|
4682
|
-
int priority
|
|
4683
|
-
)
|
|
6761
|
+
private static PrefreezeDescriptor CreatePrefreezeDescriptor(SlotKey slotKey, int priority)
|
|
4684
6762
|
{
|
|
4685
|
-
|
|
6763
|
+
if (
|
|
6764
|
+
slotKey.Phase != DispatchPhase.Handle
|
|
6765
|
+
|| slotKey.Variant != DispatchVariant.WithoutContext
|
|
6766
|
+
)
|
|
6767
|
+
{
|
|
6768
|
+
return PrefreezeDescriptor.Empty;
|
|
6769
|
+
}
|
|
6770
|
+
switch (slotKey.Kind)
|
|
4686
6771
|
{
|
|
4687
|
-
case
|
|
6772
|
+
case DispatchKind.Targeted:
|
|
4688
6773
|
return new PrefreezeDescriptor(
|
|
4689
6774
|
PrefreezeKindTargetedWithoutTargetingHandlers,
|
|
4690
6775
|
priority
|
|
4691
6776
|
);
|
|
4692
|
-
case
|
|
6777
|
+
case DispatchKind.Broadcast:
|
|
4693
6778
|
return new PrefreezeDescriptor(
|
|
4694
6779
|
PrefreezeKindBroadcastWithoutSourceHandlers,
|
|
4695
6780
|
priority
|
|
4696
6781
|
);
|
|
4697
|
-
|
|
6782
|
+
default:
|
|
6783
|
+
return PrefreezeDescriptor.Empty;
|
|
6784
|
+
}
|
|
6785
|
+
}
|
|
6786
|
+
|
|
6787
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
6788
|
+
private static PrefreezeDescriptor CreateGlobalPrefreezeDescriptor(
|
|
6789
|
+
DispatchKind kind,
|
|
6790
|
+
int priority
|
|
6791
|
+
)
|
|
6792
|
+
{
|
|
6793
|
+
switch (kind)
|
|
6794
|
+
{
|
|
6795
|
+
case DispatchKind.Untargeted:
|
|
4698
6796
|
return new PrefreezeDescriptor(PrefreezeKindGlobalUntargetedHandlers, priority);
|
|
4699
|
-
case
|
|
6797
|
+
case DispatchKind.Targeted:
|
|
4700
6798
|
return new PrefreezeDescriptor(PrefreezeKindGlobalTargetedHandlers, priority);
|
|
4701
|
-
case
|
|
6799
|
+
case DispatchKind.Broadcast:
|
|
4702
6800
|
return new PrefreezeDescriptor(PrefreezeKindGlobalBroadcastHandlers, priority);
|
|
4703
6801
|
default:
|
|
4704
|
-
|
|
6802
|
+
throw new ArgumentOutOfRangeException(
|
|
6803
|
+
nameof(kind),
|
|
6804
|
+
kind,
|
|
6805
|
+
"CreateGlobalPrefreezeDescriptor only supports Untargeted, Targeted, Broadcast."
|
|
6806
|
+
);
|
|
4705
6807
|
}
|
|
4706
6808
|
}
|
|
4707
6809
|
|
|
4708
6810
|
private static object GetDispatchLink<TMessage>(
|
|
4709
6811
|
MessageBus messageBus,
|
|
4710
6812
|
MessageHandler handler,
|
|
4711
|
-
|
|
6813
|
+
SlotKey slotKey
|
|
4712
6814
|
)
|
|
4713
6815
|
where TMessage : IMessage
|
|
4714
6816
|
{
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
case DispatchCategory.TargetedPost:
|
|
4724
|
-
return handler.GetOrCreateTargetedPostDispatchLink<TMessage>(messageBus);
|
|
4725
|
-
case DispatchCategory.TargetedWithoutTargeting:
|
|
4726
|
-
return handler.GetOrCreateTargetedWithoutTargetingDispatchLink<TMessage>(
|
|
4727
|
-
messageBus
|
|
4728
|
-
);
|
|
4729
|
-
case DispatchCategory.TargetedWithoutTargetingPost:
|
|
4730
|
-
return handler.GetOrCreateTargetedWithoutTargetingPostDispatchLink<TMessage>(
|
|
4731
|
-
messageBus
|
|
4732
|
-
);
|
|
4733
|
-
case DispatchCategory.Broadcast:
|
|
4734
|
-
return handler.GetOrCreateBroadcastDispatchLink<TMessage>(messageBus);
|
|
4735
|
-
case DispatchCategory.BroadcastPost:
|
|
4736
|
-
return handler.GetOrCreateBroadcastPostDispatchLink<TMessage>(messageBus);
|
|
4737
|
-
case DispatchCategory.BroadcastWithoutSource:
|
|
4738
|
-
return handler.GetOrCreateBroadcastWithoutSourceDispatchLink<TMessage>(
|
|
4739
|
-
messageBus
|
|
4740
|
-
);
|
|
4741
|
-
case DispatchCategory.BroadcastWithoutSourcePost:
|
|
4742
|
-
return handler.GetOrCreateBroadcastWithoutSourcePostDispatchLink<TMessage>(
|
|
4743
|
-
messageBus
|
|
4744
|
-
);
|
|
4745
|
-
case DispatchCategory.GlobalUntargeted:
|
|
4746
|
-
case DispatchCategory.GlobalTargeted:
|
|
4747
|
-
case DispatchCategory.GlobalBroadcast:
|
|
4748
|
-
return null;
|
|
4749
|
-
default:
|
|
4750
|
-
return handler.GetOrCreateUntargetedDispatchLink<TMessage>(messageBus);
|
|
6817
|
+
DispatchKind kind = slotKey.Kind;
|
|
6818
|
+
DispatchPhase phase = slotKey.Phase;
|
|
6819
|
+
DispatchVariant variant = slotKey.Variant;
|
|
6820
|
+
if (kind == DispatchKind.Untargeted)
|
|
6821
|
+
{
|
|
6822
|
+
return phase == DispatchPhase.PostProcess
|
|
6823
|
+
? handler.GetOrCreateUntargetedPostDispatchLink<TMessage>(messageBus)
|
|
6824
|
+
: handler.GetOrCreateUntargetedDispatchLink<TMessage>(messageBus);
|
|
4751
6825
|
}
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
return
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
default:
|
|
4780
|
-
return DispatchCategory.None;
|
|
6826
|
+
if (kind == DispatchKind.Targeted)
|
|
6827
|
+
{
|
|
6828
|
+
if (phase == DispatchPhase.PostProcess)
|
|
6829
|
+
{
|
|
6830
|
+
return variant == DispatchVariant.WithoutContext
|
|
6831
|
+
? handler.GetOrCreateTargetedWithoutTargetingPostDispatchLink<TMessage>(
|
|
6832
|
+
messageBus
|
|
6833
|
+
)
|
|
6834
|
+
: handler.GetOrCreateTargetedPostDispatchLink<TMessage>(messageBus);
|
|
6835
|
+
}
|
|
6836
|
+
return variant == DispatchVariant.WithoutContext
|
|
6837
|
+
? handler.GetOrCreateTargetedWithoutTargetingDispatchLink<TMessage>(messageBus)
|
|
6838
|
+
: handler.GetOrCreateTargetedDispatchLink<TMessage>(messageBus);
|
|
6839
|
+
}
|
|
6840
|
+
if (kind == DispatchKind.Broadcast)
|
|
6841
|
+
{
|
|
6842
|
+
if (phase == DispatchPhase.PostProcess)
|
|
6843
|
+
{
|
|
6844
|
+
return variant == DispatchVariant.WithoutContext
|
|
6845
|
+
? handler.GetOrCreateBroadcastWithoutSourcePostDispatchLink<TMessage>(
|
|
6846
|
+
messageBus
|
|
6847
|
+
)
|
|
6848
|
+
: handler.GetOrCreateBroadcastPostDispatchLink<TMessage>(messageBus);
|
|
6849
|
+
}
|
|
6850
|
+
return variant == DispatchVariant.WithoutContext
|
|
6851
|
+
? handler.GetOrCreateBroadcastWithoutSourceDispatchLink<TMessage>(messageBus)
|
|
6852
|
+
: handler.GetOrCreateBroadcastDispatchLink<TMessage>(messageBus);
|
|
4781
6853
|
}
|
|
6854
|
+
return handler.GetOrCreateUntargetedDispatchLink<TMessage>(messageBus);
|
|
4782
6855
|
}
|
|
4783
6856
|
|
|
4784
6857
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
@@ -4833,6 +6906,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4833
6906
|
return;
|
|
4834
6907
|
}
|
|
4835
6908
|
|
|
6909
|
+
// No fast-path short-circuit for post-processor prefreeze. See the
|
|
6910
|
+
// detailed rationale on PrefreezeUntargetedPostSnapshot; a regular
|
|
6911
|
+
// handler can register a new post-processor (same MessageHandler,
|
|
6912
|
+
// same priority) during its own execution, and the lazy first-read
|
|
6913
|
+
// inside post-processor dispatch would otherwise capture that newly
|
|
6914
|
+
// added entry. Always prefreezing pins the emission-start snapshot.
|
|
4836
6915
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
4837
6916
|
int bucketCount = snapshot.bucketCount;
|
|
4838
6917
|
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
@@ -4859,6 +6938,59 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4859
6938
|
}
|
|
4860
6939
|
}
|
|
4861
6940
|
|
|
6941
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
6942
|
+
private void PrefreezeTargetedSnapshot<TMessage>(
|
|
6943
|
+
ref InstanceId target,
|
|
6944
|
+
DispatchSnapshot snapshot
|
|
6945
|
+
)
|
|
6946
|
+
where TMessage : ITargetedMessage
|
|
6947
|
+
{
|
|
6948
|
+
if (snapshot.IsEmpty)
|
|
6949
|
+
{
|
|
6950
|
+
return;
|
|
6951
|
+
}
|
|
6952
|
+
|
|
6953
|
+
// Prefreeze fast-path short-circuit: if there is exactly one priority
|
|
6954
|
+
// bucket with at most one MessageHandler entry, no later handler can
|
|
6955
|
+
// observe a removal performed by an earlier one, so the inline lazy
|
|
6956
|
+
// freeze inside the dispatch path is sufficient. Note: a single
|
|
6957
|
+
// MessageHandler may still register multiple delegates at the same
|
|
6958
|
+
// priority; those share a HandlerActionCache that is frozen on first
|
|
6959
|
+
// read by the per-priority RunFastHandlers/RunHandlers, so the lazy
|
|
6960
|
+
// freeze covers same-priority same-MessageHandler removals correctly.
|
|
6961
|
+
// See the longer rationale on the broadcast inline prefreeze block
|
|
6962
|
+
// in SourcedBroadcast.
|
|
6963
|
+
if (snapshot.bucketCount == 1 && snapshot.buckets[0].entryCount <= 1)
|
|
6964
|
+
{
|
|
6965
|
+
return;
|
|
6966
|
+
}
|
|
6967
|
+
|
|
6968
|
+
DispatchBucket[] buckets = snapshot.buckets;
|
|
6969
|
+
int bucketCount = snapshot.bucketCount;
|
|
6970
|
+
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
6971
|
+
{
|
|
6972
|
+
DispatchBucket bucket = buckets[bucketIndex];
|
|
6973
|
+
DispatchEntry[] entries = bucket.entries;
|
|
6974
|
+
int entryCount = bucket.entryCount;
|
|
6975
|
+
if (entryCount == 0)
|
|
6976
|
+
{
|
|
6977
|
+
continue;
|
|
6978
|
+
}
|
|
6979
|
+
|
|
6980
|
+
int priority = bucket.priority;
|
|
6981
|
+
for (int entryIndex = 0; entryIndex < entryCount; ++entryIndex)
|
|
6982
|
+
{
|
|
6983
|
+
entries[entryIndex]
|
|
6984
|
+
.handler.PrefreezeTargetedHandlersForEmission<TMessage>(
|
|
6985
|
+
target,
|
|
6986
|
+
priority,
|
|
6987
|
+
_emissionId,
|
|
6988
|
+
this
|
|
6989
|
+
);
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
}
|
|
6993
|
+
|
|
4862
6994
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
4863
6995
|
private void InvokeGlobalUntargetedEntry<TMessage>(
|
|
4864
6996
|
ref TMessage message,
|
|
@@ -5007,6 +7139,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5007
7139
|
return;
|
|
5008
7140
|
}
|
|
5009
7141
|
|
|
7142
|
+
// No fast-path short-circuit for post-processor prefreeze. See the
|
|
7143
|
+
// detailed rationale on PrefreezeUntargetedPostSnapshot; a regular
|
|
7144
|
+
// handler can register a new post-processor (same MessageHandler,
|
|
7145
|
+
// same priority) during its own execution, and the lazy first-read
|
|
7146
|
+
// inside post-processor dispatch would otherwise capture that newly
|
|
7147
|
+
// added entry. Always prefreezing pins the emission-start snapshot.
|
|
5010
7148
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
5011
7149
|
int bucketCount = snapshot.bucketCount;
|
|
5012
7150
|
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
@@ -5084,6 +7222,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5084
7222
|
return;
|
|
5085
7223
|
}
|
|
5086
7224
|
|
|
7225
|
+
// No fast-path short-circuit for post-processor prefreeze. See the
|
|
7226
|
+
// detailed rationale on PrefreezeUntargetedPostSnapshot; a regular
|
|
7227
|
+
// handler can register a new post-processor (same MessageHandler,
|
|
7228
|
+
// same priority) during its own execution, and the lazy first-read
|
|
7229
|
+
// inside post-processor dispatch would otherwise capture that newly
|
|
7230
|
+
// added entry. Always prefreezing pins the emission-start snapshot.
|
|
5087
7231
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
5088
7232
|
int bucketCount = snapshot.bucketCount;
|
|
5089
7233
|
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|
|
@@ -5174,6 +7318,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5174
7318
|
return;
|
|
5175
7319
|
}
|
|
5176
7320
|
|
|
7321
|
+
// No fast-path short-circuit for post-processor prefreeze. See the
|
|
7322
|
+
// detailed rationale on PrefreezeUntargetedPostSnapshot; a regular
|
|
7323
|
+
// handler can register a new post-processor (same MessageHandler,
|
|
7324
|
+
// same priority) during its own execution, and the lazy first-read
|
|
7325
|
+
// inside post-processor dispatch would otherwise capture that newly
|
|
7326
|
+
// added entry. Always prefreezing pins the emission-start snapshot.
|
|
7327
|
+
|
|
5177
7328
|
DispatchBucket[] buckets = snapshot.buckets;
|
|
5178
7329
|
int bucketCount = snapshot.bucketCount;
|
|
5179
7330
|
for (int bucketIndex = 0; bucketIndex < bucketCount; ++bucketIndex)
|