com.wallstop-studios.dxmessaging 2.0.0-rc16 → 2.0.0-rc17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  2. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.pdb.meta +7 -0
  3. package/Editor/SetupCscRsp.cs +24 -7
  4. package/README.md +7 -17
  5. package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs +5 -1
  6. package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs +5 -1
  7. package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs +5 -1
  8. package/Runtime/Core/IMessage.cs +13 -0
  9. package/Runtime/Core/InstanceId.cs +6 -16
  10. package/Runtime/Core/MessageBus/MessageBus.cs +1751 -828
  11. package/Runtime/Core/MessageHandler.cs +408 -292
  12. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +445 -0
  13. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +3 -0
  14. package/Tests/Runtime/Benchmarks/PerformanceTests.cs +41 -26
  15. package/Tests/Runtime/Scripts/Components/GenericMessageAwareComponent.cs +19 -0
  16. package/Tests/Runtime/Scripts/Components/GenericMessageAwareComponent.cs.meta +3 -0
  17. package/Tests/Runtime/Scripts/Messages/GenericUntargetedMessage.cs +7 -0
  18. package/Tests/Runtime/Scripts/Messages/GenericUntargetedMessage.cs.meta +3 -0
  19. package/package.json +2 -2
  20. package/Runtime/Core/Attributes/DxAutoMessageTypeAttribute.cs +0 -7
  21. package/Runtime/Core/Attributes/DxAutoMessageTypeAttribute.cs.meta +0 -3
  22. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoMessageTypeGenerator.cs +0 -92
  23. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoMessageTypeGenerator.cs.meta +0 -11
  24. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxBroadcastMessageGenerator.cs +0 -94
  25. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxBroadcastMessageGenerator.cs.meta +0 -3
  26. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxTargetedMessageGenerator.cs +0 -94
  27. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxTargetedMessageGenerator.cs.meta +0 -3
  28. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxUntargetedMessageGenerator.cs +0 -94
  29. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxUntargetedMessageGenerator.cs.meta +0 -3
@@ -1339,123 +1339,116 @@
1339
1339
  return false;
1340
1340
  }
1341
1341
 
1342
- private abstract class TypedHandler
1342
+ private sealed class HandlerActionCache<T>
1343
1343
  {
1344
- protected static readonly Stack<
1345
- List<Action<IUntargetedMessage>>
1346
- > GlobalUntargetedHandlersStack = new();
1347
- protected static readonly Stack<
1348
- List<Action<InstanceId, ITargetedMessage>>
1349
- > GlobalTargetedHandlersStack = new();
1350
- protected static readonly Stack<
1351
- List<Action<InstanceId, IBroadcastMessage>>
1352
- > GlobalBroadcastHandlersStack = new();
1353
- protected static readonly Stack<
1354
- List<FastHandler<IUntargetedMessage>>
1355
- > GlobalUntargetedFastHandlersStack = new();
1356
- protected static Stack<
1357
- List<FastHandlerWithContext<ITargetedMessage>>
1358
- > GlobalTargetedFastHandlersStack = new();
1359
- protected static Stack<
1360
- List<FastHandlerWithContext<IBroadcastMessage>>
1361
- > GlobalBroadcastFastHandlersStack = new();
1344
+ public readonly Dictionary<T, int> handlers = new();
1345
+ public readonly List<T> cache = new();
1346
+ public long version;
1347
+ public long lastSeenVersion = -1;
1362
1348
  }
1363
1349
 
1364
1350
  /// <summary>
1365
1351
  /// One-size-fits-all wrapper around all possible Messaging sinks for a particular MessageHandler & MessageType.
1366
1352
  /// </summary>
1367
1353
  /// <typeparam name="T">Message type that this Handler exists to serve.</typeparam>
1368
- private sealed class TypedHandler<T> : TypedHandler
1354
+ private sealed class TypedHandler<T>
1369
1355
  where T : IMessage
