com.wallstop-studios.dxmessaging 3.2.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 +40 -0
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +8 -8
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +171 -13
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +391 -96
- 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/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/DxMessagingSettingsProvider.cs +368 -33
- 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/AssemblyInfo.cs +1 -0
- package/Runtime/Core/Diagnostics/MessageEmissionData.cs +29 -1
- package/Runtime/Core/Extensions/MessageExtensions.cs +4 -5
- package/Runtime/Core/Helper/MessageCache.cs +56 -23
- package/Runtime/Core/Internal/FlatDispatch.cs +32 -0
- package/Runtime/Core/Internal/TypedSlots.cs +3 -7
- package/Runtime/Core/MessageBus/MessageBus.cs +577 -142
- package/Runtime/Core/MessageBus/RegistrationLog.cs +25 -12
- package/Runtime/Core/MessageHandler.cs +774 -786
- package/Runtime/Core/MessageRegistrationHandle.cs +19 -5
- package/Runtime/Core/MessageRegistrationToken.cs +1080 -520
- 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/README.md +1 -1
- package/Samples~/Mini Combat/Walkthrough.md +3 -3
- 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 +13 -4
|
@@ -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,6 +2362,87 @@ namespace DxMessaging.Core.MessageBus
|
|
|
1966
2362
|
return ContextHandlerByTargetDicts.Snapshot();
|
|
1967
2363
|
}
|
|
1968
2364
|
|
|
2365
|
+
internal int SweepDirtyTargetSlotForBenchmark<TMessage>(InstanceId target)
|
|
2366
|
+
where TMessage : IMessage
|
|
2367
|
+
{
|
|
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)
|
|
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)
|
|
2400
|
+
)
|
|
2401
|
+
{
|
|
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
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
|
|
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
|
+
|
|
1969
2446
|
private Dictionary<InstanceId, HandlerCache<int, HandlerCache>> GetOrRentContextMap<T>(
|
|
1970
2447
|
MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sinks
|
|
1971
2448
|
)
|
|
@@ -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()
|
|
@@ -2664,7 +3140,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2664
3140
|
{
|
|
2665
3141
|
_globalSlots.liveCount++;
|
|
2666
3142
|
}
|
|
2667
|
-
_log
|
|
3143
|
+
_log?.Log(
|
|
2668
3144
|
new MessagingRegistration(
|
|
2669
3145
|
messageHandler.owner,
|
|
2670
3146
|
type,
|
|
@@ -2728,7 +3204,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2728
3204
|
long deregisterTouchTick = AdvanceTick();
|
|
2729
3205
|
InvalidateDispatchPlans();
|
|
2730
3206
|
_globalSlots.version++;
|
|
2731
|
-
_log
|
|
3207
|
+
_log?.Log(
|
|
2732
3208
|
new MessagingRegistration(
|
|
2733
3209
|
messageHandler.owner,
|
|
2734
3210
|
type,
|
|
@@ -2831,7 +3307,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2831
3307
|
priorityCount[priority] = count + 1;
|
|
2832
3308
|
|
|
2833
3309
|
Type type = typeof(T);
|
|
2834
|
-
_log
|
|
3310
|
+
_log?.Log(
|
|
2835
3311
|
new MessagingRegistration(
|
|
2836
3312
|
InstanceId.EmptyId,
|
|
2837
3313
|
type,
|
|
@@ -2902,7 +3378,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2902
3378
|
priorityCount[priority] = count + 1;
|
|
2903
3379
|
|
|
2904
3380
|
Type type = typeof(T);
|
|
2905
|
-
_log
|
|
3381
|
+
_log?.Log(
|
|
2906
3382
|
new MessagingRegistration(
|
|
2907
3383
|
InstanceId.EmptyId,
|
|
2908
3384
|
type,
|
|
@@ -2973,7 +3449,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
2973
3449
|
priorityCount[priority] = count + 1;
|
|
2974
3450
|
|
|
2975
3451
|
Type type = typeof(T);
|
|
2976
|
-
_log
|
|
3452
|
+
_log?.Log(
|
|
2977
3453
|
new MessagingRegistration(
|
|
2978
3454
|
InstanceId.EmptyId,
|
|
2979
3455
|
type,
|
|
@@ -3170,7 +3646,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3170
3646
|
long touchTick = AdvanceTick();
|
|
3171
3647
|
if (_diagnosticsMode)
|
|
3172
3648
|
{
|
|
3173
|
-
_emissionBuffer.Add(new MessageEmissionData(typedMessage));
|
|
3649
|
+
_emissionBuffer.Add(new MessageEmissionData(typedMessage, emissionId));
|
|
3174
3650
|
}
|
|
3175
3651
|
|
|
3176
3652
|
if (plan.fastPath)
|
|
@@ -3305,7 +3781,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3305
3781
|
}
|
|
3306
3782
|
|
|
3307
3783
|
Touch(sortedHandlers, touchTick);
|
|
3308
|
-
DispatchSnapshot snapshot = prefrozenSnapshot.
|
|
3784
|
+
DispatchSnapshot snapshot = !prefrozenSnapshot.IsInitialized
|
|
3309
3785
|
? AcquireDispatchSnapshotFast<TMessage>(
|
|
3310
3786
|
this,
|
|
3311
3787
|
sortedHandlers,
|
|
@@ -3412,7 +3888,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3412
3888
|
long touchTick = AdvanceTick();
|
|
3413
3889
|
if (_diagnosticsMode)
|
|
3414
3890
|
{
|
|
3415
|
-
_emissionBuffer.Add(new MessageEmissionData(typedMessage, target));
|
|
3891
|
+
_emissionBuffer.Add(new MessageEmissionData(typedMessage, target, emissionId));
|
|
3416
3892
|
}
|
|
3417
3893
|
|
|
3418
3894
|
// Fast lane: no interceptors, no global accept-all, no
|
|
@@ -3893,7 +4369,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3893
4369
|
target
|
|
3894
4370
|
);
|
|
3895
4371
|
}
|
|
3896
|
-
else if (targetedPostSnapshot.
|
|
4372
|
+
else if (!targetedPostSnapshot.IsInitialized)
|
|
3897
4373
|
{
|
|
3898
4374
|
snapshot = AcquireDispatchSnapshotFast<TMessage>(
|
|
3899
4375
|
this,
|
|
@@ -3919,7 +4395,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
3919
4395
|
&& postTwt.handlers.Count > 0
|
|
3920
4396
|
)
|
|
3921
4397
|
{
|
|
3922
|
-
DispatchSnapshot snapshot = targetedWithoutTargetingPostSnapshot.
|
|
4398
|
+
DispatchSnapshot snapshot = !targetedWithoutTargetingPostSnapshot.IsInitialized
|
|
3923
4399
|
? AcquireDispatchSnapshotFast<TMessage>(
|
|
3924
4400
|
this,
|
|
3925
4401
|
postTwt,
|
|
@@ -4041,7 +4517,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4041
4517
|
long touchTick = AdvanceTick();
|
|
4042
4518
|
if (_diagnosticsMode)
|
|
4043
4519
|
{
|
|
4044
|
-
_emissionBuffer.Add(new MessageEmissionData(typedMessage, source));
|
|
4520
|
+
_emissionBuffer.Add(new MessageEmissionData(typedMessage, source, emissionId));
|
|
4045
4521
|
}
|
|
4046
4522
|
|
|
4047
4523
|
// Fast lane: no interceptors, no global accept-all, no
|
|
@@ -4298,15 +4774,15 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4298
4774
|
}
|
|
4299
4775
|
}
|
|
4300
4776
|
|
|
4301
|
-
if (
|
|
4777
|
+
if (broadcastPostSnapshot.IsInitialized)
|
|
4302
4778
|
{
|
|
4303
|
-
// Legacy reporting:
|
|
4304
|
-
// "found" even
|
|
4779
|
+
// Legacy reporting: an initialized pre-frozen snapshot counts as
|
|
4780
|
+
// "found" even when it owns zero resolved delegates.
|
|
4305
4781
|
foundAnyHandlers = true;
|
|
4306
4782
|
_ = DispatchFlatSnapshot(broadcastPostSnapshot, ref typedMessage);
|
|
4307
4783
|
}
|
|
4308
4784
|
|
|
4309
|
-
if (
|
|
4785
|
+
if (broadcastWithoutSourcePostSnapshot.IsInitialized)
|
|
4310
4786
|
{
|
|
4311
4787
|
if (
|
|
4312
4788
|
DispatchContextFlatSnapshot(
|
|
@@ -4895,7 +5371,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4895
5371
|
// Empty), acquire lazily here: the legacy path took its first
|
|
4896
5372
|
// freeze at this point in that case, so a registration made
|
|
4897
5373
|
// earlier in this emission into a previously-empty sink fires.
|
|
4898
|
-
DispatchSnapshot snapshot = prefrozenSnapshot.
|
|
5374
|
+
DispatchSnapshot snapshot = !prefrozenSnapshot.IsInitialized
|
|
4899
5375
|
? AcquireDispatchSnapshotFast<TMessage>(
|
|
4900
5376
|
this,
|
|
4901
5377
|
sortedHandlers,
|
|
@@ -4960,7 +5436,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
4960
5436
|
}
|
|
4961
5437
|
StageDispatchSnapshot<T>(this, capturedHandlers, slotKey);
|
|
4962
5438
|
Type type = typeof(T);
|
|
4963
|
-
_log
|
|
5439
|
+
_log?.Log(
|
|
4964
5440
|
new MessagingRegistration(
|
|
4965
5441
|
handlerOwnerId,
|
|
4966
5442
|
type,
|
|
@@ -5029,7 +5505,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5029
5505
|
&& cache.handlers.TryGetValue(messageHandler, out int count)
|
|
5030
5506
|
)
|
|
5031
5507
|
{
|
|
5032
|
-
_log
|
|
5508
|
+
_log?.Log(
|
|
5033
5509
|
new MessagingRegistration(
|
|
5034
5510
|
handlerOwnerId,
|
|
5035
5511
|
type,
|
|
@@ -5052,7 +5528,6 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5052
5528
|
// insertionOrder tradeoff.
|
|
5053
5529
|
_ = cache.insertionOrder.Remove(messageHandler);
|
|
5054
5530
|
MarkDirtyHandler(messageHandler);
|
|
5055
|
-
// do not mutate cache.cache here; let next read rebuild from handlers
|
|
5056
5531
|
|
|
5057
5532
|
if (handler.Count == 0)
|
|
5058
5533
|
{
|
|
@@ -5175,7 +5650,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5175
5650
|
}
|
|
5176
5651
|
|
|
5177
5652
|
Type type = typeof(T);
|
|
5178
|
-
_log
|
|
5653
|
+
_log?.Log(
|
|
5179
5654
|
new MessagingRegistration(
|
|
5180
5655
|
context,
|
|
5181
5656
|
type,
|
|
@@ -5239,7 +5714,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5239
5714
|
&& cache.handlers.TryGetValue(messageHandler, out int count)
|
|
5240
5715
|
)
|
|
5241
5716
|
{
|
|
5242
|
-
_log
|
|
5717
|
+
_log?.Log(
|
|
5243
5718
|
new MessagingRegistration(
|
|
5244
5719
|
context,
|
|
5245
5720
|
type,
|
|
@@ -5257,7 +5732,6 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5257
5732
|
// DeregisterScalarHandler.
|
|
5258
5733
|
_ = cache.insertionOrder.Remove(messageHandler);
|
|
5259
5734
|
MarkDirtyHandler(messageHandler);
|
|
5260
|
-
// do not mutate cache.cache here; let next read rebuild from handlers
|
|
5261
5735
|
if (handler.Count == 0)
|
|
5262
5736
|
{
|
|
5263
5737
|
capturedHandlers.version++;
|
|
@@ -5378,7 +5852,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5378
5852
|
InvalidateDispatchPlans();
|
|
5379
5853
|
capturedInterceptors.lastTouchTicks = _tickCounter;
|
|
5380
5854
|
MarkDirtyType<T>();
|
|
5381
|
-
_log
|
|
5855
|
+
_log?.Log(
|
|
5382
5856
|
new MessagingRegistration(
|
|
5383
5857
|
InstanceId.EmptyId,
|
|
5384
5858
|
typeof(T),
|
|
@@ -5661,7 +6135,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5661
6135
|
{
|
|
5662
6136
|
DebugAssertAcquireFastPrecondition(handlers);
|
|
5663
6137
|
DispatchState state = handlers.dispatchState;
|
|
5664
|
-
if (state != null && !state.hasPending &&
|
|
6138
|
+
if (state != null && !state.hasPending && state.active.IsInitialized)
|
|
5665
6139
|
{
|
|
5666
6140
|
handlers.lastTouchTicks = messageBus._tickCounter;
|
|
5667
6141
|
state.snapshotEmissionId = emissionId;
|
|
@@ -5719,7 +6193,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5719
6193
|
|
|
5720
6194
|
if (state.hasPending)
|
|
5721
6195
|
{
|
|
5722
|
-
if (state.pendingDirty || (hasHandlers && state.pending.
|
|
6196
|
+
if (state.pendingDirty || (hasHandlers && !state.pending.IsInitialized))
|
|
5723
6197
|
{
|
|
5724
6198
|
ReleaseSnapshot(ref state.pending);
|
|
5725
6199
|
state.pending = hasHandlers
|
|
@@ -5729,7 +6203,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5729
6203
|
state.pendingDirty = false;
|
|
5730
6204
|
}
|
|
5731
6205
|
}
|
|
5732
|
-
else if (state.active.
|
|
6206
|
+
else if (!state.active.IsInitialized && hasHandlers)
|
|
5733
6207
|
{
|
|
5734
6208
|
ReleaseSnapshot(ref state.pending);
|
|
5735
6209
|
state.pending = BuildDispatchSnapshot<TMessage>(
|
|
@@ -5752,7 +6226,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5752
6226
|
// ReleaseDisplacedSnapshot defers the release to the
|
|
5753
6227
|
// outermost dispatch-lease exit when one is in flight.
|
|
5754
6228
|
messageBus.ReleaseDisplacedSnapshot(ref state.active);
|
|
5755
|
-
if (state.pendingDirty || (hasHandlers && state.pending.
|
|
6229
|
+
if (state.pendingDirty || (hasHandlers && !state.pending.IsInitialized))
|
|
5756
6230
|
{
|
|
5757
6231
|
ReleaseSnapshot(ref state.pending);
|
|
5758
6232
|
state.pending = hasHandlers
|
|
@@ -5772,7 +6246,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5772
6246
|
state.hasPending = false;
|
|
5773
6247
|
state.pendingDirty = false;
|
|
5774
6248
|
}
|
|
5775
|
-
else if (!hasHandlers &&
|
|
6249
|
+
else if (!hasHandlers && state.active.IsInitialized)
|
|
5776
6250
|
{
|
|
5777
6251
|
messageBus.ReleaseDisplacedSnapshot(ref state.active);
|
|
5778
6252
|
}
|
|
@@ -5796,46 +6270,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
5796
6270
|
return DispatchSnapshot.Empty;
|
|
5797
6271
|
}
|
|
5798
6272
|
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
if (
|
|
5807
|
-
!handlers.handlers.TryGetValue(priority, out HandlerCache cache)
|
|
5808
|
-
|| cache == null
|
|
5809
|
-
)
|
|
5810
|
-
{
|
|
5811
|
-
buckets[i] = DispatchBucket.CreateEmpty(priority);
|
|
5812
|
-
continue;
|
|
5813
|
-
}
|
|
5814
|
-
|
|
5815
|
-
Dictionary<MessageHandler, int> handlerLookup = cache.handlers;
|
|
5816
|
-
if (handlerLookup == null || handlerLookup.Count == 0)
|
|
5817
|
-
{
|
|
5818
|
-
buckets[i] = DispatchBucket.CreateEmpty(priority);
|
|
5819
|
-
continue;
|
|
5820
|
-
}
|
|
5821
|
-
|
|
5822
|
-
// Count-only bucket: non-global dispatch walks the resolved
|
|
5823
|
-
// flat array exclusively, so the bucket carries the handler
|
|
5824
|
-
// count (consumed by IsEmpty / HasAnyDispatchEntries for the
|
|
5825
|
-
// legacy "found any handlers" reporting) without renting or
|
|
5826
|
-
// filling a DispatchEntry array.
|
|
5827
|
-
DebugAssertBusInsertionOrderInSync(cache);
|
|
5828
|
-
buckets[i] = new DispatchBucket(
|
|
5829
|
-
priority,
|
|
5830
|
-
Array.Empty<DispatchEntry>(),
|
|
5831
|
-
handlerLookup.Count,
|
|
5832
|
-
pooledEntries: false
|
|
5833
|
-
);
|
|
5834
|
-
}
|
|
5835
|
-
|
|
5836
|
-
DispatchSnapshot snapshot = new DispatchSnapshot(buckets, priorityCount, pooled: true);
|
|
5837
|
-
snapshot.flat = BuildFlatDispatch<TMessage>(messageBus, handlers, slotKey, context);
|
|
5838
|
-
return snapshot;
|
|
6273
|
+
FlatDispatchArray flat = BuildFlatDispatch<TMessage>(
|
|
6274
|
+
messageBus,
|
|
6275
|
+
handlers,
|
|
6276
|
+
slotKey,
|
|
6277
|
+
context
|
|
6278
|
+
);
|
|
6279
|
+
return new DispatchSnapshot(flat, hasRegistrations: true);
|
|
5839
6280
|
}
|
|
5840
6281
|
|
|
5841
6282
|
/// <summary>
|
|
@@ -6362,24 +6803,12 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6362
6803
|
);
|
|
6363
6804
|
}
|
|
6364
6805
|
|
|
6365
|
-
// IL2CPP check elision: buckets[0..bucketCount) is non-null by snapshot
|
|
6366
|
-
// construction; same justification as DispatchFlatSnapshot.
|
|
6367
6806
|
[Il2CppSetOption(Option.NullChecks, false)]
|
|
6368
6807
|
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
|
6369
6808
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
6370
6809
|
private static bool HasAnyDispatchEntries(DispatchSnapshot snapshot)
|
|
6371
6810
|
{
|
|
6372
|
-
|
|
6373
|
-
int bucketCount = snapshot.bucketCount;
|
|
6374
|
-
for (int i = 0; i < bucketCount; ++i)
|
|
6375
|
-
{
|
|
6376
|
-
if (0 < buckets[i].entryCount)
|
|
6377
|
-
{
|
|
6378
|
-
return true;
|
|
6379
|
-
}
|
|
6380
|
-
}
|
|
6381
|
-
|
|
6382
|
-
return false;
|
|
6811
|
+
return snapshot.hasRegistrations;
|
|
6383
6812
|
}
|
|
6384
6813
|
|
|
6385
6814
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
@@ -6404,7 +6833,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6404
6833
|
|
|
6405
6834
|
if (state.hasPending)
|
|
6406
6835
|
{
|
|
6407
|
-
if (state.pendingDirty || (hasHandlers && state.pending.
|
|
6836
|
+
if (state.pendingDirty || (hasHandlers && !state.pending.IsInitialized))
|
|
6408
6837
|
{
|
|
6409
6838
|
ReleaseSnapshot(ref state.pending);
|
|
6410
6839
|
if (hasHandlers)
|
|
@@ -6423,7 +6852,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6423
6852
|
state.pendingDirty = false;
|
|
6424
6853
|
}
|
|
6425
6854
|
}
|
|
6426
|
-
else if (state.active.
|
|
6855
|
+
else if (!state.active.IsInitialized && hasHandlers)
|
|
6427
6856
|
{
|
|
6428
6857
|
ReleaseSnapshot(ref state.pending);
|
|
6429
6858
|
state.pending = BuildGlobalDispatchSnapshot<TMessage>(messageBus, handlers, kind);
|
|
@@ -6439,7 +6868,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6439
6868
|
// snapshot may still be iterated by an outer emission, so
|
|
6440
6869
|
// its release is deferred while a dispatch lease is live.
|
|
6441
6870
|
messageBus.ReleaseDisplacedSnapshot(ref state.active);
|
|
6442
|
-
if (state.pendingDirty || (hasHandlers && state.pending.
|
|
6871
|
+
if (state.pendingDirty || (hasHandlers && !state.pending.IsInitialized))
|
|
6443
6872
|
{
|
|
6444
6873
|
ReleaseSnapshot(ref state.pending);
|
|
6445
6874
|
state.pending = hasHandlers
|
|
@@ -6454,7 +6883,7 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6454
6883
|
state.hasPending = false;
|
|
6455
6884
|
state.pendingDirty = false;
|
|
6456
6885
|
}
|
|
6457
|
-
else if (!hasHandlers &&
|
|
6886
|
+
else if (!hasHandlers && state.active.IsInitialized)
|
|
6458
6887
|
{
|
|
6459
6888
|
messageBus.ReleaseDisplacedSnapshot(ref state.active);
|
|
6460
6889
|
}
|
|
@@ -6488,7 +6917,13 @@ namespace DxMessaging.Core.MessageBus
|
|
|
6488
6917
|
}
|
|
6489
6918
|
|
|
6490
6919
|
buckets[0] = new DispatchBucket(0, entries, entryCount, pooledEntries: true);
|
|
6491
|
-
return new DispatchSnapshot(
|
|
6920
|
+
return new DispatchSnapshot(
|
|
6921
|
+
buckets,
|
|
6922
|
+
1,
|
|
6923
|
+
entryCount,
|
|
6924
|
+
hasRegistrations: true,
|
|
6925
|
+
pooled: true
|
|
6926
|
+
);
|
|
6492
6927
|
}
|
|
6493
6928
|
|
|
6494
6929
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|