com.wallstop-studios.dxmessaging 2.2.0 → 3.1.0

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