com.wallstop-studios.dxmessaging 3.1.0 → 3.2.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 +326 -0
- package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +3 -3
- package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +3 -3
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +8 -8
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +3 -3
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +171 -13
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +3 -3
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +391 -96
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +3 -3
- package/Editor/CustomEditors/MessageAwareComponentInspectorView.cs +279 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorView.cs.meta +11 -0
- package/Editor/DxMessagingEditorPalette.cs +66 -0
- package/Editor/DxMessagingEditorPalette.cs.meta +11 -0
- package/Editor/DxMessagingEditorTheme.cs +200 -0
- package/Editor/DxMessagingEditorTheme.cs.meta +11 -0
- package/Editor/DxMessagingMenu.cs.meta +3 -3
- package/Editor/DxMessagingSceneBuildProcessor.cs.meta +3 -3
- package/Editor/Icons/dxmessaging-icon-256.png +0 -0
- package/Editor/Icons/dxmessaging-icon-256.png.meta +156 -0
- package/Editor/Icons/dxmessaging-icon-32.png +0 -0
- package/Editor/Icons/dxmessaging-icon-32.png.meta +156 -0
- package/Editor/Icons/dxmessaging-icon-48.png +0 -0
- package/Editor/Icons/dxmessaging-icon-48.png.meta +156 -0
- package/Editor/Icons.meta +8 -0
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +2 -3
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +3 -3
- package/Editor/Settings/DxMessagingSettingsProvider.cs +368 -33
- package/Editor/SetupCscRsp.cs +232 -163
- package/Editor/Testing/MessagingComponentEditorHarness.cs +11 -4
- package/Editor/Theme/DxMessagingTheme.uss +255 -0
- package/Editor/Theme/DxMessagingTheme.uss.meta +11 -0
- package/Editor/Theme/DxTokens.uss +90 -0
- package/Editor/Theme/DxTokens.uss.meta +11 -0
- package/Editor/Theme.meta +8 -0
- package/Editor/Windows/DxMessagingFlowGraphWindow.cs +5513 -0
- package/Editor/Windows/DxMessagingFlowGraphWindow.cs.meta +11 -0
- package/Editor/Windows/DxMessagingMessageMonitorWindow.cs +2288 -0
- package/Editor/Windows/DxMessagingMessageMonitorWindow.cs.meta +11 -0
- package/Editor/Windows.meta +8 -0
- package/README.md +84 -35
- package/Runtime/Analyzers.meta +8 -0
- package/Runtime/AssemblyInfo.cs +1 -0
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +3 -3
- package/Runtime/Core/Diagnostics/MessageEmissionData.cs +29 -1
- package/Runtime/Core/DxMessagingStaticState.cs.meta +3 -3
- package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +3 -3
- package/Runtime/Core/Extensions/MessageExtensions.cs +4 -5
- package/Runtime/Core/Extensions/MessageExtensions.cs.meta +3 -3
- package/Runtime/Core/Helper/MessageCache.cs +56 -23
- package/Runtime/Core/IMessage.cs.meta +3 -3
- package/Runtime/Core/InstanceId.cs.meta +3 -3
- package/Runtime/Core/Internal/FlatDispatch.cs +32 -0
- package/Runtime/Core/Internal/TypedSlots.cs +45 -7
- package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +3 -3
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +3 -3
- package/Runtime/Core/MessageBus/IMessageBus.cs +63 -28
- package/Runtime/Core/MessageBus/IMessageBus.cs.meta +3 -3
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +3 -3
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +3 -3
- package/Runtime/Core/MessageBus/MessageBus.cs +1135 -626
- package/Runtime/Core/MessageBus/MessageBus.cs.meta +3 -3
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +3 -3
- package/Runtime/Core/MessageBus/MessageBusRegistration.cs +207 -0
- package/Runtime/Core/MessageBus/MessageBusRegistration.cs.meta +11 -0
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +3 -3
- package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +3 -3
- package/Runtime/Core/MessageBus/RegistrationLog.cs +25 -12
- package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +3 -3
- package/Runtime/Core/MessageHandler.cs +1886 -1082
- package/Runtime/Core/MessageHandler.cs.meta +3 -3
- package/Runtime/Core/MessageRegistrationHandle.cs +19 -5
- package/Runtime/Core/MessageRegistrationHandle.cs.meta +3 -3
- package/Runtime/Core/MessageRegistrationToken.cs +1932 -895
- package/Runtime/Core/MessageRegistrationToken.cs.meta +3 -3
- package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +3 -3
- package/Runtime/Core/Messages/ITargetedMessage.cs.meta +3 -3
- package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +3 -3
- package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +3 -3
- package/Runtime/Core/MessagingDebug.cs.meta +3 -3
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +3 -3
- package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +3 -3
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +3 -3
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +3 -3
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +3 -3
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +3 -3
- package/Runtime/Unity/MessageAwareComponent.cs.meta +3 -3
- package/Runtime/Unity/MessageBusProviderHandle.cs.meta +3 -3
- package/Runtime/Unity/MessagingComponent.cs.meta +3 -3
- package/Runtime/Unity/MessagingComponentInstaller.cs.meta +3 -3
- package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +3 -3
- package/Samples~/DI/Reflex/SampleInstaller.cs.meta +3 -3
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +3 -3
- package/Samples~/DI/Zenject/SampleInstaller.cs.meta +3 -3
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.cs +173 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.cs.meta +11 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.unity +424 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.unity.meta +7 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingReceiver.cs +185 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingReceiver.cs.meta +11 -0
- package/Samples~/Diagnostics Tooling Exerciser/Messages.cs +46 -0
- package/Samples~/Diagnostics Tooling Exerciser/Messages.cs.meta +11 -0
- package/Samples~/Diagnostics Tooling Exerciser/README.md +53 -0
- package/Samples~/Diagnostics Tooling Exerciser/README.md.meta +7 -0
- package/Samples~/Diagnostics Tooling Exerciser/WallstopStudios.DxMessaging.DiagnosticsToolingExerciser.Sample.asmdef +13 -0
- package/Samples~/Diagnostics Tooling Exerciser/WallstopStudios.DxMessaging.DiagnosticsToolingExerciser.Sample.asmdef.meta +7 -0
- package/Samples~/Diagnostics Tooling Exerciser.meta +8 -0
- package/Samples~/Mini Combat/Boot.cs.meta +3 -3
- package/Samples~/Mini Combat/Enemy.cs.meta +3 -3
- package/Samples~/Mini Combat/Messages.cs.meta +3 -3
- package/Samples~/Mini Combat/Player.cs.meta +3 -3
- package/Samples~/Mini Combat/README.md +1 -1
- package/Samples~/Mini Combat/UIOverlay.cs.meta +3 -3
- package/Samples~/Mini Combat/Walkthrough.md +3 -3
- package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +3 -3
- package/Samples~/UI Buttons + Inspector/Messages.cs.meta +3 -3
- package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +3 -3
- package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +3 -3
- package/SourceGenerators/Directory.Build.props +5 -5
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +3 -3
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +12 -11
- package/docs/images/DxMessaging-banner.svg +79 -0
- package/docs/images/DxMessaging-banner.svg.meta +53 -0
- package/docs/images.meta +8 -0
- package/docs.meta +8 -0
- package/package.json +17 -5
- /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +0 -0
|
@@ -59,6 +59,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
59
59
|
/// </summary>
|
|
60
60
|
public long EmissionId => 0 < _dispatchDepth ? _scopedEmissionId : _emissionId;
|
|
61
61
|
internal long TickCounter => _tickCounter;
|
|
62
|
+
internal bool IsDispatching => _dispatchDepth > 0;
|
|
62
63
|
|
|
63
64
|
private const long DefaultIdleEvictionTicks = 30;
|
|
64
65
|
private const double DefaultEvictionTickIntervalSeconds = 5d;
|
|
@@ -173,18 +174,74 @@ namespace DxMessaging.Core.MessageBus
|
|
|
173
174
|
|
|
174
175
|
private static CollectionPool<
|
|
175
176
|
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
176
|
-
>
|
|
177
|
+
> ContextHandlerByTargetDictsOverride;
|
|
178
|
+
|
|
179
|
+
private static CollectionPool<
|
|
180
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
181
|
+
> ContextHandlerByTargetDicts =>
|
|
182
|
+
ContextHandlerByTargetDictsOverride ?? ContextHandlerByTargetDictPoolHolder.Instance;
|
|
183
|
+
|
|
184
|
+
private static CollectionPool<
|
|
185
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
186
|
+
> CreateContextHandlerByTargetPool(int maxRetained, bool useLru)
|
|
187
|
+
{
|
|
188
|
+
return new CollectionPool<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>>(
|
|
189
|
+
maxRetained,
|
|
190
|
+
useLru,
|
|
191
|
+
factory: static () => CreateFreshContextHandlerMap(),
|
|
192
|
+
onRecycled: static dict => dict.Clear()
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private static Dictionary<
|
|
197
|
+
InstanceId,
|
|
198
|
+
HandlerCache<int, HandlerCache>
|
|
199
|
+
> CreateFreshContextHandlerMap()
|
|
200
|
+
{
|
|
201
|
+
return new Dictionary<InstanceId, HandlerCache<int, HandlerCache>>();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
internal static object CreateContextMapSeedForBenchmark()
|
|
205
|
+
{
|
|
206
|
+
return new HandlerCache<int, HandlerCache>();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
internal static object CreatePopulatedContextMapForBenchmark(InstanceId[] keys, object seed)
|
|
210
|
+
{
|
|
211
|
+
if (keys == null)
|
|
212
|
+
{
|
|
213
|
+
throw new ArgumentNullException(nameof(keys));
|
|
214
|
+
}
|
|
215
|
+
HandlerCache<int, HandlerCache> typedSeed =
|
|
216
|
+
seed as HandlerCache<int, HandlerCache>
|
|
217
|
+
?? throw new ArgumentException("Unexpected context-map seed type.", nameof(seed));
|
|
218
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> map =
|
|
219
|
+
CreateFreshContextHandlerMap();
|
|
220
|
+
for (int index = 0; index < keys.Length; ++index)
|
|
221
|
+
{
|
|
222
|
+
map[keys[index]] = typedSeed;
|
|
223
|
+
}
|
|
224
|
+
return map;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
internal static void ObserveContextMapForBenchmark(
|
|
228
|
+
object opaqueMap,
|
|
229
|
+
out int count,
|
|
230
|
+
out int capacity
|
|
231
|
+
)
|
|
232
|
+
{
|
|
233
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>> map =
|
|
234
|
+
opaqueMap as Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
235
|
+
?? throw new ArgumentException("Unexpected context-map type.", nameof(opaqueMap));
|
|
236
|
+
count = map.Count;
|
|
237
|
+
capacity = map.EnsureCapacity(0);
|
|
238
|
+
}
|
|
177
239
|
|
|
178
240
|
private static class ContextHandlerByTargetDictPoolHolder
|
|
179
241
|
{
|
|
180
242
|
public static readonly CollectionPool<
|
|
181
243
|
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
182
|
-
> Instance =
|
|
183
|
-
maxRetained: 512,
|
|
184
|
-
useLru: true,
|
|
185
|
-
factory: static () => new Dictionary<InstanceId, HandlerCache<int, HandlerCache>>(),
|
|
186
|
-
onRecycled: static dict => dict.Clear()
|
|
187
|
-
);
|
|
244
|
+
> Instance = CreateContextHandlerByTargetPool(maxRetained: 512, useLru: true);
|
|
188
245
|
}
|
|
189
246
|
|
|
190
247
|
internal static int ResetStaticPools()
|
|
@@ -192,11 +249,10 @@ namespace DxMessaging.Core.MessageBus
|
|
|
192
249
|
return ContextHandlerByTargetDicts.Trim(0);
|
|
193
250
|
}
|
|
194
251
|
|
|
195
|
-
// One bucket entry per registered MessageHandler.
|
|
196
|
-
// populated only for
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
// dispatch happens exclusively through the resolved flat arrays.
|
|
252
|
+
// One bucket entry per registered MessageHandler. Bucket arrays are
|
|
253
|
+
// populated only for GLOBAL accept-all snapshots (the only remaining
|
|
254
|
+
// bucket-walking dispatch path). Non-global snapshots carry a resolved
|
|
255
|
+
// flat array and its entry count directly.
|
|
200
256
|
internal readonly struct DispatchEntry
|
|
201
257
|
{
|
|
202
258
|
public DispatchEntry(MessageHandler handler)
|
|
@@ -227,11 +283,6 @@ namespace DxMessaging.Core.MessageBus
|
|
|
227
283
|
public int entryCount;
|
|
228
284
|
public bool pooledEntries;
|
|
229
285
|
|
|
230
|
-
public static DispatchBucket CreateEmpty(int priority)
|
|
231
|
-
{
|
|
232
|
-
return new DispatchBucket(priority, Array.Empty<DispatchEntry>(), 0, false);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
286
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
236
287
|
public void ReleaseEntries()
|
|
237
288
|
{
|
|
@@ -253,18 +304,42 @@ namespace DxMessaging.Core.MessageBus
|
|
|
253
304
|
public static readonly DispatchSnapshot Empty = new DispatchSnapshot(
|
|
254
305
|
Array.Empty<DispatchBucket>(),
|
|
255
306
|
0,
|
|
307
|
+
0,
|
|
308
|
+
false,
|
|
256
309
|
false
|
|
257
310
|
);
|
|
258
311
|
|
|
259
|
-
public DispatchSnapshot(
|
|
312
|
+
public DispatchSnapshot(
|
|
313
|
+
DispatchBucket[] buckets,
|
|
314
|
+
int bucketCount,
|
|
315
|
+
int entryCount,
|
|
316
|
+
bool hasRegistrations,
|
|
317
|
+
bool pooled
|
|
318
|
+
)
|
|
260
319
|
{
|
|
261
320
|
this.buckets = buckets;
|
|
262
|
-
bucketCount =
|
|
321
|
+
this.bucketCount = bucketCount;
|
|
322
|
+
this.entryCount = entryCount;
|
|
323
|
+
this.hasRegistrations = hasRegistrations;
|
|
263
324
|
_pooled = pooled;
|
|
325
|
+
_pooledBuckets = pooled;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
public DispatchSnapshot(FlatDispatchArray flat, bool hasRegistrations)
|
|
329
|
+
{
|
|
330
|
+
buckets = Array.Empty<DispatchBucket>();
|
|
331
|
+
bucketCount = 0;
|
|
332
|
+
entryCount = flat?.Count ?? 0;
|
|
333
|
+
this.hasRegistrations = hasRegistrations;
|
|
334
|
+
this.flat = flat;
|
|
335
|
+
_pooled = true;
|
|
336
|
+
_pooledBuckets = false;
|
|
264
337
|
}
|
|
265
338
|
|
|
266
339
|
public DispatchBucket[] buckets;
|
|
267
340
|
public int bucketCount;
|
|
341
|
+
public int entryCount;
|
|
342
|
+
public bool hasRegistrations;
|
|
268
343
|
|
|
269
344
|
// Resolved flat dispatch entries for every flattened slot kind:
|
|
270
345
|
// untargeted handle/post, targeted/broadcast Default handle/post
|
|
@@ -278,8 +353,9 @@ namespace DxMessaging.Core.MessageBus
|
|
|
278
353
|
// Release().
|
|
279
354
|
public FlatDispatchArray flat;
|
|
280
355
|
private bool _pooled;
|
|
356
|
+
private readonly bool _pooledBuckets;
|
|
281
357
|
|
|
282
|
-
public bool
|
|
358
|
+
public bool IsInitialized => !ReferenceEquals(this, Empty);
|
|
283
359
|
|
|
284
360
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
285
361
|
public void Release()
|
|
@@ -292,15 +368,21 @@ namespace DxMessaging.Core.MessageBus
|
|
|
292
368
|
flat?.Release();
|
|
293
369
|
flat = null;
|
|
294
370
|
|
|
295
|
-
|
|
371
|
+
if (_pooledBuckets)
|
|
296
372
|
{
|
|
297
|
-
|
|
373
|
+
for (int i = 0; i < bucketCount; ++i)
|
|
374
|
+
{
|
|
375
|
+
buckets[i].ReleaseEntries();
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
Array.Clear(buckets, 0, bucketCount);
|
|
379
|
+
DispatchBucketPool.Return(buckets);
|
|
298
380
|
}
|
|
299
381
|
|
|
300
|
-
Array.Clear(buckets, 0, bucketCount);
|
|
301
|
-
DispatchBucketPool.Return(buckets);
|
|
302
382
|
buckets = Array.Empty<DispatchBucket>();
|
|
303
383
|
bucketCount = 0;
|
|
384
|
+
entryCount = 0;
|
|
385
|
+
hasRegistrations = false;
|
|
304
386
|
_pooled = false;
|
|
305
387
|
}
|
|
306
388
|
}
|
|
@@ -375,15 +457,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
375
457
|
{
|
|
376
458
|
public readonly Dictionary<TKey, TValue> handlers = new();
|
|
377
459
|
public readonly List<TKey> order = new();
|
|
378
|
-
public readonly List<KeyValuePair<TKey, TValue>> cache = new();
|
|
379
460
|
public long version;
|
|
380
|
-
public long lastSeenVersion = -1;
|
|
381
|
-
public long lastSeenEmissionId = -1;
|
|
382
461
|
public long lastTouchTicks;
|
|
383
462
|
public DispatchState dispatchState;
|
|
384
463
|
|
|
385
464
|
/// <summary>
|
|
386
|
-
/// Clears all
|
|
465
|
+
/// Clears all handler references and resets the mutation version.
|
|
387
466
|
/// </summary>
|
|
388
467
|
public void Clear()
|
|
389
468
|
{
|
|
@@ -392,10 +471,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
392
471
|
// is handled by sweep-driven slot reset paths.
|
|
393
472
|
handlers.Clear();
|
|
394
473
|
order.Clear();
|
|
395
|
-
cache.Clear();
|
|
396
474
|
version = 0;
|
|
397
|
-
lastSeenVersion = -1;
|
|
398
|
-
lastSeenEmissionId = -1;
|
|
399
475
|
dispatchState?.Reset();
|
|
400
476
|
dispatchState = null;
|
|
401
477
|
}
|
|
@@ -404,13 +480,11 @@ namespace DxMessaging.Core.MessageBus
|
|
|
404
480
|
private sealed class InterceptorCache<TValue>
|
|
405
481
|
{
|
|
406
482
|
public readonly SortedList<int, List<TValue>> handlers = new();
|
|
407
|
-
public long lastSeenEmissionId = -1;
|
|
408
483
|
public long lastTouchTicks;
|
|
409
484
|
|
|
410
485
|
public void Clear()
|
|
411
486
|
{
|
|
412
487
|
handlers.Clear();
|
|
413
|
-
lastSeenEmissionId = -1;
|
|
414
488
|
lastTouchTicks = 0;
|
|
415
489
|
}
|
|
416
490
|
}
|
|
@@ -489,16 +563,14 @@ namespace DxMessaging.Core.MessageBus
|
|
|
489
563
|
// across components. Invariants: contains exactly the keys of
|
|
490
564
|
// <see cref="handlers"/>; a key is appended on its FIRST
|
|
491
565
|
// registration only (refcount increments do not move it) and
|
|
492
|
-
// removed when its refcount drops to zero.
|
|
493
|
-
//
|
|
566
|
+
// removed when its refcount drops to zero. The MessageHandler-side
|
|
567
|
+
// cache keeps this invariant inside one ordered container; the
|
|
568
|
+
// bus-side map remains a separate candidate.
|
|
494
569
|
public readonly List<MessageHandler> insertionOrder = new();
|
|
495
|
-
public readonly List<MessageHandler> cache = new();
|
|
496
570
|
public long version;
|
|
497
|
-
public long lastSeenVersion = -1;
|
|
498
|
-
public long lastSeenEmissionId = -1;
|
|
499
571
|
|
|
500
572
|
/// <summary>
|
|
501
|
-
/// Clears all
|
|
573
|
+
/// Clears all handler references and resets the mutation version.
|
|
502
574
|
/// </summary>
|
|
503
575
|
public void Clear()
|
|
504
576
|
{
|
|
@@ -507,10 +579,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
507
579
|
// is handled by sweep-driven slot reset paths.
|
|
508
580
|
handlers.Clear();
|
|
509
581
|
insertionOrder.Clear();
|
|
510
|
-
cache.Clear();
|
|
511
582
|
version = 0;
|
|
512
|
-
lastSeenVersion = -1;
|
|
513
|
-
lastSeenEmissionId = -1;
|
|
514
583
|
}
|
|
515
584
|
}
|
|
516
585
|
|
|
@@ -784,7 +853,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
784
853
|
public static bool SourcedRegistered;
|
|
785
854
|
}
|
|
786
855
|
|
|
787
|
-
public RegistrationLog Log => _log;
|
|
856
|
+
public RegistrationLog Log => _log ??= new RegistrationLog();
|
|
788
857
|
|
|
789
858
|
// Storage trio for typed and global dispatch. _scalarSinks and
|
|
790
859
|
// _contextSinks are SlotKey-indexed arrays of MessageCache (call sites
|
|
@@ -829,6 +898,127 @@ namespace DxMessaging.Core.MessageBus
|
|
|
829
898
|
private readonly MessageCache<DispatchPlan> _targetedDispatchPlans = new();
|
|
830
899
|
private readonly MessageCache<DispatchPlan> _broadcastDispatchPlans = new();
|
|
831
900
|
|
|
901
|
+
internal readonly struct FlatSnapshotStorageObservation
|
|
902
|
+
{
|
|
903
|
+
internal FlatSnapshotStorageObservation(
|
|
904
|
+
int entryCount,
|
|
905
|
+
int arrayCapacity,
|
|
906
|
+
int emptyHolderPoolCount
|
|
907
|
+
)
|
|
908
|
+
{
|
|
909
|
+
EntryCount = entryCount;
|
|
910
|
+
ArrayCapacity = arrayCapacity;
|
|
911
|
+
EmptyHolderPoolCount = emptyHolderPoolCount;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
internal int EntryCount { get; }
|
|
915
|
+
|
|
916
|
+
internal int ArrayCapacity { get; }
|
|
917
|
+
|
|
918
|
+
/// <summary>
|
|
919
|
+
/// Released holder objects currently pooled with empty entry arrays. This does not
|
|
920
|
+
/// measure arrays or retained bytes; ArrayPool memory retention requires an external
|
|
921
|
+
/// memory measurement.
|
|
922
|
+
/// </summary>
|
|
923
|
+
internal int EmptyHolderPoolCount { get; }
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
internal readonly struct PriorityStorageObservation
|
|
927
|
+
{
|
|
928
|
+
internal PriorityStorageObservation(int entries, int mapCapacity, int orderCapacity)
|
|
929
|
+
{
|
|
930
|
+
Entries = entries;
|
|
931
|
+
MapCapacity = mapCapacity;
|
|
932
|
+
OrderCapacity = orderCapacity;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
internal int Entries { get; }
|
|
936
|
+
|
|
937
|
+
internal int MapCapacity { get; }
|
|
938
|
+
|
|
939
|
+
internal int OrderCapacity { get; }
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
internal bool TryObserveUntargetedPriorityStorageForBenchmark<TMessage>(
|
|
943
|
+
out PriorityStorageObservation observation
|
|
944
|
+
)
|
|
945
|
+
where TMessage : IUntargetedMessage
|
|
946
|
+
{
|
|
947
|
+
if (
|
|
948
|
+
!_scalarSinks[BusSinkIndex.UntargetedHandleDefault]
|
|
949
|
+
.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> handlers)
|
|
950
|
+
)
|
|
951
|
+
{
|
|
952
|
+
observation = default;
|
|
953
|
+
return false;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
observation = ObservePriorityStorage(handlers);
|
|
957
|
+
return true;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
internal static object CreatePriorityStorageOwnerForBenchmark()
|
|
961
|
+
{
|
|
962
|
+
return new HandlerCache<int, HandlerCache>();
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
internal static bool TryObservePriorityStorageOwnerForBenchmark(
|
|
966
|
+
object owner,
|
|
967
|
+
out PriorityStorageObservation observation
|
|
968
|
+
)
|
|
969
|
+
{
|
|
970
|
+
if (owner is not HandlerCache<int, HandlerCache> handlers)
|
|
971
|
+
{
|
|
972
|
+
observation = default;
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
observation = ObservePriorityStorage(handlers);
|
|
977
|
+
return true;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
private static PriorityStorageObservation ObservePriorityStorage(
|
|
981
|
+
HandlerCache<int, HandlerCache> handlers
|
|
982
|
+
)
|
|
983
|
+
{
|
|
984
|
+
return new PriorityStorageObservation(
|
|
985
|
+
handlers.handlers.Count,
|
|
986
|
+
handlers.handlers.EnsureCapacity(0),
|
|
987
|
+
handlers.order.Capacity
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/// <summary>
|
|
992
|
+
/// Read-only benchmark telemetry for the active untargeted flat snapshot of
|
|
993
|
+
/// <typeparamref name="TMessage"/>. This method does not acquire, build, or mutate a
|
|
994
|
+
/// snapshot, so observing storage cannot alter the path being measured.
|
|
995
|
+
/// </summary>
|
|
996
|
+
internal bool TryObserveUntargetedFlatSnapshotStorageForBenchmark<TMessage>(
|
|
997
|
+
out FlatSnapshotStorageObservation observation
|
|
998
|
+
)
|
|
999
|
+
where TMessage : IUntargetedMessage
|
|
1000
|
+
{
|
|
1001
|
+
if (
|
|
1002
|
+
!_scalarSinks[BusSinkIndex.UntargetedHandleDefault]
|
|
1003
|
+
.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> handlers)
|
|
1004
|
+
|| handlers.dispatchState == null
|
|
1005
|
+
|| ReferenceEquals(handlers.dispatchState.active, DispatchSnapshot.Empty)
|
|
1006
|
+
|| handlers.dispatchState.active.flat == null
|
|
1007
|
+
)
|
|
1008
|
+
{
|
|
1009
|
+
observation = default;
|
|
1010
|
+
return false;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
FlatDispatchArray flat = handlers.dispatchState.active.flat;
|
|
1014
|
+
observation = new FlatSnapshotStorageObservation(
|
|
1015
|
+
flat.Count,
|
|
1016
|
+
flat.Capacity,
|
|
1017
|
+
flat.EmptyHolderPoolCount
|
|
1018
|
+
);
|
|
1019
|
+
return true;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
832
1022
|
// Bumped by every mutation that can change what a DispatchPlan
|
|
833
1023
|
// caches or decides. Plans compare their stamp against this value at
|
|
834
1024
|
// every emission; the emit shells also re-compare mid-emission to
|
|
@@ -923,7 +1113,9 @@ namespace DxMessaging.Core.MessageBus
|
|
|
923
1113
|
}
|
|
924
1114
|
|
|
925
1115
|
#if UNITY_2021_3_OR_NEWER
|
|
926
|
-
private static
|
|
1116
|
+
private static List<WeakReference<MessageBus>> IdleSweepBuses = new();
|
|
1117
|
+
private static IdleSweepRegistryBenchmarkScope ActiveIdleSweepRegistryBenchmarkScope;
|
|
1118
|
+
private static ContextMapPoolBenchmarkScope ActiveContextMapPoolBenchmarkScope;
|
|
927
1119
|
private static bool RuntimeSettingsSubscribed;
|
|
928
1120
|
|
|
929
1121
|
private static void RegisterForIdleSweeps(MessageBus bus)
|
|
@@ -997,6 +1189,206 @@ namespace DxMessaging.Core.MessageBus
|
|
|
997
1189
|
ResetStaticPools();
|
|
998
1190
|
}
|
|
999
1191
|
|
|
1192
|
+
internal static IDisposable IsolateIdleSweepRegistryForBenchmark()
|
|
1193
|
+
{
|
|
1194
|
+
return new IdleSweepRegistryBenchmarkScope();
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
internal static int IdleSweepRegistryCountForBenchmark => IdleSweepBuses.Count;
|
|
1198
|
+
|
|
1199
|
+
internal static object IdleSweepRegistryIdentityForBenchmark => IdleSweepBuses;
|
|
1200
|
+
|
|
1201
|
+
internal static IDisposable IsolateContextMapPoolForBenchmark()
|
|
1202
|
+
{
|
|
1203
|
+
return new ContextMapPoolBenchmarkScope();
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
internal static bool ContextMapPoolOverrideActiveForBenchmark =>
|
|
1207
|
+
ContextHandlerByTargetDictsOverride != null;
|
|
1208
|
+
|
|
1209
|
+
internal static ContextMapPoolBenchmarkObservation ObserveContextMapPoolForBenchmark()
|
|
1210
|
+
{
|
|
1211
|
+
CollectionPool<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> current =
|
|
1212
|
+
ContextHandlerByTargetDicts;
|
|
1213
|
+
return new ContextMapPoolBenchmarkObservation(
|
|
1214
|
+
current,
|
|
1215
|
+
current.UseLru,
|
|
1216
|
+
current.MaxRetained
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
internal static void ConfigureContextMapPoolForBenchmark(bool useLru, int maxRetained)
|
|
1221
|
+
{
|
|
1222
|
+
ContextHandlerByTargetDicts.UseLru = useLru;
|
|
1223
|
+
ContextHandlerByTargetDicts.MaxRetained = maxRetained;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
private sealed class IdleSweepRegistryBenchmarkScope : IDisposable
|
|
1227
|
+
{
|
|
1228
|
+
private readonly List<WeakReference<MessageBus>> _saved;
|
|
1229
|
+
private readonly IdleSweepRegistryBenchmarkScope _parent;
|
|
1230
|
+
private readonly int _ownerThreadId;
|
|
1231
|
+
private bool _disposed;
|
|
1232
|
+
|
|
1233
|
+
internal IdleSweepRegistryBenchmarkScope()
|
|
1234
|
+
{
|
|
1235
|
+
_ownerThreadId = Environment.CurrentManagedThreadId;
|
|
1236
|
+
lock (typeof(IdleSweepRegistryBenchmarkScope))
|
|
1237
|
+
{
|
|
1238
|
+
if (
|
|
1239
|
+
ActiveIdleSweepRegistryBenchmarkScope != null
|
|
1240
|
+
&& ActiveIdleSweepRegistryBenchmarkScope._ownerThreadId != _ownerThreadId
|
|
1241
|
+
)
|
|
1242
|
+
{
|
|
1243
|
+
throw new InvalidOperationException(
|
|
1244
|
+
"Nested idle-sweep benchmark scopes must share one owning thread."
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
_parent = ActiveIdleSweepRegistryBenchmarkScope;
|
|
1249
|
+
_saved = IdleSweepBuses;
|
|
1250
|
+
ActiveIdleSweepRegistryBenchmarkScope = this;
|
|
1251
|
+
IdleSweepBuses = new List<WeakReference<MessageBus>>();
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
public void Dispose()
|
|
1256
|
+
{
|
|
1257
|
+
lock (typeof(IdleSweepRegistryBenchmarkScope))
|
|
1258
|
+
{
|
|
1259
|
+
if (_disposed)
|
|
1260
|
+
{
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
if (Environment.CurrentManagedThreadId != _ownerThreadId)
|
|
1264
|
+
{
|
|
1265
|
+
throw new InvalidOperationException(
|
|
1266
|
+
"Idle-sweep benchmark scopes must be disposed on their owning thread."
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
_disposed = true;
|
|
1271
|
+
while (
|
|
1272
|
+
ActiveIdleSweepRegistryBenchmarkScope != null
|
|
1273
|
+
&& ActiveIdleSweepRegistryBenchmarkScope._disposed
|
|
1274
|
+
)
|
|
1275
|
+
{
|
|
1276
|
+
IdleSweepRegistryBenchmarkScope completed =
|
|
1277
|
+
ActiveIdleSweepRegistryBenchmarkScope;
|
|
1278
|
+
IdleSweepBuses = completed._saved;
|
|
1279
|
+
ActiveIdleSweepRegistryBenchmarkScope = completed._parent;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
private sealed class ContextMapPoolBenchmarkScope : IDisposable
|
|
1286
|
+
{
|
|
1287
|
+
private readonly CollectionPool<
|
|
1288
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
1289
|
+
> _savedOverride;
|
|
1290
|
+
private readonly CollectionPool<
|
|
1291
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
1292
|
+
> _savedPool;
|
|
1293
|
+
private readonly CollectionPool<
|
|
1294
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
1295
|
+
> _isolated;
|
|
1296
|
+
private readonly ContextMapPoolBenchmarkScope _parent;
|
|
1297
|
+
private readonly int _ownerThreadId;
|
|
1298
|
+
private readonly bool _initialUseLru;
|
|
1299
|
+
private readonly int _initialMaxRetained;
|
|
1300
|
+
private bool _disposed;
|
|
1301
|
+
|
|
1302
|
+
internal ContextMapPoolBenchmarkScope()
|
|
1303
|
+
{
|
|
1304
|
+
_ownerThreadId = Environment.CurrentManagedThreadId;
|
|
1305
|
+
lock (typeof(ContextMapPoolBenchmarkScope))
|
|
1306
|
+
{
|
|
1307
|
+
if (
|
|
1308
|
+
ActiveContextMapPoolBenchmarkScope != null
|
|
1309
|
+
&& ActiveContextMapPoolBenchmarkScope._ownerThreadId != _ownerThreadId
|
|
1310
|
+
)
|
|
1311
|
+
{
|
|
1312
|
+
throw new InvalidOperationException(
|
|
1313
|
+
"Nested context-map benchmark scopes must share one owning thread."
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
_parent = ActiveContextMapPoolBenchmarkScope;
|
|
1318
|
+
_savedOverride = ContextHandlerByTargetDictsOverride;
|
|
1319
|
+
_savedPool = ContextHandlerByTargetDicts;
|
|
1320
|
+
_initialUseLru = _savedPool.UseLru;
|
|
1321
|
+
_initialMaxRetained = _savedPool.MaxRetained;
|
|
1322
|
+
_isolated = CreateContextHandlerByTargetPool(
|
|
1323
|
+
_initialMaxRetained,
|
|
1324
|
+
_initialUseLru
|
|
1325
|
+
);
|
|
1326
|
+
ActiveContextMapPoolBenchmarkScope = this;
|
|
1327
|
+
ContextHandlerByTargetDictsOverride = _isolated;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
public void Dispose()
|
|
1332
|
+
{
|
|
1333
|
+
lock (typeof(ContextMapPoolBenchmarkScope))
|
|
1334
|
+
{
|
|
1335
|
+
if (_disposed)
|
|
1336
|
+
{
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
if (Environment.CurrentManagedThreadId != _ownerThreadId)
|
|
1341
|
+
{
|
|
1342
|
+
throw new InvalidOperationException(
|
|
1343
|
+
"Context-map benchmark scopes must be disposed on their owning thread."
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
if (!ReferenceEquals(ActiveContextMapPoolBenchmarkScope, this))
|
|
1347
|
+
{
|
|
1348
|
+
throw new InvalidOperationException(
|
|
1349
|
+
"Context-map benchmark scopes must be disposed in strict LIFO order."
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
bool effectiveUseLru = _isolated.UseLru;
|
|
1354
|
+
int effectiveMaxRetained = _isolated.MaxRetained;
|
|
1355
|
+
if (effectiveUseLru != _initialUseLru)
|
|
1356
|
+
{
|
|
1357
|
+
_savedPool.UseLru = effectiveUseLru;
|
|
1358
|
+
}
|
|
1359
|
+
if (effectiveMaxRetained != _initialMaxRetained)
|
|
1360
|
+
{
|
|
1361
|
+
_savedPool.MaxRetained = effectiveMaxRetained;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
_disposed = true;
|
|
1365
|
+
_ = _isolated.Trim(0);
|
|
1366
|
+
ContextHandlerByTargetDictsOverride = _savedOverride;
|
|
1367
|
+
ActiveContextMapPoolBenchmarkScope = _parent;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
internal readonly struct ContextMapPoolBenchmarkObservation
|
|
1373
|
+
{
|
|
1374
|
+
internal ContextMapPoolBenchmarkObservation(
|
|
1375
|
+
object identity,
|
|
1376
|
+
bool useLru,
|
|
1377
|
+
int maxRetained
|
|
1378
|
+
)
|
|
1379
|
+
{
|
|
1380
|
+
Identity = identity;
|
|
1381
|
+
UseLru = useLru;
|
|
1382
|
+
MaxRetained = maxRetained;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
internal object Identity { get; }
|
|
1386
|
+
|
|
1387
|
+
internal bool UseLru { get; }
|
|
1388
|
+
|
|
1389
|
+
internal int MaxRetained { get; }
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1000
1392
|
private void ApplyRuntimeSettings(DxMessagingRuntimeSettings settings)
|
|
1001
1393
|
{
|
|
1002
1394
|
if (settings == null)
|
|
@@ -1011,7 +1403,8 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1011
1403
|
{
|
|
1012
1404
|
IMessageBus.GlobalMessageBufferSize = Math.Max(0, settings.MessageBufferSize);
|
|
1013
1405
|
}
|
|
1014
|
-
|
|
1406
|
+
_emissionBufferCapacity = Math.Max(0, IMessageBus.GlobalMessageBufferSize);
|
|
1407
|
+
_emissionBufferBacking?.Resize(_emissionBufferCapacity);
|
|
1015
1408
|
_idleEvictionTicks = ComputeIdleEvictionTicks(settings.IdleEvictionSeconds);
|
|
1016
1409
|
_evictionTickIntervalSeconds = Math.Max(0d, settings.EvictionTickIntervalSeconds);
|
|
1017
1410
|
_idleEvictionEnabled = settings.EvictionEnabled;
|
|
@@ -1169,10 +1562,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1169
1562
|
private readonly List<MonoBehaviour> _componentCache = new();
|
|
1170
1563
|
#endif
|
|
1171
1564
|
|
|
1172
|
-
private
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1565
|
+
private RegistrationLog _log;
|
|
1566
|
+
private CyclicBuffer<MessageEmissionData> _emissionBufferBacking;
|
|
1567
|
+
private int _emissionBufferCapacity = GlobalMessageBufferSize;
|
|
1568
|
+
internal CyclicBuffer<MessageEmissionData> _emissionBuffer =>
|
|
1569
|
+
_emissionBufferBacking ??= new CyclicBuffer<MessageEmissionData>(
|
|
1570
|
+
_emissionBufferCapacity
|
|
1571
|
+
);
|
|
1176
1572
|
|
|
1177
1573
|
private bool _diagnosticsMode = ShouldEnableDiagnostics();
|
|
1178
1574
|
private bool _loggedReflexiveWarning;
|
|
@@ -1696,7 +2092,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1696
2092
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
1697
2093
|
private bool HasActiveDispatchSnapshot(DispatchState state)
|
|
1698
2094
|
{
|
|
1699
|
-
return _dispatchDepth > 0 && state != null &&
|
|
2095
|
+
return _dispatchDepth > 0 && state != null && state.active.IsInitialized;
|
|
1700
2096
|
}
|
|
1701
2097
|
|
|
1702
2098
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
@@ -1966,24 +2362,105 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1966
2362
|
return ContextHandlerByTargetDicts.Snapshot();
|
|
1967
2363
|
}
|
|
1968
2364
|
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
)
|
|
1972
|
-
where T : IMessage
|
|
2365
|
+
internal int SweepDirtyTargetSlotForBenchmark<TMessage>(InstanceId target)
|
|
2366
|
+
where TMessage : IMessage
|
|
1973
2367
|
{
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2368
|
+
int typeIndex = MessageHelperIndexer<TMessage>.SequentialId;
|
|
2369
|
+
int evicted = 0;
|
|
2370
|
+
for (int sinkIndex = 0; sinkIndex < _contextSinks.Length; ++sinkIndex)
|
|
2371
|
+
{
|
|
2372
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sink =
|
|
2373
|
+
_contextSinks[sinkIndex];
|
|
2374
|
+
if (
|
|
2375
|
+
sink == null
|
|
2376
|
+
|| !sink.TryGetValueAtIndex(
|
|
2377
|
+
typeIndex,
|
|
2378
|
+
out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
2379
|
+
)
|
|
2380
|
+
|| !handlersByTarget.TryGetValue(
|
|
2381
|
+
target,
|
|
2382
|
+
out HandlerCache<int, HandlerCache> handlers
|
|
2383
|
+
)
|
|
2384
|
+
|| handlers.handlers.Count != 0
|
|
2385
|
+
|| HasActiveDispatchSnapshot(handlers.dispatchState)
|
|
1977
2386
|
)
|
|
2387
|
+
{
|
|
2388
|
+
continue;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
handlers.Clear();
|
|
2392
|
+
_ = handlersByTarget.Remove(target);
|
|
2393
|
+
evicted++;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
if (
|
|
2397
|
+
_dirtyTargets.TryGetValue(typeIndex, out List<InstanceId> targets)
|
|
2398
|
+
&& _dirtyTargetSets.TryGetValue(typeIndex, out HashSet<InstanceId> targetSet)
|
|
2399
|
+
&& targetSet.Remove(target)
|
|
1978
2400
|
)
|
|
1979
2401
|
{
|
|
1980
|
-
|
|
2402
|
+
for (int index = targets.Count - 1; index >= 0; --index)
|
|
2403
|
+
{
|
|
2404
|
+
if (targets[index] != target)
|
|
2405
|
+
{
|
|
2406
|
+
continue;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
int lastIndex = targets.Count - 1;
|
|
2410
|
+
targets[index] = targets[lastIndex];
|
|
2411
|
+
targets.RemoveAt(lastIndex);
|
|
2412
|
+
break;
|
|
2413
|
+
}
|
|
1981
2414
|
}
|
|
1982
2415
|
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
return
|
|
2416
|
+
// Deliberately retain the empty per-type context map and dirty-candidate
|
|
2417
|
+
// collections. Returning and immediately renting them would make this row measure
|
|
2418
|
+
// shared pools in addition to the target map. ResetState returns them at teardown.
|
|
2419
|
+
return evicted;
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
internal bool TryObserveTargetedHandleMapStorageForBenchmark<TMessage>(
|
|
2423
|
+
out int entries,
|
|
2424
|
+
out int capacity
|
|
2425
|
+
)
|
|
2426
|
+
where TMessage : IMessage
|
|
2427
|
+
{
|
|
2428
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sink =
|
|
2429
|
+
_contextSinks[BusContextIndex.TargetedHandleDefault];
|
|
2430
|
+
if (
|
|
2431
|
+
!sink.TryGetValue<TMessage>(
|
|
2432
|
+
out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
2433
|
+
)
|
|
2434
|
+
)
|
|
2435
|
+
{
|
|
2436
|
+
entries = 0;
|
|
2437
|
+
capacity = 0;
|
|
2438
|
+
return false;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
entries = handlersByTarget.Count;
|
|
2442
|
+
capacity = handlersByTarget.EnsureCapacity(0);
|
|
2443
|
+
return true;
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
private Dictionary<InstanceId, HandlerCache<int, HandlerCache>> GetOrRentContextMap<T>(
|
|
2447
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sinks
|
|
2448
|
+
)
|
|
2449
|
+
where T : IMessage
|
|
2450
|
+
{
|
|
2451
|
+
if (
|
|
2452
|
+
sinks.TryGetValue<T>(
|
|
2453
|
+
out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> handlersByTarget
|
|
2454
|
+
)
|
|
2455
|
+
)
|
|
2456
|
+
{
|
|
2457
|
+
return handlersByTarget;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
handlersByTarget = ContextHandlerByTargetDicts.Rent();
|
|
2461
|
+
_contextMapHighWaterCounts[handlersByTarget] = handlersByTarget.Count;
|
|
2462
|
+
sinks.Set<T>(handlersByTarget);
|
|
2463
|
+
return handlersByTarget;
|
|
1987
2464
|
}
|
|
1988
2465
|
|
|
1989
2466
|
private void RemoveAndReturnContextMap(
|
|
@@ -2130,7 +2607,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2130
2607
|
{
|
|
2131
2608
|
DispatchSnapshot displaced = snapshot;
|
|
2132
2609
|
snapshot = DispatchSnapshot.Empty;
|
|
2133
|
-
if (displaced == null || displaced.
|
|
2610
|
+
if (displaced == null || !displaced.IsInitialized)
|
|
2134
2611
|
{
|
|
2135
2612
|
return;
|
|
2136
2613
|
}
|
|
@@ -2474,11 +2951,10 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2474
2951
|
_componentCache.Clear();
|
|
2475
2952
|
#endif
|
|
2476
2953
|
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
_emissionBuffer.Clear();
|
|
2954
|
+
_log?.Clear();
|
|
2955
|
+
_emissionBufferCapacity = GlobalMessageBufferSize;
|
|
2956
|
+
_emissionBufferBacking?.Resize(_emissionBufferCapacity);
|
|
2957
|
+
_emissionBufferBacking?.Clear();
|
|
2482
2958
|
}
|
|
2483
2959
|
|
|
2484
2960
|
private void ResetTypedSlotsForReferencedHandlers()
|
|
@@ -2562,7 +3038,10 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2562
3038
|
}
|
|
2563
3039
|
|
|
2564
3040
|
/// <inheritdoc />
|
|
2565
|
-
public
|
|
3041
|
+
public MessageBusRegistration RegisterUntargeted<T>(
|
|
3042
|
+
MessageHandler messageHandler,
|
|
3043
|
+
int priority = 0
|
|
3044
|
+
)
|
|
2566
3045
|
where T : IUntargetedMessage
|
|
2567
3046
|
{
|
|
2568
3047
|
EnsureAotUntargetedBridge<T>();
|
|
@@ -2575,7 +3054,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2575
3054
|
}
|
|
2576
3055
|
|
|
2577
3056
|
/// <inheritdoc />
|
|
2578
|
-
public
|
|
3057
|
+
public MessageBusRegistration RegisterTargeted<T>(
|
|
2579
3058
|
InstanceId target,
|
|
2580
3059
|
MessageHandler messageHandler,
|
|
2581
3060
|
int priority = 0
|
|
@@ -2593,7 +3072,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2593
3072
|
}
|
|
2594
3073
|
|
|
2595
3074
|
/// <inheritdoc />
|
|
2596
|
-
public
|
|
3075
|
+
public MessageBusRegistration RegisterSourcedBroadcast<T>(
|
|
2597
3076
|
InstanceId source,
|
|
2598
3077
|
MessageHandler messageHandler,
|
|
2599
3078
|
int priority = 0
|
|
@@ -2611,7 +3090,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2611
3090
|
}
|
|
2612
3091
|
|
|
2613
3092
|
/// <inheritdoc />
|
|
2614
|
-
public
|
|
3093
|
+
public MessageBusRegistration RegisterSourcedBroadcastWithoutSource<T>(
|
|
2615
3094
|
MessageHandler messageHandler,
|
|
2616
3095
|
int priority = 0
|
|
2617
3096
|
)
|
|
@@ -2627,7 +3106,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2627
3106
|
}
|
|
2628
3107
|
|
|
2629
3108
|
/// <inheritdoc />
|
|
2630
|
-
public
|
|
3109
|
+
public MessageBusRegistration RegisterTargetedWithoutTargeting<T>(
|
|
2631
3110
|
MessageHandler messageHandler,
|
|
2632
3111
|
int priority = 0
|
|
2633
3112
|
)
|
|
@@ -2643,7 +3122,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2643
3122
|
}
|
|
2644
3123
|
|
|
2645
3124
|
/// <inheritdoc />
|
|
2646
|
-
public
|
|
3125
|
+
public MessageBusRegistration RegisterGlobalAcceptAll(MessageHandler messageHandler)
|
|
2647
3126
|
{
|
|
2648
3127
|
long touchTick = AdvanceTick();
|
|
2649
3128
|
InvalidateDispatchPlans();
|
|
@@ -2661,7 +3140,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2661
3140
|
{
|
|
2662
3141
|
_globalSlots.liveCount++;
|
|
2663
3142
|
}
|
|
2664
|
-
_log
|
|
3143
|
+
_log?.Log(
|
|
2665
3144
|
new MessagingRegistration(
|
|
2666
3145
|
messageHandler.owner,
|
|
2667
3146
|
type,
|
|
@@ -2689,88 +3168,107 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2689
3168
|
|
|
2690
3169
|
long capturedGeneration = _resetGeneration;
|
|
2691
3170
|
long capturedSweepGeneration = _globalSlotSweepGeneration;
|
|
2692
|
-
return (
|
|
3171
|
+
return new MessageBusRegistration(
|
|
3172
|
+
MessageBusRegistration.Kind.GlobalAcceptAll,
|
|
3173
|
+
RegistrationMethod.GlobalAcceptAll,
|
|
3174
|
+
0,
|
|
3175
|
+
capturedGeneration,
|
|
3176
|
+
capturedSweepGeneration,
|
|
3177
|
+
touchTick,
|
|
3178
|
+
null,
|
|
3179
|
+
null,
|
|
3180
|
+
messageHandler,
|
|
3181
|
+
default
|
|
3182
|
+
);
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
/// <summary>
|
|
3186
|
+
/// Re-expression of the former <see cref="RegisterGlobalAcceptAll"/> closure body: the
|
|
3187
|
+
/// global accept-all deregistration. Behaviour and both generation guards are unchanged.
|
|
3188
|
+
/// </summary>
|
|
3189
|
+
private void DeregisterGlobalAcceptAll(in MessageBusRegistration reg)
|
|
3190
|
+
{
|
|
3191
|
+
// Generation guard: see DeregisterScalarHandler. The global slot guards on BOTH the
|
|
3192
|
+
// reset generation and the global-slot sweep generation.
|
|
3193
|
+
if (
|
|
3194
|
+
reg.generation != _resetGeneration
|
|
3195
|
+
|| reg.sweepGeneration != _globalSlotSweepGeneration
|
|
3196
|
+
)
|
|
2693
3197
|
{
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
MessageHandler messageHandler = (MessageHandler)reg.payload;
|
|
3202
|
+
Type type = typeof(IMessage);
|
|
3203
|
+
|
|
3204
|
+
long deregisterTouchTick = AdvanceTick();
|
|
3205
|
+
InvalidateDispatchPlans();
|
|
3206
|
+
_globalSlots.version++;
|
|
3207
|
+
_log?.Log(
|
|
3208
|
+
new MessagingRegistration(
|
|
3209
|
+
messageHandler.owner,
|
|
3210
|
+
type,
|
|
3211
|
+
RegistrationType.Deregister,
|
|
3212
|
+
RegistrationMethod.GlobalAcceptAll
|
|
2699
3213
|
)
|
|
3214
|
+
);
|
|
3215
|
+
if (!_globalSlots.sharedHandlers.TryGetValue(messageHandler, out int count))
|
|
3216
|
+
{
|
|
3217
|
+
if (MessagingDebug.enabled)
|
|
2700
3218
|
{
|
|
2701
|
-
|
|
3219
|
+
MessagingDebug.Log(
|
|
3220
|
+
LogLevel.Error,
|
|
3221
|
+
"Received over-deregistration of GlobalAcceptAll for MessageHandler {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
3222
|
+
messageHandler
|
|
3223
|
+
);
|
|
2702
3224
|
}
|
|
2703
3225
|
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
_globalSlots.version++;
|
|
2707
|
-
_log.Log(
|
|
2708
|
-
new MessagingRegistration(
|
|
2709
|
-
messageHandler.owner,
|
|
2710
|
-
type,
|
|
2711
|
-
RegistrationType.Deregister,
|
|
2712
|
-
RegistrationMethod.GlobalAcceptAll
|
|
2713
|
-
)
|
|
2714
|
-
);
|
|
2715
|
-
if (!_globalSlots.sharedHandlers.TryGetValue(messageHandler, out count))
|
|
2716
|
-
{
|
|
2717
|
-
if (MessagingDebug.enabled)
|
|
2718
|
-
{
|
|
2719
|
-
MessagingDebug.Log(
|
|
2720
|
-
LogLevel.Error,
|
|
2721
|
-
"Received over-deregistration of GlobalAcceptAll for MessageHandler {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
2722
|
-
messageHandler
|
|
2723
|
-
);
|
|
2724
|
-
}
|
|
2725
|
-
|
|
2726
|
-
return;
|
|
2727
|
-
}
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
2728
3228
|
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
}
|
|
3229
|
+
_globalSlots.lastTouchTicks = deregisterTouchTick;
|
|
3230
|
+
if (count <= 1)
|
|
3231
|
+
{
|
|
3232
|
+
_ = _globalSlots.sharedHandlers.Remove(messageHandler);
|
|
3233
|
+
MarkDirtyHandler(messageHandler);
|
|
3234
|
+
_globalSlotSweepCandidate = true;
|
|
3235
|
+
// Final-removal of this handler from sharedHandlers is the 1 -> 0 transition that
|
|
3236
|
+
// mirrors back into liveCount. Partial deregistration (count > 1) leaves liveCount
|
|
3237
|
+
// alone -- the dictionary entry is still present.
|
|
3238
|
+
_globalSlots.liveCount--;
|
|
3239
|
+
}
|
|
3240
|
+
else
|
|
3241
|
+
{
|
|
3242
|
+
_globalSlots.sharedHandlers[messageHandler] = count - 1;
|
|
3243
|
+
}
|
|
2745
3244
|
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
};
|
|
3245
|
+
StageGlobalDispatchSnapshot<IUntargetedMessage>(
|
|
3246
|
+
this,
|
|
3247
|
+
_globalSlots,
|
|
3248
|
+
DispatchKind.Untargeted
|
|
3249
|
+
);
|
|
3250
|
+
StageGlobalDispatchSnapshot<ITargetedMessage>(
|
|
3251
|
+
this,
|
|
3252
|
+
_globalSlots,
|
|
3253
|
+
DispatchKind.Targeted
|
|
3254
|
+
);
|
|
3255
|
+
StageGlobalDispatchSnapshot<IBroadcastMessage>(
|
|
3256
|
+
this,
|
|
3257
|
+
_globalSlots,
|
|
3258
|
+
DispatchKind.Broadcast
|
|
3259
|
+
);
|
|
3260
|
+
DebugAssertGlobalLiveCount();
|
|
2763
3261
|
}
|
|
2764
3262
|
|
|
2765
3263
|
/// <inheritdoc />
|
|
2766
|
-
public
|
|
3264
|
+
public MessageBusRegistration RegisterUntargetedInterceptor<T>(
|
|
2767
3265
|
UntargetedInterceptor<T> interceptor,
|
|
2768
3266
|
int priority = 0
|
|
2769
3267
|
)
|
|
2770
3268
|
where T : IUntargetedMessage
|
|
2771
3269
|
{
|
|
2772
3270
|
EnsureAotUntargetedBridge<T>();
|
|
2773
|
-
|
|
3271
|
+
long touchTick = AdvanceTick();
|
|
2774
3272
|
InvalidateDispatchPlans();
|
|
2775
3273
|
InterceptorCache<object> prioritizedInterceptors =
|
|
2776
3274
|
_untargetedInterceptsByType.GetOrAdd<T>();
|
|
@@ -2809,7 +3307,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2809
3307
|
priorityCount[priority] = count + 1;
|
|
2810
3308
|
|
|
2811
3309
|
Type type = typeof(T);
|
|
2812
|
-
_log
|
|
3310
|
+
_log?.Log(
|
|
2813
3311
|
new MessagingRegistration(
|
|
2814
3312
|
InstanceId.EmptyId,
|
|
2815
3313
|
type,
|
|
@@ -2819,107 +3317,29 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2819
3317
|
);
|
|
2820
3318
|
|
|
2821
3319
|
long capturedGeneration = _resetGeneration;
|
|
2822
|
-
return (
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
)
|
|
2835
|
-
)
|
|
2836
|
-
{
|
|
2837
|
-
return;
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
_ = AdvanceTick();
|
|
2841
|
-
InvalidateDispatchPlans();
|
|
2842
|
-
prioritizedInterceptors.lastTouchTicks = _tickCounter;
|
|
2843
|
-
MarkDirtyType<T>();
|
|
2844
|
-
_log.Log(
|
|
2845
|
-
new MessagingRegistration(
|
|
2846
|
-
InstanceId.EmptyId,
|
|
2847
|
-
type,
|
|
2848
|
-
RegistrationType.Deregister,
|
|
2849
|
-
RegistrationMethod.Interceptor
|
|
2850
|
-
)
|
|
2851
|
-
);
|
|
2852
|
-
bool removed = false;
|
|
2853
|
-
if (_uniqueInterceptorsAndPriorities.TryGetValue(interceptor, out priorityCount))
|
|
2854
|
-
{
|
|
2855
|
-
if (priorityCount.TryGetValue(priority, out count))
|
|
2856
|
-
{
|
|
2857
|
-
if (1 < count)
|
|
2858
|
-
{
|
|
2859
|
-
priorityCount[priority] = count - 1;
|
|
2860
|
-
}
|
|
2861
|
-
else
|
|
2862
|
-
{
|
|
2863
|
-
removed = true;
|
|
2864
|
-
_ = priorityCount.Remove(priority);
|
|
2865
|
-
}
|
|
2866
|
-
}
|
|
2867
|
-
|
|
2868
|
-
if (priorityCount.Count == 0)
|
|
2869
|
-
{
|
|
2870
|
-
_uniqueInterceptorsAndPriorities.Remove(interceptor);
|
|
2871
|
-
}
|
|
2872
|
-
}
|
|
2873
|
-
else if (MessagingDebug.enabled)
|
|
2874
|
-
{
|
|
2875
|
-
MessagingDebug.Log(
|
|
2876
|
-
LogLevel.Error,
|
|
2877
|
-
"Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
2878
|
-
interceptor
|
|
2879
|
-
);
|
|
2880
|
-
}
|
|
2881
|
-
|
|
2882
|
-
bool complete = false;
|
|
2883
|
-
if (removed)
|
|
2884
|
-
{
|
|
2885
|
-
if (_untargetedInterceptsByType.TryGetValue<T>(out prioritizedInterceptors))
|
|
2886
|
-
{
|
|
2887
|
-
if (
|
|
2888
|
-
prioritizedInterceptors.handlers.TryGetValue(
|
|
2889
|
-
priority,
|
|
2890
|
-
out List<object> interceptors
|
|
2891
|
-
)
|
|
2892
|
-
)
|
|
2893
|
-
{
|
|
2894
|
-
complete = interceptors.Remove(interceptor);
|
|
2895
|
-
if (interceptors.Count == 0)
|
|
2896
|
-
{
|
|
2897
|
-
_ = prioritizedInterceptors.handlers.Remove(priority);
|
|
2898
|
-
}
|
|
2899
|
-
}
|
|
2900
|
-
}
|
|
2901
|
-
|
|
2902
|
-
if (!complete && MessagingDebug.enabled)
|
|
2903
|
-
{
|
|
2904
|
-
MessagingDebug.Log(
|
|
2905
|
-
LogLevel.Error,
|
|
2906
|
-
"Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
2907
|
-
interceptor
|
|
2908
|
-
);
|
|
2909
|
-
}
|
|
2910
|
-
}
|
|
2911
|
-
};
|
|
3320
|
+
return new MessageBusRegistration(
|
|
3321
|
+
MessageBusRegistration.Kind.UntargetedInterceptor,
|
|
3322
|
+
RegistrationMethod.Interceptor,
|
|
3323
|
+
priority,
|
|
3324
|
+
capturedGeneration,
|
|
3325
|
+
0L,
|
|
3326
|
+
touchTick,
|
|
3327
|
+
capturedInterceptors,
|
|
3328
|
+
null,
|
|
3329
|
+
interceptor,
|
|
3330
|
+
default
|
|
3331
|
+
);
|
|
2912
3332
|
}
|
|
2913
3333
|
|
|
2914
3334
|
/// <inheritdoc />
|
|
2915
|
-
public
|
|
3335
|
+
public MessageBusRegistration RegisterTargetedInterceptor<T>(
|
|
2916
3336
|
TargetedInterceptor<T> interceptor,
|
|
2917
3337
|
int priority = 0
|
|
2918
3338
|
)
|
|
2919
3339
|
where T : ITargetedMessage
|
|
2920
3340
|
{
|
|
2921
3341
|
EnsureAotTargetedBridge<T>();
|
|
2922
|
-
|
|
3342
|
+
long touchTick = AdvanceTick();
|
|
2923
3343
|
InvalidateDispatchPlans();
|
|
2924
3344
|
InterceptorCache<object> prioritizedInterceptors =
|
|
2925
3345
|
_targetedInterceptsByType.GetOrAdd<T>();
|
|
@@ -2958,7 +3378,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2958
3378
|
priorityCount[priority] = count + 1;
|
|
2959
3379
|
|
|
2960
3380
|
Type type = typeof(T);
|
|
2961
|
-
_log
|
|
3381
|
+
_log?.Log(
|
|
2962
3382
|
new MessagingRegistration(
|
|
2963
3383
|
InstanceId.EmptyId,
|
|
2964
3384
|
type,
|
|
@@ -2968,107 +3388,29 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2968
3388
|
);
|
|
2969
3389
|
|
|
2970
3390
|
long capturedGeneration = _resetGeneration;
|
|
2971
|
-
return (
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
)
|
|
2984
|
-
)
|
|
2985
|
-
{
|
|
2986
|
-
return;
|
|
2987
|
-
}
|
|
2988
|
-
|
|
2989
|
-
_ = AdvanceTick();
|
|
2990
|
-
InvalidateDispatchPlans();
|
|
2991
|
-
prioritizedInterceptors.lastTouchTicks = _tickCounter;
|
|
2992
|
-
MarkDirtyType<T>();
|
|
2993
|
-
_log.Log(
|
|
2994
|
-
new MessagingRegistration(
|
|
2995
|
-
InstanceId.EmptyId,
|
|
2996
|
-
type,
|
|
2997
|
-
RegistrationType.Deregister,
|
|
2998
|
-
RegistrationMethod.Interceptor
|
|
2999
|
-
)
|
|
3000
|
-
);
|
|
3001
|
-
bool removed = false;
|
|
3002
|
-
if (_uniqueInterceptorsAndPriorities.TryGetValue(interceptor, out priorityCount))
|
|
3003
|
-
{
|
|
3004
|
-
if (priorityCount.TryGetValue(priority, out count))
|
|
3005
|
-
{
|
|
3006
|
-
if (1 < count)
|
|
3007
|
-
{
|
|
3008
|
-
priorityCount[priority] = count - 1;
|
|
3009
|
-
}
|
|
3010
|
-
else
|
|
3011
|
-
{
|
|
3012
|
-
removed = true;
|
|
3013
|
-
_ = priorityCount.Remove(priority);
|
|
3014
|
-
}
|
|
3015
|
-
}
|
|
3016
|
-
|
|
3017
|
-
if (priorityCount.Count == 0)
|
|
3018
|
-
{
|
|
3019
|
-
_uniqueInterceptorsAndPriorities.Remove(interceptor);
|
|
3020
|
-
}
|
|
3021
|
-
}
|
|
3022
|
-
else if (MessagingDebug.enabled)
|
|
3023
|
-
{
|
|
3024
|
-
MessagingDebug.Log(
|
|
3025
|
-
LogLevel.Error,
|
|
3026
|
-
"Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
3027
|
-
interceptor
|
|
3028
|
-
);
|
|
3029
|
-
}
|
|
3030
|
-
|
|
3031
|
-
bool complete = false;
|
|
3032
|
-
if (removed)
|
|
3033
|
-
{
|
|
3034
|
-
if (_targetedInterceptsByType.TryGetValue<T>(out prioritizedInterceptors))
|
|
3035
|
-
{
|
|
3036
|
-
if (
|
|
3037
|
-
prioritizedInterceptors.handlers.TryGetValue(
|
|
3038
|
-
priority,
|
|
3039
|
-
out List<object> interceptors
|
|
3040
|
-
)
|
|
3041
|
-
)
|
|
3042
|
-
{
|
|
3043
|
-
complete = interceptors.Remove(interceptor);
|
|
3044
|
-
if (interceptors.Count == 0)
|
|
3045
|
-
{
|
|
3046
|
-
_ = prioritizedInterceptors.handlers.Remove(priority);
|
|
3047
|
-
}
|
|
3048
|
-
}
|
|
3049
|
-
}
|
|
3050
|
-
|
|
3051
|
-
if (!complete && MessagingDebug.enabled)
|
|
3052
|
-
{
|
|
3053
|
-
MessagingDebug.Log(
|
|
3054
|
-
LogLevel.Error,
|
|
3055
|
-
"Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
3056
|
-
interceptor
|
|
3057
|
-
);
|
|
3058
|
-
}
|
|
3059
|
-
}
|
|
3060
|
-
};
|
|
3391
|
+
return new MessageBusRegistration(
|
|
3392
|
+
MessageBusRegistration.Kind.TargetedInterceptor,
|
|
3393
|
+
RegistrationMethod.Interceptor,
|
|
3394
|
+
priority,
|
|
3395
|
+
capturedGeneration,
|
|
3396
|
+
0L,
|
|
3397
|
+
touchTick,
|
|
3398
|
+
capturedInterceptors,
|
|
3399
|
+
null,
|
|
3400
|
+
interceptor,
|
|
3401
|
+
default
|
|
3402
|
+
);
|
|
3061
3403
|
}
|
|
3062
3404
|
|
|
3063
3405
|
/// <inheritdoc />
|
|
3064
|
-
public
|
|
3406
|
+
public MessageBusRegistration RegisterBroadcastInterceptor<T>(
|
|
3065
3407
|
BroadcastInterceptor<T> interceptor,
|
|
3066
3408
|
int priority = 0
|
|
3067
3409
|
)
|
|
3068
3410
|
where T : IBroadcastMessage
|
|
3069
3411
|
{
|
|
3070
3412
|
EnsureAotSourcedBridge<T>();
|
|
3071
|
-
|
|
3413
|
+
long touchTick = AdvanceTick();
|
|
3072
3414
|
InvalidateDispatchPlans();
|
|
3073
3415
|
InterceptorCache<object> prioritizedInterceptors =
|
|
3074
3416
|
_broadcastInterceptsByType.GetOrAdd<T>();
|
|
@@ -3107,7 +3449,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3107
3449
|
priorityCount[priority] = count + 1;
|
|
3108
3450
|
|
|
3109
3451
|
Type type = typeof(T);
|
|
3110
|
-
_log
|
|
3452
|
+
_log?.Log(
|
|
3111
3453
|
new MessagingRegistration(
|
|
3112
3454
|
InstanceId.EmptyId,
|
|
3113
3455
|
type,
|
|
@@ -3117,96 +3459,18 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3117
3459
|
);
|
|
3118
3460
|
|
|
3119
3461
|
long capturedGeneration = _resetGeneration;
|
|
3120
|
-
return (
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
)
|
|
3133
|
-
)
|
|
3134
|
-
{
|
|
3135
|
-
return;
|
|
3136
|
-
}
|
|
3137
|
-
|
|
3138
|
-
_ = AdvanceTick();
|
|
3139
|
-
InvalidateDispatchPlans();
|
|
3140
|
-
prioritizedInterceptors.lastTouchTicks = _tickCounter;
|
|
3141
|
-
MarkDirtyType<T>();
|
|
3142
|
-
_log.Log(
|
|
3143
|
-
new MessagingRegistration(
|
|
3144
|
-
InstanceId.EmptyId,
|
|
3145
|
-
type,
|
|
3146
|
-
RegistrationType.Deregister,
|
|
3147
|
-
RegistrationMethod.Interceptor
|
|
3148
|
-
)
|
|
3149
|
-
);
|
|
3150
|
-
bool removed = false;
|
|
3151
|
-
if (_uniqueInterceptorsAndPriorities.TryGetValue(interceptor, out priorityCount))
|
|
3152
|
-
{
|
|
3153
|
-
if (priorityCount.TryGetValue(priority, out count))
|
|
3154
|
-
{
|
|
3155
|
-
if (1 < count)
|
|
3156
|
-
{
|
|
3157
|
-
priorityCount[priority] = count - 1;
|
|
3158
|
-
}
|
|
3159
|
-
else
|
|
3160
|
-
{
|
|
3161
|
-
removed = true;
|
|
3162
|
-
_ = priorityCount.Remove(priority);
|
|
3163
|
-
}
|
|
3164
|
-
}
|
|
3165
|
-
|
|
3166
|
-
if (priorityCount.Count == 0)
|
|
3167
|
-
{
|
|
3168
|
-
_uniqueInterceptorsAndPriorities.Remove(interceptor);
|
|
3169
|
-
}
|
|
3170
|
-
}
|
|
3171
|
-
else if (MessagingDebug.enabled)
|
|
3172
|
-
{
|
|
3173
|
-
MessagingDebug.Log(
|
|
3174
|
-
LogLevel.Error,
|
|
3175
|
-
"Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
3176
|
-
interceptor
|
|
3177
|
-
);
|
|
3178
|
-
}
|
|
3179
|
-
|
|
3180
|
-
bool complete = false;
|
|
3181
|
-
if (removed)
|
|
3182
|
-
{
|
|
3183
|
-
if (_broadcastInterceptsByType.TryGetValue<T>(out prioritizedInterceptors))
|
|
3184
|
-
{
|
|
3185
|
-
if (
|
|
3186
|
-
prioritizedInterceptors.handlers.TryGetValue(
|
|
3187
|
-
priority,
|
|
3188
|
-
out List<object> interceptors
|
|
3189
|
-
)
|
|
3190
|
-
)
|
|
3191
|
-
{
|
|
3192
|
-
complete = interceptors.Remove(interceptor);
|
|
3193
|
-
if (interceptors.Count == 0)
|
|
3194
|
-
{
|
|
3195
|
-
_ = prioritizedInterceptors.handlers.Remove(priority);
|
|
3196
|
-
}
|
|
3197
|
-
}
|
|
3198
|
-
}
|
|
3199
|
-
|
|
3200
|
-
if (!complete && MessagingDebug.enabled)
|
|
3201
|
-
{
|
|
3202
|
-
MessagingDebug.Log(
|
|
3203
|
-
LogLevel.Error,
|
|
3204
|
-
"Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
3205
|
-
interceptor
|
|
3206
|
-
);
|
|
3207
|
-
}
|
|
3208
|
-
}
|
|
3209
|
-
};
|
|
3462
|
+
return new MessageBusRegistration(
|
|
3463
|
+
MessageBusRegistration.Kind.BroadcastInterceptor,
|
|
3464
|
+
RegistrationMethod.Interceptor,
|
|
3465
|
+
priority,
|
|
3466
|
+
capturedGeneration,
|
|
3467
|
+
0L,
|
|
3468
|
+
touchTick,
|
|
3469
|
+
capturedInterceptors,
|
|
3470
|
+
null,
|
|
3471
|
+
interceptor,
|
|
3472
|
+
default
|
|
3473
|
+
);
|
|
3210
3474
|
}
|
|
3211
3475
|
|
|
3212
3476
|
private bool IsStaleInterceptorDeregisterAfterSweep<T>(
|
|
@@ -3221,7 +3485,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3221
3485
|
}
|
|
3222
3486
|
|
|
3223
3487
|
/// <inheritdoc />
|
|
3224
|
-
public
|
|
3488
|
+
public MessageBusRegistration RegisterUntargetedPostProcessor<T>(
|
|
3225
3489
|
MessageHandler messageHandler,
|
|
3226
3490
|
int priority = 0
|
|
3227
3491
|
)
|
|
@@ -3237,7 +3501,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3237
3501
|
}
|
|
3238
3502
|
|
|
3239
3503
|
/// <inheritdoc />
|
|
3240
|
-
public
|
|
3504
|
+
public MessageBusRegistration RegisterTargetedPostProcessor<T>(
|
|
3241
3505
|
InstanceId target,
|
|
3242
3506
|
MessageHandler messageHandler,
|
|
3243
3507
|
int priority = 0
|
|
@@ -3255,7 +3519,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3255
3519
|
}
|
|
3256
3520
|
|
|
3257
3521
|
/// <inheritdoc />
|
|
3258
|
-
public
|
|
3522
|
+
public MessageBusRegistration RegisterTargetedWithoutTargetingPostProcessor<T>(
|
|
3259
3523
|
MessageHandler messageHandler,
|
|
3260
3524
|
int priority = 0
|
|
3261
3525
|
)
|
|
@@ -3271,7 +3535,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3271
3535
|
}
|
|
3272
3536
|
|
|
3273
3537
|
/// <inheritdoc />
|
|
3274
|
-
public
|
|
3538
|
+
public MessageBusRegistration RegisterBroadcastPostProcessor<T>(
|
|
3275
3539
|
InstanceId source,
|
|
3276
3540
|
MessageHandler messageHandler,
|
|
3277
3541
|
int priority = 0
|
|
@@ -3289,7 +3553,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3289
3553
|
}
|
|
3290
3554
|
|
|
3291
3555
|
/// <inheritdoc />
|
|
3292
|
-
public
|
|
3556
|
+
public MessageBusRegistration RegisterBroadcastWithoutSourcePostProcessor<T>(
|
|
3293
3557
|
MessageHandler messageHandler,
|
|
3294
3558
|
int priority = 0
|
|
3295
3559
|
)
|
|
@@ -3382,7 +3646,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3382
3646
|
long touchTick = AdvanceTick();
|
|
3383
3647
|
if (_diagnosticsMode)
|
|
3384
3648
|
{
|
|
3385
|
-
_emissionBuffer.Add(new MessageEmissionData(typedMessage));
|
|
3649
|
+
_emissionBuffer.Add(new MessageEmissionData(typedMessage, emissionId));
|
|
3386
3650
|
}
|
|
3387
3651
|
|
|
3388
3652
|
if (plan.fastPath)
|
|
@@ -3517,7 +3781,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3517
3781
|
}
|
|
3518
3782
|
|
|
3519
3783
|
Touch(sortedHandlers, touchTick);
|
|
3520
|
-
DispatchSnapshot snapshot = prefrozenSnapshot.
|
|
3784
|
+
DispatchSnapshot snapshot = !prefrozenSnapshot.IsInitialized
|
|
3521
3785
|
? AcquireDispatchSnapshotFast<TMessage>(
|
|
3522
3786
|
this,
|
|
3523
3787
|
sortedHandlers,
|
|
@@ -3624,7 +3888,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3624
3888
|
long touchTick = AdvanceTick();
|
|
3625
3889
|
if (_diagnosticsMode)
|
|
3626
3890
|
{
|
|
3627
|
-
_emissionBuffer.Add(new MessageEmissionData(typedMessage, target));
|
|
3891
|
+
_emissionBuffer.Add(new MessageEmissionData(typedMessage, target, emissionId));
|
|
3628
3892
|
}
|
|
3629
3893
|
|
|
3630
3894
|
// Fast lane: no interceptors, no global accept-all, no
|
|
@@ -4105,7 +4369,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4105
4369
|
target
|
|
4106
4370
|
);
|
|
4107
4371
|
}
|
|
4108
|
-
else if (targetedPostSnapshot.
|
|
4372
|
+
else if (!targetedPostSnapshot.IsInitialized)
|
|
4109
4373
|
{
|
|
4110
4374
|
snapshot = AcquireDispatchSnapshotFast<TMessage>(
|
|
4111
4375
|
this,
|
|
@@ -4131,7 +4395,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4131
4395
|
&& postTwt.handlers.Count > 0
|
|
4132
4396
|
)
|
|
4133
4397
|
{
|
|
4134
|
-
DispatchSnapshot snapshot = targetedWithoutTargetingPostSnapshot.
|
|
4398
|
+
DispatchSnapshot snapshot = !targetedWithoutTargetingPostSnapshot.IsInitialized
|
|
4135
4399
|
? AcquireDispatchSnapshotFast<TMessage>(
|
|
4136
4400
|
this,
|
|
4137
4401
|
postTwt,
|
|
@@ -4253,7 +4517,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4253
4517
|
long touchTick = AdvanceTick();
|
|
4254
4518
|
if (_diagnosticsMode)
|
|
4255
4519
|
{
|
|
4256
|
-
_emissionBuffer.Add(new MessageEmissionData(typedMessage, source));
|
|
4520
|
+
_emissionBuffer.Add(new MessageEmissionData(typedMessage, source, emissionId));
|
|
4257
4521
|
}
|
|
4258
4522
|
|
|
4259
4523
|
// Fast lane: no interceptors, no global accept-all, no
|
|
@@ -4510,15 +4774,15 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4510
4774
|
}
|
|
4511
4775
|
}
|
|
4512
4776
|
|
|
4513
|
-
if (
|
|
4777
|
+
if (broadcastPostSnapshot.IsInitialized)
|
|
4514
4778
|
{
|
|
4515
|
-
// Legacy reporting:
|
|
4516
|
-
// "found" even
|
|
4779
|
+
// Legacy reporting: an initialized pre-frozen snapshot counts as
|
|
4780
|
+
// "found" even when it owns zero resolved delegates.
|
|
4517
4781
|
foundAnyHandlers = true;
|
|
4518
4782
|
_ = DispatchFlatSnapshot(broadcastPostSnapshot, ref typedMessage);
|
|
4519
4783
|
}
|
|
4520
4784
|
|
|
4521
|
-
if (
|
|
4785
|
+
if (broadcastWithoutSourcePostSnapshot.IsInitialized)
|
|
4522
4786
|
{
|
|
4523
4787
|
if (
|
|
4524
4788
|
DispatchContextFlatSnapshot(
|
|
@@ -5107,7 +5371,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5107
5371
|
// Empty), acquire lazily here: the legacy path took its first
|
|
5108
5372
|
// freeze at this point in that case, so a registration made
|
|
5109
5373
|
// earlier in this emission into a previously-empty sink fires.
|
|
5110
|
-
DispatchSnapshot snapshot = prefrozenSnapshot.
|
|
5374
|
+
DispatchSnapshot snapshot = !prefrozenSnapshot.IsInitialized
|
|
5111
5375
|
? AcquireDispatchSnapshotFast<TMessage>(
|
|
5112
5376
|
this,
|
|
5113
5377
|
sortedHandlers,
|
|
@@ -5122,7 +5386,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5122
5386
|
return true;
|
|
5123
5387
|
}
|
|
5124
5388
|
|
|
5125
|
-
private
|
|
5389
|
+
private MessageBusRegistration InternalRegisterUntargeted<T>(
|
|
5126
5390
|
MessageHandler messageHandler,
|
|
5127
5391
|
MessageCache<HandlerCache<int, HandlerCache>> sinks,
|
|
5128
5392
|
RegistrationMethod registrationMethod,
|
|
@@ -5172,7 +5436,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5172
5436
|
}
|
|
5173
5437
|
StageDispatchSnapshot<T>(this, capturedHandlers, slotKey);
|
|
5174
5438
|
Type type = typeof(T);
|
|
5175
|
-
_log
|
|
5439
|
+
_log?.Log(
|
|
5176
5440
|
new MessagingRegistration(
|
|
5177
5441
|
handlerOwnerId,
|
|
5178
5442
|
type,
|
|
@@ -5182,49 +5446,66 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5182
5446
|
);
|
|
5183
5447
|
|
|
5184
5448
|
long capturedGeneration = _resetGeneration;
|
|
5185
|
-
return (
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5449
|
+
return new MessageBusRegistration(
|
|
5450
|
+
MessageBusRegistration.Kind.Handler,
|
|
5451
|
+
registrationMethod,
|
|
5452
|
+
priority,
|
|
5453
|
+
capturedGeneration,
|
|
5454
|
+
0L,
|
|
5455
|
+
touchTick,
|
|
5456
|
+
capturedHandlers,
|
|
5457
|
+
null,
|
|
5458
|
+
messageHandler,
|
|
5459
|
+
default
|
|
5460
|
+
);
|
|
5461
|
+
}
|
|
5195
5462
|
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
}
|
|
5463
|
+
/// <summary>
|
|
5464
|
+
/// Re-expression of the former <see cref="InternalRegisterUntargeted{T}"/> closure body:
|
|
5465
|
+
/// the scalar (untargeted / without-context) handler deregistration. Behaviour and the
|
|
5466
|
+
/// four reentrancy invariants are unchanged; the closure's captured locals are read from
|
|
5467
|
+
/// <paramref name="reg"/>'s fields instead, and the sink is re-resolved from the method.
|
|
5468
|
+
/// </summary>
|
|
5469
|
+
private void DeregisterScalarHandler<T>(in MessageBusRegistration reg)
|
|
5470
|
+
where T : IMessage
|
|
5471
|
+
{
|
|
5472
|
+
// Generation guard: if ResetState() ran after this handle was captured (e.g. a
|
|
5473
|
+
// deferred Object.Destroy fires after a domain-reload-style reset), silently no-op
|
|
5474
|
+
// rather than logging a misleading over-deregistration error.
|
|
5475
|
+
if (reg.generation != _resetGeneration)
|
|
5476
|
+
{
|
|
5477
|
+
return;
|
|
5478
|
+
}
|
|
5213
5479
|
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
}
|
|
5480
|
+
MessageHandler messageHandler = (MessageHandler)reg.payload;
|
|
5481
|
+
HandlerCache<int, HandlerCache> capturedHandlers =
|
|
5482
|
+
(HandlerCache<int, HandlerCache>)reg.capturedPrimary;
|
|
5483
|
+
int priority = reg.priority;
|
|
5484
|
+
RegistrationMethod registrationMethod = reg.method;
|
|
5485
|
+
SlotKey slotKey = RegistrationMethodAxes.GetSlotKey(registrationMethod);
|
|
5486
|
+
Type type = typeof(T);
|
|
5487
|
+
InstanceId handlerOwnerId = messageHandler.owner;
|
|
5223
5488
|
|
|
5224
|
-
|
|
5225
|
-
|
|
5489
|
+
long deregisterTouchTick = AdvanceTick();
|
|
5490
|
+
InvalidateDispatchPlans();
|
|
5226
5491
|
|
|
5227
|
-
|
|
5492
|
+
// FAST PATH: operate on the captured leaf handler-cache (already pinned on the
|
|
5493
|
+
// handle at registration time) DIRECTLY, without re-resolving the sink from the
|
|
5494
|
+
// method (ScalarSinkForMethod) or re-walking sinks->type->priority->handler. When
|
|
5495
|
+
// the generation guard above has passed and the captured bucket still holds this
|
|
5496
|
+
// handler, the captured leaf IS the live sink entry (handles are unique and never
|
|
5497
|
+
// reused), so the re-resolution + ReferenceEquals identity check is redundant. The
|
|
5498
|
+
// sweep-staleness / over-deregistration classification only matters when the handler
|
|
5499
|
+
// is NOT found, so it is deferred to the cold fallback below. This removes the
|
|
5500
|
+
// per-deregistration sink re-resolution (the measured cold-path regression source)
|
|
5501
|
+
// while preserving every guard and the throw-safe ordering (this method performs no
|
|
5502
|
+
// user callback; the throwing IMessageBus.Deregister boundary is the caller's).
|
|
5503
|
+
if (
|
|
5504
|
+
capturedHandlers.handlers.TryGetValue(priority, out HandlerCache cache)
|
|
5505
|
+
&& cache.handlers.TryGetValue(messageHandler, out int count)
|
|
5506
|
+
)
|
|
5507
|
+
{
|
|
5508
|
+
_log?.Log(
|
|
5228
5509
|
new MessagingRegistration(
|
|
5229
5510
|
handlerOwnerId,
|
|
5230
5511
|
type,
|
|
@@ -5232,30 +5513,27 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5232
5513
|
registrationMethod
|
|
5233
5514
|
)
|
|
5234
5515
|
);
|
|
5235
|
-
Touch(
|
|
5236
|
-
|
|
5237
|
-
|
|
5516
|
+
Touch(capturedHandlers, deregisterTouchTick);
|
|
5517
|
+
capturedHandlers.version++;
|
|
5518
|
+
cache.version++;
|
|
5519
|
+
Dictionary<MessageHandler, int> handler = cache.handlers;
|
|
5238
5520
|
if (count <= 1)
|
|
5239
5521
|
{
|
|
5240
|
-
|
|
5241
|
-
// List.Remove is O(n) over the same-priority bucket.
|
|
5242
|
-
//
|
|
5243
|
-
//
|
|
5244
|
-
//
|
|
5245
|
-
//
|
|
5246
|
-
// order, unlike Dictionary enumeration whose freed slots
|
|
5247
|
-
// are reused LIFO. Mirrors the MessageHandler-side
|
|
5522
|
+
_ = handler.Remove(messageHandler);
|
|
5523
|
+
// List.Remove is O(n) over the same-priority bucket. Accepted tradeoff (here
|
|
5524
|
+
// and at the context-path sibling site): buckets are small in practice,
|
|
5525
|
+
// removal is a cold churn path, and the list keeps dispatch-order rebuilds
|
|
5526
|
+
// allocation-free while preserving first-registration order, unlike Dictionary
|
|
5527
|
+
// enumeration whose freed slots are reused LIFO. Mirrors the MessageHandler-side
|
|
5248
5528
|
// insertionOrder tradeoff.
|
|
5249
5529
|
_ = cache.insertionOrder.Remove(messageHandler);
|
|
5250
5530
|
MarkDirtyHandler(messageHandler);
|
|
5251
|
-
cache.version++;
|
|
5252
|
-
// do not mutate cache.cache here; let next read rebuild from handlers
|
|
5253
5531
|
|
|
5254
5532
|
if (handler.Count == 0)
|
|
5255
5533
|
{
|
|
5256
|
-
_ =
|
|
5534
|
+
_ = capturedHandlers.handlers.Remove(priority);
|
|
5257
5535
|
// remove priority from order
|
|
5258
|
-
List<int> order =
|
|
5536
|
+
List<int> order = capturedHandlers.order;
|
|
5259
5537
|
int removeIdx = order.IndexOf(priority);
|
|
5260
5538
|
if (removeIdx >= 0)
|
|
5261
5539
|
{
|
|
@@ -5263,30 +5541,51 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5263
5541
|
}
|
|
5264
5542
|
}
|
|
5265
5543
|
|
|
5266
|
-
if (
|
|
5544
|
+
if (capturedHandlers.handlers.Count == 0)
|
|
5267
5545
|
{
|
|
5268
5546
|
MarkDirtyType<T>();
|
|
5269
5547
|
}
|
|
5270
|
-
|
|
5271
|
-
if (!complete && MessagingDebug.enabled)
|
|
5272
|
-
{
|
|
5273
|
-
MessagingDebug.Log(
|
|
5274
|
-
LogLevel.Error,
|
|
5275
|
-
"Received over-deregistration of {0} for {1}. Check to make sure you're not calling (de)registration multiple times.",
|
|
5276
|
-
type,
|
|
5277
|
-
messageHandler
|
|
5278
|
-
);
|
|
5279
|
-
}
|
|
5280
5548
|
}
|
|
5281
5549
|
else
|
|
5282
5550
|
{
|
|
5283
5551
|
handler[messageHandler] = count - 1;
|
|
5284
5552
|
}
|
|
5285
|
-
StageDispatchSnapshot<T>(this,
|
|
5286
|
-
|
|
5553
|
+
StageDispatchSnapshot<T>(this, capturedHandlers, slotKey);
|
|
5554
|
+
return;
|
|
5555
|
+
}
|
|
5556
|
+
|
|
5557
|
+
// COLD FALLBACK: the handler was not found in the captured bucket, so this is a no-op
|
|
5558
|
+
// deregistration (post-sweep, or a genuine over-deregistration) -- nothing is removed,
|
|
5559
|
+
// so NO version bump / snapshot invalidation is performed. Bumping here would
|
|
5560
|
+
// spuriously rebuild whichever bucket currently occupies this priority, which after a
|
|
5561
|
+
// full deregister + re-registration at the same priority is the NEW live bucket (the
|
|
5562
|
+
// stale handle must not touch it). Re-resolve the live sink only to CLASSIFY this as a
|
|
5563
|
+
// silent stale-after-sweep no-op versus a genuine over-deregistration. Rare path; not
|
|
5564
|
+
// on the steady deregistration cost.
|
|
5565
|
+
MessageCache<HandlerCache<int, HandlerCache>> sinks = ScalarSinkForMethod(
|
|
5566
|
+
registrationMethod
|
|
5567
|
+
);
|
|
5568
|
+
_ = sinks.TryGetValue<T>(out HandlerCache<int, HandlerCache> handlers);
|
|
5569
|
+
if (
|
|
5570
|
+
capturedHandlers.handlers.Count == 0
|
|
5571
|
+
&& !ReferenceEquals(handlers, capturedHandlers)
|
|
5572
|
+
)
|
|
5573
|
+
{
|
|
5574
|
+
return;
|
|
5575
|
+
}
|
|
5576
|
+
|
|
5577
|
+
if (MessagingDebug.enabled)
|
|
5578
|
+
{
|
|
5579
|
+
MessagingDebug.Log(
|
|
5580
|
+
LogLevel.Error,
|
|
5581
|
+
"Received over-deregistration of {0} for {1}. Check to make sure you're not calling (de)registration multiple times.",
|
|
5582
|
+
type,
|
|
5583
|
+
messageHandler
|
|
5584
|
+
);
|
|
5585
|
+
}
|
|
5287
5586
|
}
|
|
5288
5587
|
|
|
5289
|
-
private
|
|
5588
|
+
private MessageBusRegistration InternalRegisterWithContext<T>(
|
|
5290
5589
|
InstanceId context,
|
|
5291
5590
|
MessageHandler messageHandler,
|
|
5292
5591
|
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sinks,
|
|
@@ -5351,7 +5650,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5351
5650
|
}
|
|
5352
5651
|
|
|
5353
5652
|
Type type = typeof(T);
|
|
5354
|
-
_log
|
|
5653
|
+
_log?.Log(
|
|
5355
5654
|
new MessagingRegistration(
|
|
5356
5655
|
context,
|
|
5357
5656
|
type,
|
|
@@ -5362,46 +5661,60 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5362
5661
|
StageDispatchSnapshot<T>(this, handlers, slotKey);
|
|
5363
5662
|
|
|
5364
5663
|
long capturedGeneration = _resetGeneration;
|
|
5365
|
-
return (
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5664
|
+
return new MessageBusRegistration(
|
|
5665
|
+
MessageBusRegistration.Kind.Handler,
|
|
5666
|
+
registrationMethod,
|
|
5667
|
+
priority,
|
|
5668
|
+
capturedGeneration,
|
|
5669
|
+
0L,
|
|
5670
|
+
touchTick,
|
|
5671
|
+
capturedHandlers,
|
|
5672
|
+
capturedBroadcastHandlers,
|
|
5673
|
+
messageHandler,
|
|
5674
|
+
context
|
|
5675
|
+
);
|
|
5676
|
+
}
|
|
5373
5677
|
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
}
|
|
5678
|
+
/// <summary>
|
|
5679
|
+
/// Re-expression of the former <see cref="InternalRegisterWithContext{T}"/> closure body:
|
|
5680
|
+
/// the keyed (targeted / sourced-broadcast) handler deregistration. Behaviour and the four
|
|
5681
|
+
/// reentrancy invariants are unchanged; the closure's captured locals are read from
|
|
5682
|
+
/// <paramref name="reg"/>'s fields, and the sink is re-resolved from the method.
|
|
5683
|
+
/// </summary>
|
|
5684
|
+
private void DeregisterContextHandler<T>(in MessageBusRegistration reg)
|
|
5685
|
+
where T : IMessage
|
|
5686
|
+
{
|
|
5687
|
+
// Generation guard: see DeregisterScalarHandler for the rationale. Skip silently when
|
|
5688
|
+
// the handle outlived a Reset.
|
|
5689
|
+
if (reg.generation != _resetGeneration)
|
|
5690
|
+
{
|
|
5691
|
+
return;
|
|
5692
|
+
}
|
|
5390
5693
|
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
}
|
|
5694
|
+
MessageHandler messageHandler = (MessageHandler)reg.payload;
|
|
5695
|
+
HandlerCache<int, HandlerCache> capturedHandlers =
|
|
5696
|
+
(HandlerCache<int, HandlerCache>)reg.capturedPrimary;
|
|
5697
|
+
InstanceId context = reg.context;
|
|
5698
|
+
int priority = reg.priority;
|
|
5699
|
+
RegistrationMethod registrationMethod = reg.method;
|
|
5700
|
+
SlotKey slotKey = RegistrationMethodAxes.GetSlotKey(registrationMethod);
|
|
5701
|
+
Type type = typeof(T);
|
|
5400
5702
|
|
|
5401
|
-
|
|
5402
|
-
|
|
5703
|
+
long deregisterTouchTick = AdvanceTick();
|
|
5704
|
+
InvalidateDispatchPlans();
|
|
5403
5705
|
|
|
5404
|
-
|
|
5706
|
+
// FAST PATH: operate on the captured per-context leaf handler-cache directly, without
|
|
5707
|
+
// re-resolving the sink (ContextSinkForMethod) or re-walking sinks->type->context->
|
|
5708
|
+
// priority->handler. See DeregisterScalarHandler for the full rationale and the
|
|
5709
|
+
// sweep-staleness argument (the context sweep, like the scalar sweep, only evicts
|
|
5710
|
+
// EMPTY caches, so a found handler is never in a swept/detached cache). Throw-safe:
|
|
5711
|
+
// no user callback runs here.
|
|
5712
|
+
if (
|
|
5713
|
+
capturedHandlers.handlers.TryGetValue(priority, out HandlerCache cache)
|
|
5714
|
+
&& cache.handlers.TryGetValue(messageHandler, out int count)
|
|
5715
|
+
)
|
|
5716
|
+
{
|
|
5717
|
+
_log?.Log(
|
|
5405
5718
|
new MessagingRegistration(
|
|
5406
5719
|
context,
|
|
5407
5720
|
type,
|
|
@@ -5409,23 +5722,22 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5409
5722
|
registrationMethod
|
|
5410
5723
|
)
|
|
5411
5724
|
);
|
|
5412
|
-
Touch(
|
|
5413
|
-
|
|
5725
|
+
Touch(capturedHandlers, deregisterTouchTick);
|
|
5726
|
+
cache.version++;
|
|
5727
|
+
Dictionary<MessageHandler, int> handler = cache.handlers;
|
|
5414
5728
|
if (count <= 1)
|
|
5415
5729
|
{
|
|
5416
|
-
|
|
5417
|
-
// O(n) List.Remove: see the tradeoff comment at the
|
|
5418
|
-
//
|
|
5730
|
+
_ = handler.Remove(messageHandler);
|
|
5731
|
+
// O(n) List.Remove: see the tradeoff comment at the scalar-path sibling site in
|
|
5732
|
+
// DeregisterScalarHandler.
|
|
5419
5733
|
_ = cache.insertionOrder.Remove(messageHandler);
|
|
5420
5734
|
MarkDirtyHandler(messageHandler);
|
|
5421
|
-
cache.version++;
|
|
5422
|
-
// do not mutate cache.cache here; let next read rebuild from handlers
|
|
5423
5735
|
if (handler.Count == 0)
|
|
5424
5736
|
{
|
|
5425
|
-
|
|
5426
|
-
_ =
|
|
5737
|
+
capturedHandlers.version++;
|
|
5738
|
+
_ = capturedHandlers.handlers.Remove(priority);
|
|
5427
5739
|
// remove priority from order
|
|
5428
|
-
List<int> order =
|
|
5740
|
+
List<int> order = capturedHandlers.order;
|
|
5429
5741
|
int removeIdx = order.IndexOf(priority);
|
|
5430
5742
|
if (removeIdx >= 0)
|
|
5431
5743
|
{
|
|
@@ -5433,29 +5745,265 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5433
5745
|
}
|
|
5434
5746
|
}
|
|
5435
5747
|
|
|
5436
|
-
if (
|
|
5748
|
+
if (capturedHandlers.handlers.Count == 0)
|
|
5437
5749
|
{
|
|
5438
5750
|
MarkDirtyTarget<T>(context);
|
|
5439
5751
|
}
|
|
5440
|
-
|
|
5441
|
-
if (!complete && MessagingDebug.enabled)
|
|
5442
|
-
{
|
|
5443
|
-
MessagingDebug.Log(
|
|
5444
|
-
LogLevel.Error,
|
|
5445
|
-
"Received over-deregistration of {0} for {1}. Check to make sure you're not calling (de)registration multiple times.",
|
|
5446
|
-
type,
|
|
5447
|
-
messageHandler
|
|
5448
|
-
);
|
|
5449
|
-
}
|
|
5450
5752
|
}
|
|
5451
5753
|
else
|
|
5452
5754
|
{
|
|
5453
5755
|
handler[messageHandler] = count - 1;
|
|
5454
5756
|
}
|
|
5455
|
-
StageDispatchSnapshot<T>(this,
|
|
5757
|
+
StageDispatchSnapshot<T>(this, capturedHandlers, slotKey);
|
|
5758
|
+
return;
|
|
5759
|
+
}
|
|
5760
|
+
|
|
5761
|
+
// COLD FALLBACK: handler not found in the captured bucket -> a no-op deregistration
|
|
5762
|
+
// (nothing removed), so NO version bump is performed (it would spuriously rebuild the
|
|
5763
|
+
// bucket currently at this priority -- the NEW live bucket after a full deregister +
|
|
5764
|
+
// re-registration). Re-resolve only to CLASSIFY stale-after-sweep (silent) versus
|
|
5765
|
+
// over-deregistration (error). Rare path; not on the steady deregistration cost.
|
|
5766
|
+
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sinks =
|
|
5767
|
+
ContextSinkForMethod(registrationMethod);
|
|
5768
|
+
if (IsStaleContextDeregisterAfterSweep<T>(sinks, context, capturedHandlers))
|
|
5769
|
+
{
|
|
5770
|
+
return;
|
|
5771
|
+
}
|
|
5772
|
+
|
|
5773
|
+
if (MessagingDebug.enabled)
|
|
5774
|
+
{
|
|
5775
|
+
MessagingDebug.Log(
|
|
5776
|
+
LogLevel.Error,
|
|
5777
|
+
"Received over-deregistration of {0} for {1}. Check to make sure you're not calling (de)registration multiple times.",
|
|
5778
|
+
type,
|
|
5779
|
+
messageHandler
|
|
5780
|
+
);
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
|
|
5784
|
+
/// <inheritdoc />
|
|
5785
|
+
public void Deregister<T>(in MessageBusRegistration registration)
|
|
5786
|
+
where T : IMessage
|
|
5787
|
+
{
|
|
5788
|
+
switch (registration.kind)
|
|
5789
|
+
{
|
|
5790
|
+
case MessageBusRegistration.Kind.Handler:
|
|
5791
|
+
if (IsContextMethod(registration.method))
|
|
5792
|
+
{
|
|
5793
|
+
DeregisterContextHandler<T>(in registration);
|
|
5794
|
+
}
|
|
5795
|
+
else
|
|
5796
|
+
{
|
|
5797
|
+
DeregisterScalarHandler<T>(in registration);
|
|
5798
|
+
}
|
|
5799
|
+
break;
|
|
5800
|
+
case MessageBusRegistration.Kind.UntargetedInterceptor:
|
|
5801
|
+
case MessageBusRegistration.Kind.TargetedInterceptor:
|
|
5802
|
+
case MessageBusRegistration.Kind.BroadcastInterceptor:
|
|
5803
|
+
DeregisterInterceptor<T>(in registration);
|
|
5804
|
+
break;
|
|
5805
|
+
case MessageBusRegistration.Kind.GlobalAcceptAll:
|
|
5806
|
+
DeregisterGlobalAcceptAll(in registration);
|
|
5807
|
+
break;
|
|
5808
|
+
default:
|
|
5809
|
+
// Kind.None is the empty/sentinel handle (no-op); Kind.External handles are
|
|
5810
|
+
// minted by a foreign IMessageBus implementation and own no store here.
|
|
5811
|
+
break;
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
5814
|
+
|
|
5815
|
+
/// <summary>
|
|
5816
|
+
/// Re-expression of the former interceptor closure body. The interceptor store is selected
|
|
5817
|
+
/// by <see cref="MessageBusRegistration.kind"/> (all three interceptor registrars log the
|
|
5818
|
+
/// single <see cref="RegistrationMethod.Interceptor"/>, so the method cannot discriminate).
|
|
5819
|
+
/// </summary>
|
|
5820
|
+
private void DeregisterInterceptor<T>(in MessageBusRegistration reg)
|
|
5821
|
+
where T : IMessage
|
|
5822
|
+
{
|
|
5823
|
+
// Generation guard: see DeregisterScalarHandler.
|
|
5824
|
+
if (reg.generation != _resetGeneration)
|
|
5825
|
+
{
|
|
5826
|
+
return;
|
|
5827
|
+
}
|
|
5828
|
+
|
|
5829
|
+
MessageCache<InterceptorCache<object>> interceptsByType = reg.kind switch
|
|
5830
|
+
{
|
|
5831
|
+
MessageBusRegistration.Kind.UntargetedInterceptor => _untargetedInterceptsByType,
|
|
5832
|
+
MessageBusRegistration.Kind.TargetedInterceptor => _targetedInterceptsByType,
|
|
5833
|
+
MessageBusRegistration.Kind.BroadcastInterceptor => _broadcastInterceptsByType,
|
|
5834
|
+
_ => null,
|
|
5456
5835
|
};
|
|
5836
|
+
if (interceptsByType == null)
|
|
5837
|
+
{
|
|
5838
|
+
return;
|
|
5839
|
+
}
|
|
5840
|
+
|
|
5841
|
+
InterceptorCache<object> capturedInterceptors =
|
|
5842
|
+
(InterceptorCache<object>)reg.capturedPrimary;
|
|
5843
|
+
object interceptor = reg.payload;
|
|
5844
|
+
int priority = reg.priority;
|
|
5845
|
+
|
|
5846
|
+
if (IsStaleInterceptorDeregisterAfterSweep<T>(interceptsByType, capturedInterceptors))
|
|
5847
|
+
{
|
|
5848
|
+
return;
|
|
5849
|
+
}
|
|
5850
|
+
|
|
5851
|
+
_ = AdvanceTick();
|
|
5852
|
+
InvalidateDispatchPlans();
|
|
5853
|
+
capturedInterceptors.lastTouchTicks = _tickCounter;
|
|
5854
|
+
MarkDirtyType<T>();
|
|
5855
|
+
_log?.Log(
|
|
5856
|
+
new MessagingRegistration(
|
|
5857
|
+
InstanceId.EmptyId,
|
|
5858
|
+
typeof(T),
|
|
5859
|
+
RegistrationType.Deregister,
|
|
5860
|
+
RegistrationMethod.Interceptor
|
|
5861
|
+
)
|
|
5862
|
+
);
|
|
5863
|
+
bool removed = false;
|
|
5864
|
+
if (
|
|
5865
|
+
_uniqueInterceptorsAndPriorities.TryGetValue(
|
|
5866
|
+
interceptor,
|
|
5867
|
+
out Dictionary<int, int> priorityCount
|
|
5868
|
+
)
|
|
5869
|
+
)
|
|
5870
|
+
{
|
|
5871
|
+
if (priorityCount.TryGetValue(priority, out int count))
|
|
5872
|
+
{
|
|
5873
|
+
if (1 < count)
|
|
5874
|
+
{
|
|
5875
|
+
priorityCount[priority] = count - 1;
|
|
5876
|
+
}
|
|
5877
|
+
else
|
|
5878
|
+
{
|
|
5879
|
+
removed = true;
|
|
5880
|
+
_ = priorityCount.Remove(priority);
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5884
|
+
if (priorityCount.Count == 0)
|
|
5885
|
+
{
|
|
5886
|
+
_uniqueInterceptorsAndPriorities.Remove(interceptor);
|
|
5887
|
+
}
|
|
5888
|
+
}
|
|
5889
|
+
else if (MessagingDebug.enabled)
|
|
5890
|
+
{
|
|
5891
|
+
MessagingDebug.Log(
|
|
5892
|
+
LogLevel.Error,
|
|
5893
|
+
"Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
5894
|
+
interceptor
|
|
5895
|
+
);
|
|
5896
|
+
}
|
|
5897
|
+
|
|
5898
|
+
bool complete = false;
|
|
5899
|
+
if (removed)
|
|
5900
|
+
{
|
|
5901
|
+
if (
|
|
5902
|
+
interceptsByType.TryGetValue<T>(
|
|
5903
|
+
out InterceptorCache<object> prioritizedInterceptors
|
|
5904
|
+
)
|
|
5905
|
+
)
|
|
5906
|
+
{
|
|
5907
|
+
if (
|
|
5908
|
+
prioritizedInterceptors.handlers.TryGetValue(
|
|
5909
|
+
priority,
|
|
5910
|
+
out List<object> interceptors
|
|
5911
|
+
)
|
|
5912
|
+
)
|
|
5913
|
+
{
|
|
5914
|
+
complete = interceptors.Remove(interceptor);
|
|
5915
|
+
if (interceptors.Count == 0)
|
|
5916
|
+
{
|
|
5917
|
+
_ = prioritizedInterceptors.handlers.Remove(priority);
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5921
|
+
|
|
5922
|
+
if (!complete && MessagingDebug.enabled)
|
|
5923
|
+
{
|
|
5924
|
+
MessagingDebug.Log(
|
|
5925
|
+
LogLevel.Error,
|
|
5926
|
+
"Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
|
|
5927
|
+
interceptor
|
|
5928
|
+
);
|
|
5929
|
+
}
|
|
5930
|
+
}
|
|
5457
5931
|
}
|
|
5458
5932
|
|
|
5933
|
+
/// <summary>
|
|
5934
|
+
/// True for the keyed (targeted / sourced-broadcast) handler registration methods, whose
|
|
5935
|
+
/// deregistration runs <see cref="DeregisterContextHandler{T}"/>; false for the scalar
|
|
5936
|
+
/// (untargeted / without-context) methods. Mirrors the register-side sink routing.
|
|
5937
|
+
/// </summary>
|
|
5938
|
+
private static bool IsContextMethod(RegistrationMethod method) =>
|
|
5939
|
+
method switch
|
|
5940
|
+
{
|
|
5941
|
+
RegistrationMethod.Targeted
|
|
5942
|
+
or RegistrationMethod.Broadcast
|
|
5943
|
+
or RegistrationMethod.TargetedPostProcessor
|
|
5944
|
+
or RegistrationMethod.BroadcastPostProcessor => true,
|
|
5945
|
+
_ => false,
|
|
5946
|
+
};
|
|
5947
|
+
|
|
5948
|
+
/// <summary>
|
|
5949
|
+
/// Reverse of the register-side hardcoded scalar-sink selection (e.g.
|
|
5950
|
+
/// <c>_scalarSinks[BusSinkIndex.UntargetedHandleDefault]</c>): maps a scalar handler method
|
|
5951
|
+
/// back to its sink so <see cref="DeregisterScalarHandler{T}"/> can re-resolve it.
|
|
5952
|
+
/// </summary>
|
|
5953
|
+
private MessageCache<HandlerCache<int, HandlerCache>> ScalarSinkForMethod(
|
|
5954
|
+
RegistrationMethod method
|
|
5955
|
+
) =>
|
|
5956
|
+
method switch
|
|
5957
|
+
{
|
|
5958
|
+
RegistrationMethod.Untargeted => _scalarSinks[BusSinkIndex.UntargetedHandleDefault],
|
|
5959
|
+
RegistrationMethod.BroadcastWithoutSource => _scalarSinks[
|
|
5960
|
+
BusSinkIndex.BroadcastHandleWithoutContext
|
|
5961
|
+
],
|
|
5962
|
+
RegistrationMethod.TargetedWithoutTargeting => _scalarSinks[
|
|
5963
|
+
BusSinkIndex.TargetedHandleWithoutContext
|
|
5964
|
+
],
|
|
5965
|
+
RegistrationMethod.UntargetedPostProcessor => _scalarSinks[
|
|
5966
|
+
BusSinkIndex.UntargetedPostProcessDefault
|
|
5967
|
+
],
|
|
5968
|
+
RegistrationMethod.TargetedWithoutTargetingPostProcessor => _scalarSinks[
|
|
5969
|
+
BusSinkIndex.TargetedPostProcessWithoutContext
|
|
5970
|
+
],
|
|
5971
|
+
RegistrationMethod.BroadcastWithoutSourcePostProcessor => _scalarSinks[
|
|
5972
|
+
BusSinkIndex.BroadcastPostProcessWithoutContext
|
|
5973
|
+
],
|
|
5974
|
+
_ => throw new ArgumentOutOfRangeException(
|
|
5975
|
+
nameof(method),
|
|
5976
|
+
method,
|
|
5977
|
+
"Not a scalar handler registration method."
|
|
5978
|
+
),
|
|
5979
|
+
};
|
|
5980
|
+
|
|
5981
|
+
/// <summary>
|
|
5982
|
+
/// Reverse of the register-side hardcoded context-sink selection: maps a keyed handler
|
|
5983
|
+
/// method back to its sink so <see cref="DeregisterContextHandler{T}"/> can re-resolve it.
|
|
5984
|
+
/// </summary>
|
|
5985
|
+
private MessageCache<
|
|
5986
|
+
Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
|
|
5987
|
+
> ContextSinkForMethod(RegistrationMethod method) =>
|
|
5988
|
+
method switch
|
|
5989
|
+
{
|
|
5990
|
+
RegistrationMethod.Targeted => _contextSinks[BusContextIndex.TargetedHandleDefault],
|
|
5991
|
+
RegistrationMethod.Broadcast => _contextSinks[
|
|
5992
|
+
BusContextIndex.BroadcastHandleDefault
|
|
5993
|
+
],
|
|
5994
|
+
RegistrationMethod.TargetedPostProcessor => _contextSinks[
|
|
5995
|
+
BusContextIndex.TargetedPostProcessDefault
|
|
5996
|
+
],
|
|
5997
|
+
RegistrationMethod.BroadcastPostProcessor => _contextSinks[
|
|
5998
|
+
BusContextIndex.BroadcastPostProcessDefault
|
|
5999
|
+
],
|
|
6000
|
+
_ => throw new ArgumentOutOfRangeException(
|
|
6001
|
+
nameof(method),
|
|
6002
|
+
method,
|
|
6003
|
+
"Not a context handler registration method."
|
|
6004
|
+
),
|
|
6005
|
+
};
|
|
6006
|
+
|
|
5459
6007
|
private static bool IsStaleContextDeregisterAfterSweep<T>(
|
|
5460
6008
|
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sinks,
|
|
5461
6009
|
InstanceId context,
|
|
@@ -5587,7 +6135,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5587
6135
|
{
|
|
5588
6136
|
DebugAssertAcquireFastPrecondition(handlers);
|
|
5589
6137
|
DispatchState state = handlers.dispatchState;
|
|
5590
|
-
if (state != null && !state.hasPending &&
|
|
6138
|
+
if (state != null && !state.hasPending && state.active.IsInitialized)
|
|
5591
6139
|
{
|
|
5592
6140
|
handlers.lastTouchTicks = messageBus._tickCounter;
|
|
5593
6141
|
state.snapshotEmissionId = emissionId;
|
|
@@ -5645,7 +6193,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5645
6193
|
|
|
5646
6194
|
if (state.hasPending)
|
|
5647
6195
|
{
|
|
5648
|
-
if (state.pendingDirty || (hasHandlers && state.pending.
|
|
6196
|
+
if (state.pendingDirty || (hasHandlers && !state.pending.IsInitialized))
|
|
5649
6197
|
{
|
|
5650
6198
|
ReleaseSnapshot(ref state.pending);
|
|
5651
6199
|
state.pending = hasHandlers
|
|
@@ -5655,7 +6203,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5655
6203
|
state.pendingDirty = false;
|
|
5656
6204
|
}
|
|
5657
6205
|
}
|
|
5658
|
-
else if (state.active.
|
|
6206
|
+
else if (!state.active.IsInitialized && hasHandlers)
|
|
5659
6207
|
{
|
|
5660
6208
|
ReleaseSnapshot(ref state.pending);
|
|
5661
6209
|
state.pending = BuildDispatchSnapshot<TMessage>(
|
|
@@ -5678,7 +6226,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5678
6226
|
// ReleaseDisplacedSnapshot defers the release to the
|
|
5679
6227
|
// outermost dispatch-lease exit when one is in flight.
|
|
5680
6228
|
messageBus.ReleaseDisplacedSnapshot(ref state.active);
|
|
5681
|
-
if (state.pendingDirty || (hasHandlers && state.pending.
|
|
6229
|
+
if (state.pendingDirty || (hasHandlers && !state.pending.IsInitialized))
|
|
5682
6230
|
{
|
|
5683
6231
|
ReleaseSnapshot(ref state.pending);
|
|
5684
6232
|
state.pending = hasHandlers
|
|
@@ -5698,7 +6246,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5698
6246
|
state.hasPending = false;
|
|
5699
6247
|
state.pendingDirty = false;
|
|
5700
6248
|
}
|
|
5701
|
-
else if (!hasHandlers &&
|
|
6249
|
+
else if (!hasHandlers && state.active.IsInitialized)
|
|
5702
6250
|
{
|
|
5703
6251
|
messageBus.ReleaseDisplacedSnapshot(ref state.active);
|
|
5704
6252
|
}
|
|
@@ -5722,46 +6270,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5722
6270
|
return DispatchSnapshot.Empty;
|
|
5723
6271
|
}
|
|
5724
6272
|
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
if (
|
|
5733
|
-
!handlers.handlers.TryGetValue(priority, out HandlerCache cache)
|
|
5734
|
-
|| cache == null
|
|
5735
|
-
)
|
|
5736
|
-
{
|
|
5737
|
-
buckets[i] = DispatchBucket.CreateEmpty(priority);
|
|
5738
|
-
continue;
|
|
5739
|
-
}
|
|
5740
|
-
|
|
5741
|
-
Dictionary<MessageHandler, int> handlerLookup = cache.handlers;
|
|
5742
|
-
if (handlerLookup == null || handlerLookup.Count == 0)
|
|
5743
|
-
{
|
|
5744
|
-
buckets[i] = DispatchBucket.CreateEmpty(priority);
|
|
5745
|
-
continue;
|
|
5746
|
-
}
|
|
5747
|
-
|
|
5748
|
-
// Count-only bucket: non-global dispatch walks the resolved
|
|
5749
|
-
// flat array exclusively, so the bucket carries the handler
|
|
5750
|
-
// count (consumed by IsEmpty / HasAnyDispatchEntries for the
|
|
5751
|
-
// legacy "found any handlers" reporting) without renting or
|
|
5752
|
-
// filling a DispatchEntry array.
|
|
5753
|
-
DebugAssertBusInsertionOrderInSync(cache);
|
|
5754
|
-
buckets[i] = new DispatchBucket(
|
|
5755
|
-
priority,
|
|
5756
|
-
Array.Empty<DispatchEntry>(),
|
|
5757
|
-
handlerLookup.Count,
|
|
5758
|
-
pooledEntries: false
|
|
5759
|
-
);
|
|
5760
|
-
}
|
|
5761
|
-
|
|
5762
|
-
DispatchSnapshot snapshot = new DispatchSnapshot(buckets, priorityCount, pooled: true);
|
|
5763
|
-
snapshot.flat = BuildFlatDispatch<TMessage>(messageBus, handlers, slotKey, context);
|
|
5764
|
-
return snapshot;
|
|
6273
|
+
FlatDispatchArray flat = BuildFlatDispatch<TMessage>(
|
|
6274
|
+
messageBus,
|
|
6275
|
+
handlers,
|
|
6276
|
+
slotKey,
|
|
6277
|
+
context
|
|
6278
|
+
);
|
|
6279
|
+
return new DispatchSnapshot(flat, hasRegistrations: true);
|
|
5765
6280
|
}
|
|
5766
6281
|
|
|
5767
6282
|
/// <summary>
|
|
@@ -6288,24 +6803,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6288
6803
|
);
|
|
6289
6804
|
}
|
|
6290
6805
|
|
|
6291
|
-
// IL2CPP check elision: buckets[0..bucketCount) is non-null by snapshot
|
|
6292
|
-
// construction; same justification as DispatchFlatSnapshot.
|
|
6293
6806
|
[Il2CppSetOption(Option.NullChecks, false)]
|
|
6294
6807
|
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
|
6295
6808
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
6296
6809
|
private static bool HasAnyDispatchEntries(DispatchSnapshot snapshot)
|
|
6297
6810
|
{
|
|
6298
|
-
|
|
6299
|
-
int bucketCount = snapshot.bucketCount;
|
|
6300
|
-
for (int i = 0; i < bucketCount; ++i)
|
|
6301
|
-
{
|
|
6302
|
-
if (0 < buckets[i].entryCount)
|
|
6303
|
-
{
|
|
6304
|
-
return true;
|
|
6305
|
-
}
|
|
6306
|
-
}
|
|
6307
|
-
|
|
6308
|
-
return false;
|
|
6811
|
+
return snapshot.hasRegistrations;
|
|
6309
6812
|
}
|
|
6310
6813
|
|
|
6311
6814
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
@@ -6330,7 +6833,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6330
6833
|
|
|
6331
6834
|
if (state.hasPending)
|
|
6332
6835
|
{
|
|
6333
|
-
if (state.pendingDirty || (hasHandlers && state.pending.
|
|
6836
|
+
if (state.pendingDirty || (hasHandlers && !state.pending.IsInitialized))
|
|
6334
6837
|
{
|
|
6335
6838
|
ReleaseSnapshot(ref state.pending);
|
|
6336
6839
|
if (hasHandlers)
|
|
@@ -6349,7 +6852,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6349
6852
|
state.pendingDirty = false;
|
|
6350
6853
|
}
|
|
6351
6854
|
}
|
|
6352
|
-
else if (state.active.
|
|
6855
|
+
else if (!state.active.IsInitialized && hasHandlers)
|
|
6353
6856
|
{
|
|
6354
6857
|
ReleaseSnapshot(ref state.pending);
|
|
6355
6858
|
state.pending = BuildGlobalDispatchSnapshot<TMessage>(messageBus, handlers, kind);
|
|
@@ -6365,7 +6868,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6365
6868
|
// snapshot may still be iterated by an outer emission, so
|
|
6366
6869
|
// its release is deferred while a dispatch lease is live.
|
|
6367
6870
|
messageBus.ReleaseDisplacedSnapshot(ref state.active);
|
|
6368
|
-
if (state.pendingDirty || (hasHandlers && state.pending.
|
|
6871
|
+
if (state.pendingDirty || (hasHandlers && !state.pending.IsInitialized))
|
|
6369
6872
|
{
|
|
6370
6873
|
ReleaseSnapshot(ref state.pending);
|
|
6371
6874
|
state.pending = hasHandlers
|
|
@@ -6380,7 +6883,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6380
6883
|
state.hasPending = false;
|
|
6381
6884
|
state.pendingDirty = false;
|
|
6382
6885
|
}
|
|
6383
|
-
else if (!hasHandlers &&
|
|
6886
|
+
else if (!hasHandlers && state.active.IsInitialized)
|
|
6384
6887
|
{
|
|
6385
6888
|
messageBus.ReleaseDisplacedSnapshot(ref state.active);
|
|
6386
6889
|
}
|
|
@@ -6414,7 +6917,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6414
6917
|
}
|
|
6415
6918
|
|
|
6416
6919
|
buckets[0] = new DispatchBucket(0, entries, entryCount, pooledEntries: true);
|
|
6417
|
-
return new DispatchSnapshot(
|
|
6920
|
+
return new DispatchSnapshot(
|
|
6921
|
+
buckets,
|
|
6922
|
+
1,
|
|
6923
|
+
entryCount,
|
|
6924
|
+
hasRegistrations: true,
|
|
6925
|
+
pooled: true
|
|
6926
|
+
);
|
|
6418
6927
|
}
|
|
6419
6928
|
|
|
6420
6929
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|