1370
1356
  {
1371
- // Buffers so we don't allocate memory as often
1372
- private static Stack<List<Action<T>>> HandlersStack;
1373
- private static Stack<List<Action<InstanceId, T>>> HandlersWithoutContextStack;
1374
- private static Stack<List<FastHandler<T>>> FastHandlersStack;
1375
- private static Stack<List<FastHandlerWithContext<T>>> FastHandlersWithContextStack;
1376
-
1377
1357
  private Dictionary<
1378
1358
  InstanceId,
1379
- Dictionary<int, Dictionary<Action<T>, int>>
1359
+ Dictionary<int, HandlerActionCache<Action<T>>>
1380
1360
  > _targetedHandlers;
1381
- private Dictionary<int, Dictionary<Action<T>, int>> _untargetedHandlers;
1361
+ private Dictionary<int, HandlerActionCache<Action<T>>> _untargetedHandlers;
1382
1362
  private Dictionary<
1383
1363
  InstanceId,
1384
- Dictionary<int, Dictionary<Action<T>, int>>
1364
+ Dictionary<int, HandlerActionCache<Action<T>>>
1385
1365
  > _broadcastHandlers;
1386
1366
  private Dictionary<
1387
1367
  InstanceId,
1388
- Dictionary<int, Dictionary<Action<T>, int>>
1368
+ Dictionary<int, HandlerActionCache<Action<T>>>
1389
1369
  > _targetedPostProcessingHandlers;
1390
- private Dictionary<int, Dictionary<Action<T>, int>> _untargetedPostProcessingHandlers;
1370
+ private Dictionary<
1371
+ int,
1372
+ HandlerActionCache<Action<T>>
1373
+ > _untargetedPostProcessingHandlers;
1391
1374
  private Dictionary<
1392
1375
  InstanceId,
1393
- Dictionary<int, Dictionary<Action<T>, int>>
1376
+ Dictionary<int, HandlerActionCache<Action<T>>>
1394
1377
  > _broadcastPostProcessingHandlers;
1395
1378
  private Dictionary<
1396
1379
  InstanceId,
1397
- Dictionary<int, Dictionary<FastHandler<T>, int>>
1380
+ Dictionary<int, HandlerActionCache<FastHandler<T>>>
1398
1381
  > _targetedFastHandlers;
1399
- private Dictionary<int, Dictionary<FastHandler<T>, int>> _untargetedFastHandlers;
1382
+ private Dictionary<int, HandlerActionCache<FastHandler<T>>> _untargetedFastHandlers;
1400
1383
  private Dictionary<
1401
1384
  InstanceId,
1402
- Dictionary<int, Dictionary<FastHandler<T>, int>>
1385
+ Dictionary<int, HandlerActionCache<FastHandler<T>>>
1403
1386
  > _broadcastFastHandlers;
1404
1387
  private Dictionary<
1405
1388
  InstanceId,
1406
- Dictionary<int, Dictionary<FastHandler<T>, int>>
1389
+ Dictionary<int, HandlerActionCache<FastHandler<T>>>
1407
1390
  > _targetedPostProcessingFastHandlers;
1408
1391
  private Dictionary<
1409
1392
  int,
1410
- Dictionary<FastHandler<T>, int>
1393
+ HandlerActionCache<FastHandler<T>>
1411
1394
  > _untargetedPostProcessingFastHandlers;
1412
1395
  private Dictionary<
1413
1396
  InstanceId,
1414
- Dictionary<int, Dictionary<FastHandler<T>, int>>
1397
+ Dictionary<int, HandlerActionCache<FastHandler<T>>>
1415
1398
  > _broadcastPostProcessingFastHandlers;
1416
- private Dictionary<Action<IUntargetedMessage>, int> _globalUntargetedHandlers;
1417
- private Dictionary<Action<InstanceId, ITargetedMessage>, int> _globalTargetedHandlers;
1418
- private Dictionary<Action<InstanceId, IBroadcastMessage>, int> _globalBroadcastHandlers;
1419
- private Dictionary<FastHandler<IUntargetedMessage>, int> _globalUntargetedFastHandlers;
1420
- private Dictionary<
1421
- FastHandlerWithContext<ITargetedMessage>,
1422
- int
1399
+
1400
+ private HandlerActionCache<Action<IUntargetedMessage>> _globalUntargetedHandlers;
1401
+
1402
+ private HandlerActionCache<
1403
+ Action<InstanceId, ITargetedMessage>
1404
+ > _globalTargetedHandlers;
1405
+
1406
+ private HandlerActionCache<
1407
+ Action<InstanceId, IBroadcastMessage>
1408
+ > _globalBroadcastHandlers;
1409
+
1410
+ private HandlerActionCache<
1411
+ FastHandler<IUntargetedMessage>
1412
+ > _globalUntargetedFastHandlers;
1413
+
1414
+ private HandlerActionCache<
1415
+ FastHandlerWithContext<ITargetedMessage>
1423
1416
  > _globalTargetedFastHandlers;
1424
- private Dictionary<
1425
- FastHandlerWithContext<IBroadcastMessage>,
1426
- int
1417
+
1418
+ private HandlerActionCache<
1419
+ FastHandlerWithContext<IBroadcastMessage>
1427
1420
  > _globalBroadcastFastHandlers;
1428
1421
  private Dictionary<
1429
1422
  int,
1430
- Dictionary<Action<InstanceId, T>, int>
1423
+ HandlerActionCache<Action<InstanceId, T>>
1431
1424
  > _targetedWithoutTargetingHandlers;
1432
1425
  private Dictionary<
1433
1426
  int,
1434
- Dictionary<FastHandlerWithContext<T>, int>
1427
+ HandlerActionCache<FastHandlerWithContext<T>>
1435
1428
  > _fastTargetedWithoutTargetingHandlers;
1436
1429
  private Dictionary<
1437
1430
  int,
1438
- Dictionary<Action<InstanceId, T>, int>
1431
+ HandlerActionCache<Action<InstanceId, T>>
1439
1432
  > _broadcastWithoutSourceHandlers;
1440
1433
  private Dictionary<
1441
1434
  int,
1442
- Dictionary<FastHandlerWithContext<T>, int>
1435
+ HandlerActionCache<FastHandlerWithContext<T>>
1443
1436
  > _fastBroadcastWithoutSourceHandlers;
1444
1437
  private Dictionary<
1445
1438
  int,
1446
- Dictionary<Action<InstanceId, T>, int>
1439
+ HandlerActionCache<Action<InstanceId, T>>
1447
1440
  > _targetedWithoutTargetingPostProcessingHandlers;
1448
1441
  private Dictionary<
1449
1442
  int,
1450
- Dictionary<FastHandlerWithContext<T>, int>
1443
+ HandlerActionCache<FastHandlerWithContext<T>>
1451
1444
  > _fastTargetedWithoutTargetingPostProcessingHandlers;
1452
1445
  private Dictionary<
1453
1446
  int,
1454
- Dictionary<Action<InstanceId, T>, int>
1447
+ HandlerActionCache<Action<InstanceId, T>>
1455
1448
  > _broadcastWithoutSourcePostProcessingHandlers;
1456
1449
  private Dictionary<
1457
1450
  int,
1458
- Dictionary<FastHandlerWithContext<T>, int>
1451
+ HandlerActionCache<FastHandlerWithContext<T>>
1459
1452
  > _fastBroadcastWithoutSourcePostProcessingHandlers;
1460
1453
 
1461
1454
  /// <summary>
@@ -1500,7 +1493,6 @@
1500
1493
  {
1501
1494
  RunFastHandlers(
1502
1495
  ref target,
1503
- ref FastHandlersWithContextStack,
1504
1496
  _fastTargetedWithoutTargetingHandlers,
1505
1497
  ref message,
1506
1498
  priority
@@ -1539,7 +1531,6 @@
1539
1531
  {
1540
1532
  RunFastHandlers(
1541
1533
  ref source,
1542
- ref FastHandlersWithContextStack,
1543
1534
  _fastBroadcastWithoutSourceHandlers,
1544
1535
  ref message,
1545
1536
  priority
@@ -1553,44 +1544,18 @@
1553
1544
  /// <param name="message">Message to emit.</param>
1554
1545
  public void HandleGlobalUntargeted(ref IUntargetedMessage message)
1555
1546
  {
1556
- RunFastHandlers(
1557
- GlobalUntargetedFastHandlersStack,
1558
- _globalUntargetedFastHandlers,
1559
- ref message
1560
- );
1561
-
1562
- if (_globalUntargetedHandlers is not { Count: > 0 })
1547
+ RunFastHandlers(_globalUntargetedFastHandlers, ref message);
1548
+ if (_globalUntargetedHandlers?.handlers is not { Count: > 0 })
1563
1549
  {
1564
1550
  return;
1565
1551
  }
1566
1552
 
1567
- if (
1568
- GlobalUntargetedHandlersStack.TryPop(
1569
- out List<Action<IUntargetedMessage>> handlers
1570
- )
1571
- )
1572
- {
1573
- handlers.Clear();
1574
- foreach (Action<IUntargetedMessage> handler in _globalUntargetedHandlers.Keys)
1575
- {
1576
- handlers.Add(handler);
1577
- }
1578
- }
1579
- else
1580
- {
1581
- handlers = new List<Action<IUntargetedMessage>>(_globalUntargetedHandlers.Keys);
1582
- }
1583
-
1584
- try
1585
- {
1586
- foreach (Action<IUntargetedMessage> handler in handlers)
1587
- {
1588
- handler(message);
1589
- }
1590
- }
1591
- finally
1553
+ List<Action<IUntargetedMessage>> handlers = GetOrAddNewHandlerStack(
1554
+ _globalUntargetedHandlers
1555
+ );
1556
+ foreach (Action<IUntargetedMessage> handler in handlers)
1592
1557
  {
1593
- GlobalUntargetedHandlersStack.Push(handlers);
1558
+ handler(message);
1594
1559
  }
1595
1560
  }
1596
1561
 
@@ -1601,49 +1566,19 @@
1601
1566
  /// <param name="message">Message to emit.</param>
1602
1567
  public void HandleGlobalTargeted(ref InstanceId target, ref ITargetedMessage message)
1603
1568
  {
1604
- RunFastHandlers(
1605
- ref target,
1606
- ref GlobalTargetedFastHandlersStack,
1607
- _globalTargetedFastHandlers,
1608
- ref message
1609
- );
1569
+ RunFastHandlers(ref target, _globalTargetedFastHandlers, ref message);
1610
1570
 
1611
- if (_globalTargetedHandlers is not { Count: > 0 })
1571
+ if (_globalTargetedHandlers?.handlers is not { Count: > 0 })
1612
1572
  {
1613
1573
  return;
1614
1574
  }
1615
1575
 
1616
- if (
1617
- GlobalTargetedHandlersStack.TryPop(
1618
- out List<Action<InstanceId, ITargetedMessage>> handlers
1619
- )
1620
- )
1621
- {
1622
- handlers.Clear();
1623
- foreach (
1624
- Action<InstanceId, ITargetedMessage> handler in _globalTargetedHandlers.Keys
1625
- )
1626
- {
1627
- handlers.Add(handler);
1628
- }
1629
- }
1630
- else
1631
- {
1632
- handlers = new List<Action<InstanceId, ITargetedMessage>>(
1633
- _globalTargetedHandlers.Keys
1634
- );
1635
- }
1636
-
1637
- try
1638
- {
1639
- foreach (Action<InstanceId, ITargetedMessage> handler in handlers)
1640
- {
1641
- handler(target, message);
1642
- }
1643
- }
1644
- finally
1576
+ List<Action<InstanceId, ITargetedMessage>> handlers = GetOrAddNewHandlerStack(
1577
+ _globalTargetedHandlers
1578
+ );
1579
+ foreach (Action<InstanceId, ITargetedMessage> handler in handlers)
1645
1580
  {
1646
- GlobalTargetedHandlersStack.Push(handlers);
1581
+ handler(target, message);
1647
1582
  }
1648
1583
  }
1649
1584
 
@@ -1654,52 +1589,58 @@
1654
1589
  /// <param name="message">Message to emit.</param>
1655
1590
  public void HandleGlobalBroadcast(ref InstanceId source, ref IBroadcastMessage message)
1656
1591
  {
1657
- RunFastHandlers(
1658
- ref source,
1659
- ref GlobalBroadcastFastHandlersStack,
1660
- _globalBroadcastFastHandlers,
1661
- ref message
1662
- );
1592
+ RunFastHandlers(ref source, _globalBroadcastFastHandlers, ref message);
1663
1593
 
1664
- if (_globalBroadcastHandlers is not { Count: > 0 })
1594
+ if (_globalBroadcastHandlers?.handlers is not { Count: > 0 })
1665
1595
  {
1666
1596
  return;
1667
1597
  }
1668
1598
 
1669
- if (
1670
- GlobalBroadcastHandlersStack.TryPop(
1671
- out List<Action<InstanceId, IBroadcastMessage>> handlers
1672
- )
1673
- )
1599
+ List<Action<InstanceId, IBroadcastMessage>> handlers = GetOrAddNewHandlerStack(
1600
+ _globalBroadcastHandlers
1601
+ );
1602
+ switch (handlers.Count)
1674
1603
  {
1675
- handlers.Clear();
1676
- foreach (
1677
- Action<
1678
- InstanceId,
1679
- IBroadcastMessage
1680
- > handler in _globalBroadcastHandlers.Keys
1681
- )
1604
+ case 1:
1682
1605
  {
1683
- handlers.Add(handler);
1606
+ handlers[0](source, message);
1607
+ return;
1684
1608
  }
1685
- }
1686
- else
1687
- {
1688
- handlers = new List<Action<InstanceId, IBroadcastMessage>>(
1689
- _globalBroadcastHandlers.Keys
1690
- );
1691
- }
1692
-
1693
- try
1694
- {
1695
- foreach (Action<InstanceId, IBroadcastMessage> handler in handlers)
1609
+ case 2:
1610
+ {
1611
+ handlers[0](source, message);
1612
+ handlers[1](source, message);
1613
+ return;
1614
+ }
1615
+ case 3:
1616
+ {
1617
+ handlers[0](source, message);
1618
+ handlers[1](source, message);
1619
+ handlers[2](source, message);
1620
+ return;
1621
+ }
1622
+ case 4:
1623
+ {
1624
+ handlers[0](source, message);
1625
+ handlers[1](source, message);
1626
+ handlers[2](source, message);
1627
+ handlers[3](source, message);
1628
+ return;
1629
+ }
1630
+ case 5:
1696
1631
  {
1697
- handler(source, message);
1632
+ handlers[0](source, message);
1633
+ handlers[1](source, message);
1634
+ handlers[2](source, message);
1635
+ handlers[3](source, message);
1636
+ handlers[4](source, message);
1637
+ return;
1698
1638
  }
1699
1639
  }
1700
- finally
1640
+
1641
+ foreach (Action<InstanceId, IBroadcastMessage> handler in handlers)
1701
1642
  {
1702
- GlobalBroadcastHandlersStack.Push(handlers);
1643
+ handler(source, message);
1703
1644
  }
1704
1645
  }
1705
1646
 
@@ -2305,7 +2246,10 @@
2305
2246
 
2306
2247
  private static void RunFastHandlersWithContext<TMessage>(
2307
2248
  ref InstanceId context,
2308
- Dictionary<int, Dictionary<FastHandlerWithContext<T>, int>> fastHandlersByContext,
2249
+ Dictionary<
2250
+ int,
2251
+ HandlerActionCache<FastHandlerWithContext<T>>
2252
+ > fastHandlersByContext,
2309
2253
  ref TMessage message,
2310
2254
  int priority
2311
2255
  )
@@ -2316,20 +2260,14 @@
2316
2260
  return;
2317
2261
  }
2318
2262
 
2319
- RunFastHandlers(
2320
- ref context,
2321
- ref FastHandlersWithContextStack,
2322
- fastHandlersByContext,
2323
- ref message,
2324
- priority
2325
- );
2263
+ RunFastHandlers(ref context, fastHandlersByContext, ref message, priority);
2326
2264
  }
2327
2265
 
2328
2266
  private static void RunFastHandlersWithContext<TMessage>(
2329
2267
  ref InstanceId context,
2330
2268
  Dictionary<
2331
2269
  InstanceId,
2332
- Dictionary<int, Dictionary<FastHandler<T>, int>>
2270
+ Dictionary<int, HandlerActionCache<FastHandler<T>>>
2333
2271
  > fastHandlersByContext,
2334
2272
  ref TMessage message,
2335
2273
  int priority
@@ -2340,18 +2278,18 @@
2340
2278
  fastHandlersByContext is not { Count: > 0 }
2341
2279
  || !fastHandlersByContext.TryGetValue(
2342
2280
  context,
2343
- out Dictionary<int, Dictionary<FastHandler<T>, int>> fastHandlers
2281
+ out Dictionary<int, HandlerActionCache<FastHandler<T>>> cache
2344
2282
  )
2345
2283
  )
2346
2284
  {
2347
2285
  return;
2348
2286
  }
2349
2287
 
2350
- RunFastHandlers(fastHandlers, ref message, priority);
2288
+ RunFastHandlers(cache, ref message, priority);
2351
2289
  }
2352
2290
 
2353
2291
  private static void RunFastHandlers<TMessage>(
2354
- Dictionary<int, Dictionary<FastHandler<T>, int>> fastHandlers,
2292
+ Dictionary<int, HandlerActionCache<FastHandler<T>>> fastHandlers,
2355
2293
  ref TMessage message,
2356
2294
  int priority
2357
2295
  )
@@ -2365,7 +2303,7 @@
2365
2303
  if (
2366
2304
  !fastHandlers.TryGetValue(
2367
2305
  priority,
2368
- out Dictionary<FastHandler<T>, int> fastHandlersByPriority
2306
+ out HandlerActionCache<FastHandler<T>> cache
2369
2307
  )
2370
2308
  )
2371
2309
  {
@@ -2374,92 +2312,177 @@
2374
2312
 
2375
2313
  ref T typedMessage = ref Unsafe.As<TMessage, T>(ref message);
2376
2314
 
2377
- List<FastHandler<T>> handlers = GetOrAddNewHandlerStack(
2378
- ref FastHandlersStack,
2379
- fastHandlersByPriority.Keys
2380
- );
2381
- try
2315
+ List<FastHandler<T>> handlers = GetOrAddNewHandlerStack(cache);
2316
+
2317
+ switch (handlers.Count)
2382
2318
  {
2383
- foreach (FastHandler<T> fastHandler in handlers)
2319
+ case 1:
2384
2320
  {
2385
- fastHandler(ref typedMessage);
2321
+ handlers[0](ref typedMessage);
2322
+ return;
2323
+ }
2324
+ case 2:
2325
+ {
2326
+ handlers[0](ref typedMessage);
2327
+ handlers[1](ref typedMessage);
2328
+ return;
2329
+ }
2330
+ case 3:
2331
+ {
2332
+ handlers[0](ref typedMessage);
2333
+ handlers[1](ref typedMessage);
2334
+ handlers[2](ref typedMessage);
2335
+ return;
2336
+ }
2337
+ case 4:
2338
+ {
2339
+ handlers[0](ref typedMessage);
2340
+ handlers[1](ref typedMessage);
2341
+ handlers[2](ref typedMessage);
2342
+ handlers[3](ref typedMessage);
2343
+ return;
2344
+ }
2345
+ case 5:
2346
+ {
2347
+ handlers[0](ref typedMessage);
2348
+ handlers[1](ref typedMessage);
2349
+ handlers[2](ref typedMessage);
2350
+ handlers[3](ref typedMessage);
2351
+ handlers[4](ref typedMessage);
2352
+ return;
2386
2353
  }
2387
2354
  }
2388
- finally
2355
+
2356
+ foreach (FastHandler<T> fastHandler in handlers)
2389
2357
  {
2390
- FastHandlersStack.Push(handlers);
2358
+ fastHandler(ref typedMessage);
2391
2359
  }
2392
2360
  }
2393
2361
 
2394
2362
  private static void RunFastHandlers<TMessage, TU>(
2395
- Stack<List<FastHandler<TU>>> stack,
2396
- Dictionary<FastHandler<TU>, int> fastHandlers,
2363
+ HandlerActionCache<FastHandler<TU>> cache,
2397
2364
  ref TMessage message
2398
2365
  )
2399
2366
  where TMessage : IMessage
2400
2367
  where TU : IMessage
2401
2368
  {
2402
- if (fastHandlers is not { Count: > 0 })
2369
+ if (cache?.handlers is not { Count: > 0 })
2403
2370
  {
2404
2371
  return;
2405
2372
  }
2406
2373
 
2407
2374
  ref TU typedMessage = ref Unsafe.As<TMessage, TU>(ref message);
2408
2375
 
2409
- List<FastHandler<TU>> handlers = GetOrAddNewHandlerStack(
2410
- ref stack,
2411
- fastHandlers.Keys
2412
- );
2413
- try
2376
+ List<FastHandler<TU>> handlers = GetOrAddNewHandlerStack(cache);
2377
+ switch (handlers.Count)
2414
2378
  {
2415
- foreach (FastHandler<TU> fastHandler in handlers)
2379
+ case 1:
2380
+ {
2381
+ handlers[0](ref typedMessage);
2382
+ return;
2383
+ }
2384
+ case 2:
2385
+ {
2386
+ handlers[0](ref typedMessage);
2387
+ handlers[1](ref typedMessage);
2388
+ return;
2389
+ }
2390
+ case 3:
2391
+ {
2392
+ handlers[0](ref typedMessage);
2393
+ handlers[1](ref typedMessage);
2394
+ handlers[2](ref typedMessage);
2395
+ return;
2396
+ }
2397
+ case 4:
2398
+ {
2399
+ handlers[0](ref typedMessage);
2400
+ handlers[1](ref typedMessage);
2401
+ handlers[2](ref typedMessage);
2402
+ handlers[3](ref typedMessage);
2403
+ return;
2404
+ }
2405
+ case 5:
2416
2406
  {
2417
- fastHandler(ref typedMessage);
2407
+ handlers[0](ref typedMessage);
2408
+ handlers[1](ref typedMessage);
2409
+ handlers[2](ref typedMessage);
2410
+ handlers[3](ref typedMessage);
2411
+ handlers[4](ref typedMessage);
2412
+ return;
2418
2413
  }
2419
2414
  }
2420
- finally
2415
+
2416
+ foreach (FastHandler<TU> fastHandler in handlers)
2421
2417
  {
2422
- stack.Push(handlers);
2418
+ fastHandler(ref typedMessage);
2423
2419
  }
2424
2420
  }
2425
2421
 
2426
2422
  private static void RunFastHandlers<TMessage, TU>(
2427
2423
  ref InstanceId context,
2428
- ref Stack<List<FastHandlerWithContext<TU>>> stack,
2429
- Dictionary<FastHandlerWithContext<TU>, int> priorityHandlers,
2424
+ HandlerActionCache<FastHandlerWithContext<TU>> cache,
2430
2425
  ref TMessage message
2431
2426
  )
2432
2427
  where TMessage : IMessage
2433
2428
  where TU : IMessage
2434
2429
  {
2435
- if (priorityHandlers is not { Count: > 0 })
2430
+ if (cache?.handlers is not { Count: > 0 })
2436
2431
  {
2437
2432
  return;
2438
2433
  }
2439
2434
 
2440
2435
  ref TU typedMessage = ref Unsafe.As<TMessage, TU>(ref message);
2441
2436
 
2442
- List<FastHandlerWithContext<TU>> handlers = GetOrAddNewHandlerStack(
2443
- ref stack,
2444
- priorityHandlers.Keys
2445
- );
2446
- try
2437
+ List<FastHandlerWithContext<TU>> handlers = GetOrAddNewHandlerStack(cache);
2438
+ switch (handlers.Count)
2447
2439
  {
2448
- foreach (FastHandlerWithContext<TU> fastHandler in handlers)
2440
+ case 1:
2441
+ {
2442
+ handlers[0](ref context, ref typedMessage);
2443
+ return;
2444
+ }
2445
+ case 2:
2446
+ {
2447
+ handlers[0](ref context, ref typedMessage);
2448
+ handlers[1](ref context, ref typedMessage);
2449
+ return;
2450
+ }
2451
+ case 3:
2452
+ {
2453
+ handlers[0](ref context, ref typedMessage);
2454
+ handlers[1](ref context, ref typedMessage);
2455
+ handlers[2](ref context, ref typedMessage);
2456
+ return;
2457
+ }
2458
+ case 4:
2459
+ {
2460
+ handlers[0](ref context, ref typedMessage);
2461
+ handlers[1](ref context, ref typedMessage);
2462
+ handlers[2](ref context, ref typedMessage);
2463
+ handlers[3](ref context, ref typedMessage);
2464
+ return;
2465
+ }
2466
+ case 5:
2449
2467
  {
2450
- fastHandler(ref context, ref typedMessage);
2468
+ handlers[0](ref context, ref typedMessage);
2469
+ handlers[1](ref context, ref typedMessage);
2470
+ handlers[2](ref context, ref typedMessage);
2471
+ handlers[3](ref context, ref typedMessage);
2472
+ handlers[4](ref context, ref typedMessage);
2473
+ return;
2451
2474
  }
2452
2475
  }
2453
- finally
2476
+
2477
+ foreach (FastHandlerWithContext<TU> fastHandler in handlers)
2454
2478
  {
2455
- stack.Push(handlers);
2479
+ fastHandler(ref context, ref typedMessage);
2456
2480
  }
2457
2481
  }
2458
2482
 
2459
2483
  private static void RunFastHandlers<TMessage, TU>(
2460
2484
  ref InstanceId context,
2461
- ref Stack<List<FastHandlerWithContext<TU>>> stack,
2462
- Dictionary<int, Dictionary<FastHandlerWithContext<TU>, int>> fastHandlers,
2485
+ Dictionary<int, HandlerActionCache<FastHandlerWithContext<TU>>> fastHandlers,
2463
2486
  ref TMessage message,
2464
2487
  int priority
2465
2488
  )
@@ -2474,7 +2497,7 @@
2474
2497
  if (
2475
2498
  !fastHandlers.TryGetValue(
2476
2499
  priority,
2477
- out Dictionary<FastHandlerWithContext<TU>, int> priorityHandlers
2500
+ out HandlerActionCache<FastHandlerWithContext<TU>> cache
2478
2501
  )
2479
2502
  )
2480
2503
  {
@@ -2483,20 +2506,49 @@
2483
2506
 
2484
2507
  ref TU typedMessage = ref Unsafe.As<TMessage, TU>(ref message);
2485
2508
 
2486
- List<FastHandlerWithContext<TU>> handlers = GetOrAddNewHandlerStack(
2487
- ref stack,
2488
- priorityHandlers.Keys
2489
- );
2490
- try
2509
+ List<FastHandlerWithContext<TU>> handlers = GetOrAddNewHandlerStack(cache);
2510
+ switch (handlers.Count)
2491
2511
  {
2492
- foreach (FastHandlerWithContext<TU> fastHandler in handlers)
2512
+ case 1:
2513
+ {
2514
+ handlers[0](ref context, ref typedMessage);
2515
+ return;
2516
+ }
2517
+ case 2:
2518
+ {
2519
+ handlers[0](ref context, ref typedMessage);
2520
+ handlers[1](ref context, ref typedMessage);
2521
+ return;
2522
+ }
2523
+ case 3:
2524
+ {
2525
+ handlers[0](ref context, ref typedMessage);
2526
+ handlers[1](ref context, ref typedMessage);
2527
+ handlers[2](ref context, ref typedMessage);
2528
+ return;
2529
+ }
2530
+ case 4:
2531
+ {
2532
+ handlers[0](ref context, ref typedMessage);
2533
+ handlers[1](ref context, ref typedMessage);
2534
+ handlers[2](ref context, ref typedMessage);
2535
+ handlers[3](ref context, ref typedMessage);
2536
+ return;
2537
+ }
2538
+ case 5:
2493
2539
  {
2494
- fastHandler(ref context, ref typedMessage);
2540
+ handlers[0](ref context, ref typedMessage);
2541
+ handlers[1](ref context, ref typedMessage);
2542
+ handlers[2](ref context, ref typedMessage);
2543
+ handlers[3](ref context, ref typedMessage);
2544
+ handlers[4](ref context, ref typedMessage);
2545
+ return;
2495
2546
  }
2496
2547
  }
2497
- finally
2548
+
2549
+ foreach (FastHandlerWithContext<TU> fastHandler in handlers)
2498
2550
  {
2499
- stack.Push(handlers);
2551
+ fastHandler(ref context, ref typedMessage);
2500
2552
  }
2501
2553
  }
2502
2554
 
@@ -2504,7 +2556,7 @@
2504
2556
  ref InstanceId context,
2505
2557
  Dictionary<
2506
2558
  InstanceId,
2507
- Dictionary<int, Dictionary<Action<T>, int>>
2559
+ Dictionary<int, HandlerActionCache<Action<T>>>
2508
2560
  > handlersByContext,
2509
2561
  ref TMessage message,
2510
2562
  int priority
@@ -2515,18 +2567,18 @@
2515
2567
  handlersByContext is not { Count: > 0 }
2516
2568
  || !handlersByContext.TryGetValue(
2517
2569
  context,
2518
- out Dictionary<int, Dictionary<Action<T>, int>> handlers
2570
+ out Dictionary<int, HandlerActionCache<Action<T>>> cache
2519
2571
  )
2520
2572
  )
2521
2573
  {
2522
2574
  return;
2523
2575
  }
2524
2576
 
2525
- RunHandlers(handlers, ref message, priority);
2577
+ RunHandlers(cache, ref message, priority);
2526
2578
  }
2527
2579
 
2528
2580
  private static void RunHandlers<TMessage>(
2529
- Dictionary<int, Dictionary<Action<T>, int>> sortedHandlers,
2581
+ Dictionary<int, HandlerActionCache<Action<T>>> sortedHandlers,
2530
2582
  ref TMessage message,
2531
2583
  int priority
2532
2584
  )
@@ -2537,33 +2589,62 @@
2537
2589
  return;
2538
2590
  }
2539
2591
 
2540
- if (!sortedHandlers.TryGetValue(priority, out Dictionary<Action<T>, int> handlers))
2592
+ if (!sortedHandlers.TryGetValue(priority, out HandlerActionCache<Action<T>> cache))
2541
2593
  {
2542
2594
  return;
2543
2595
  }
2544
2596
 
2545
- List<Action<T>> typedHandlers = GetOrAddNewHandlerStack(
2546
- ref HandlersStack,
2547
- handlers.Keys
2548
- );
2549
- try
2550
- {
2551
- ref T typedMessage = ref Unsafe.As<TMessage, T>(ref message);
2597
+ List<Action<T>> handlers = GetOrAddNewHandlerStack(cache);
2598
+ ref T typedMessage = ref Unsafe.As<TMessage, T>(ref message);
2552
2599
 
2553
- foreach (Action<T> handler in typedHandlers)
2600
+ switch (handlers.Count)
2601
+ {
2602
+ case 1:
2603
+ {
2604
+ handlers[0](typedMessage);
2605
+ return;
2606
+ }
2607
+ case 2:
2608
+ {
2609
+ handlers[0](typedMessage);
2610
+ handlers[1](typedMessage);
2611
+ return;
2612
+ }
2613
+ case 3:
2614
+ {
2615
+ handlers[0](typedMessage);
2616
+ handlers[1](typedMessage);
2617
+ handlers[2](typedMessage);
2618
+ return;
2619
+ }
2620
+ case 4:
2621
+ {
2622
+ handlers[0](typedMessage);
2623
+ handlers[1](typedMessage);
2624
+ handlers[2](typedMessage);
2625
+ handlers[3](typedMessage);
2626
+ return;
2627
+ }
2628
+ case 5:
2554
2629
  {
2555
- handler(typedMessage);
2630
+ handlers[0](typedMessage);
2631
+ handlers[1](typedMessage);
2632
+ handlers[2](typedMessage);
2633
+ handlers[3](typedMessage);
2634
+ handlers[4](typedMessage);
2635
+ return;
2556
2636
  }
2557
2637
  }
2558
- finally
2638
+
2639
+ foreach (Action<T> handler in handlers)
2559
2640
  {
2560
- HandlersStack.Push(typedHandlers);
2641
+ handler(typedMessage);
2561
2642
  }
2562
2643
  }
2563
2644
 
2564
2645
  private static void RunHandlers<TMessage>(
2565
2646
  ref InstanceId context,
2566
- Dictionary<int, Dictionary<Action<InstanceId, T>, int>> handlers,
2647
+ Dictionary<int, HandlerActionCache<Action<InstanceId, T>>> handlers,
2567
2648
  ref TMessage message,
2568
2649
  int priority
2569
2650
  )
@@ -2577,100 +2658,135 @@
2577
2658
  if (
2578
2659
  !handlers.TryGetValue(
2579
2660
  priority,
2580
- out Dictionary<Action<InstanceId, T>, int> handlersByPriority
2661
+ out HandlerActionCache<Action<InstanceId, T>> cache
2581
2662
  )
2582
2663
  )
2583
2664
  {
2584
2665
  return;
2585
2666
  }
2586
2667
 
2587
- List<Action<InstanceId, T>> typedHandlers = GetOrAddNewHandlerStack(
2588
- ref HandlersWithoutContextStack,
2589
- handlersByPriority.Keys
2590
- );
2591
- try
2592
- {
2593
- ref T typedMessage = ref Unsafe.As<TMessage, T>(ref message);
2668
+ List<Action<InstanceId, T>> typedHandlers = GetOrAddNewHandlerStack(cache);
2669
+ ref T typedMessage = ref Unsafe.As<TMessage, T>(ref message);
2594
2670
 
2595
- foreach (Action<InstanceId, T> handler in typedHandlers)
2671
+ switch (typedHandlers.Count)
2672
+ {
2673
+ case 1:
2674
+ {
2675
+ typedHandlers[0](context, typedMessage);
2676
+ return;
2677
+ }
2678
+ case 2:
2596
2679
  {
2597
- handler(context, typedMessage);
2680
+ typedHandlers[0](context, typedMessage);
2681
+ typedHandlers[1](context, typedMessage);
2682
+ return;
2683
+ }
2684
+ case 3:
2685
+ {
2686
+ typedHandlers[0](context, typedMessage);
2687
+ typedHandlers[1](context, typedMessage);
2688
+ typedHandlers[2](context, typedMessage);
2689
+ return;
2690
+ }
2691
+ case 4:
2692
+ {
2693
+ typedHandlers[0](context, typedMessage);
2694
+ typedHandlers[1](context, typedMessage);
2695
+ typedHandlers[2](context, typedMessage);
2696
+ typedHandlers[3](context, typedMessage);
2697
+ return;
2698
+ }
2699
+ case 5:
2700
+ {
2701
+ typedHandlers[0](context, typedMessage);
2702
+ typedHandlers[1](context, typedMessage);
2703
+ typedHandlers[2](context, typedMessage);
2704
+ typedHandlers[3](context, typedMessage);
2705
+ typedHandlers[4](context, typedMessage);
2706
+ return;
2598
2707
  }
2599
2708
  }
2600
- finally
2709
+
2710
+ foreach (Action<InstanceId, T> handler in typedHandlers)
2601
2711
  {
2602
- HandlersWithoutContextStack.Push(typedHandlers);
2712
+ handler(context, typedMessage);
2603
2713
  }
2604
2714
  }
2605
2715
 
2606
- private static List<TU> GetOrAddNewHandlerStack<TU>(
2607
- ref Stack<List<TU>> stack,
2608
- Dictionary<TU, int>.KeyCollection handlers
2609
- )
2716
+ private static List<TU> GetOrAddNewHandlerStack<TU>(HandlerActionCache<TU> actionCache)
2610
2717
  {
2611
- stack ??= new Stack<List<TU>>();
2612
- if (!stack.TryPop(out List<TU> typedHandlerStack))
2718
+ if (actionCache.version == actionCache.lastSeenVersion)
2613
2719
  {
2614
- return new List<TU>(handlers);
2720
+ return actionCache.cache;
2615
2721
  }
2616
2722
 
2617
- typedHandlerStack.Clear();
2618
- foreach (TU handler in handlers)
2723
+ List<TU> cache = actionCache.cache;
2724
+ cache.Clear();
2725
+ foreach (TU handler in actionCache.handlers.Keys)
2619
2726
  {
2620
- typedHandlerStack.Add(handler);
2727
+ cache.Add(handler);
2621
2728
  }
2622
- return typedHandlerStack;
2729
+ actionCache.lastSeenVersion = actionCache.version;
2730
+ return cache;
2623
2731
  }
2624
2732
 
2625
2733
  private static Action AddHandler<TU>(
2626
2734
  InstanceId context,
2627
- ref Dictionary<InstanceId, Dictionary<int, Dictionary<TU, int>>> handlersByContext,
2735
+ ref Dictionary<
2736
+ InstanceId,
2737
+ Dictionary<int, HandlerActionCache<TU>>
2738
+ > handlersByContext,
2628
2739
  TU handler,
2629
2740
  Action deregistration,
2630
2741
  int priority
2631
2742
  )
2632
2743
  {
2633
2744
  handlersByContext ??=
2634
- new Dictionary<InstanceId, Dictionary<int, Dictionary<TU, int>>>();
2745
+ new Dictionary<InstanceId, Dictionary<int, HandlerActionCache<TU>>>();
2635
2746
 
2636
2747
  if (
2637
2748
  !handlersByContext.TryGetValue(
2638
2749
  context,
2639
- out Dictionary<int, Dictionary<TU, int>> sortedHandlers
2750
+ out Dictionary<int, HandlerActionCache<TU>> sortedHandlers
2640
2751
  )
2641
2752
  )
2642
2753
  {
2643
- sortedHandlers = new Dictionary<int, Dictionary<TU, int>>();
2754
+ sortedHandlers = new Dictionary<int, HandlerActionCache<TU>>();
2644
2755
  handlersByContext[context] = sortedHandlers;
2645
2756
  }
2646
2757
 
2647
- if (!sortedHandlers.TryGetValue(priority, out Dictionary<TU, int> handlers))
2758
+ if (!sortedHandlers.TryGetValue(priority, out HandlerActionCache<TU> cache))
2648
2759
  {
2649
- handlers = new Dictionary<TU, int>();
2650
- sortedHandlers[priority] = handlers;
2760
+ cache = new HandlerActionCache<TU>();
2761
+ sortedHandlers[priority] = cache;
2651
2762
  }
2652
2763
 
2764
+ Dictionary<TU, int> handlers = cache.handlers;
2653
2765
  int count = handlers.GetValueOrDefault(handler, 0);
2654
2766
 
2655
2767
  handlers[handler] = count + 1;
2656
2768
 
2657
2769
  Dictionary<
2658
2770
  InstanceId,
2659
- Dictionary<int, Dictionary<TU, int>>
2771
+ Dictionary<int, HandlerActionCache<TU>>
2660
2772
  > localHandlersByContext = handlersByContext;
2661
2773
 
2774
+ cache.version++;
2662
2775
  return () =>
2663
2776
  {
2777
+ cache.version++;
2664
2778
  if (!localHandlersByContext.TryGetValue(context, out sortedHandlers))
2665
2779
  {
2666
2780
  return;
2667
2781
  }
2668
2782
 
2669
- if (!sortedHandlers.TryGetValue(priority, out handlers))
2783
+ if (!sortedHandlers.TryGetValue(priority, out cache))
2670
2784
  {
2671
2785
  return;
2672
2786
  }
2673
2787
 
2788
+ handlers = cache.handlers;
2789
+
2674
2790
  if (!handlers.TryGetValue(handler, out count))
2675
2791
  {
2676
2792
  return;
@@ -2701,22 +2817,25 @@
2701
2817
  };
2702
2818
  }
2703
2819
 
2704
- private static Action AddHandler<TU>(
2705
- ref Dictionary<TU, int> handlersByPriority,
2820
+ private Action AddHandler<TU>(
2821
+ ref HandlerActionCache<TU> cache,
2706
2822
  TU handler,
2707
2823
  Action deregistration
2708
2824
  )
2709
2825
  {
2710
- handlersByPriority ??= new Dictionary<TU, int>();
2711
-
2826
+ cache ??= new HandlerActionCache<TU>();
2827
+ Dictionary<TU, int> handlersByPriority = cache.handlers;
2712
2828
  int count = handlersByPriority.GetValueOrDefault(handler, 0);
2713
2829
 
2714
2830
  handlersByPriority[handler] = count + 1;
2715
2831
 
2716
2832
  Dictionary<TU, int> localHandlers = handlersByPriority;
2717
2833
 
2834
+ HandlerActionCache<TU> localCache = cache;
2835
+ localCache.version++;
2718
2836
  return () =>
2719
2837
  {
2838
+ localCache.version++;
2720
2839
  if (!localHandlers.TryGetValue(handler, out count))
2721
2840
  {
2722
2841
  return;
@@ -2734,40 +2853,37 @@
2734
2853
  };
2735
2854
  }
2736
2855
 
2737
- private static Action AddHandler<TU>(
2738
- ref Dictionary<int, Dictionary<TU, int>> sortedHandlers,
2856
+ private Action AddHandler<TU>(
2857
+ ref Dictionary<int, HandlerActionCache<TU>> sortedHandlers,
2739
2858
  TU handler,
2740
2859
  Action deregistration,
2741
2860
  int priority
2742
2861
  )
2743
2862
  {
2744
- sortedHandlers ??= new Dictionary<int, Dictionary<TU, int>>();
2863
+ sortedHandlers ??= new Dictionary<int, HandlerActionCache<TU>>();
2745
2864
 
2746
- if (
2747
- !sortedHandlers.TryGetValue(
2748
- priority,
2749
- out Dictionary<TU, int> handlersByPriority
2750
- )
2751
- )
2865
+ if (!sortedHandlers.TryGetValue(priority, out HandlerActionCache<TU> cache))
2752
2866
  {
2753
- handlersByPriority = new Dictionary<TU, int>();
2754
- sortedHandlers[priority] = handlersByPriority;
2867
+ cache = new HandlerActionCache<TU>();
2868
+ sortedHandlers[priority] = cache;
2755
2869
  }
2756
2870
 
2757
- int count = handlersByPriority.GetValueOrDefault(handler, 0);
2871
+ int count = cache.handlers.GetValueOrDefault(handler, 0);
2758
2872
 
2759
- handlersByPriority[handler] = count + 1;
2873
+ cache.handlers[handler] = count + 1;
2760
2874
 
2761
- Dictionary<int, Dictionary<TU, int>> localSortedHandlers = sortedHandlers;
2875
+ Dictionary<int, HandlerActionCache<TU>> localSortedHandlers = sortedHandlers;
2876
+ cache.version++;
2762
2877
 
2763
2878
  return () =>
2764
2879
  {
2765
- if (!localSortedHandlers.TryGetValue(priority, out handlersByPriority))
2880
+ cache.version++;
2881
+ if (!localSortedHandlers.TryGetValue(priority, out cache))
2766
2882
  {
2767
2883
  return;
2768
2884
  }
2769
2885
 
2770
- if (!handlersByPriority.TryGetValue(handler, out count))
2886
+ if (!cache.handlers.TryGetValue(handler, out count))
2771
2887
  {
2772
2888
  return;
2773
2889
  }
@@ -2776,15 +2892,15 @@
2776
2892
  deregistration?.Invoke();
2777
2893
  if (count <= 1)
2778
2894
  {
2779
- _ = handlersByPriority.Remove(handler);
2780
- if (handlersByPriority.Count <= 0)
2895
+ _ = cache.handlers.Remove(handler);
2896
+ if (cache.handlers.Count == 0)
2781
2897
  {
2782
2898
  _ = localSortedHandlers.Remove(priority);
2783
2899
  }
2784
2900
  return;
2785
2901
  }
2786
2902
 
2787
- handlersByPriority[handler] = count - 1;
2903
+ cache.handlers[handler] = count - 1;
2788
2904
  };
2789
2905
  }
2790
2906
  }