feat(sdk): migrate to EventBus 2.0
Replace the legacy EventBase runtime with generated multi-bus bindings and explicit scheduling. Migrate app, data, network, demo, and mod consumers; add generated network-event registration and owner-scoped mod content overrides.
This commit is contained in:
@@ -1,200 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ShrinkEventBus.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class EventBusDispatchTests
|
||||
{
|
||||
private class PlainEvent : EventBase
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class DerivedPlainEvent : PlainEvent
|
||||
{
|
||||
}
|
||||
|
||||
[Cancelable]
|
||||
private sealed class CancelableEvent : EventBase
|
||||
{
|
||||
}
|
||||
|
||||
[HasResult]
|
||||
private sealed class ResultEvent : EventBase
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TriggerEvent_ReturnsFalseWithoutSubscribers_TrueWhenHandled()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
|
||||
Assert.IsFalse(bus.TriggerEvent(new PlainEvent()));
|
||||
|
||||
bus.RegisterEvent<PlainEvent>(_ => { }, EventPriority.NORMAL);
|
||||
Assert.IsTrue(bus.TriggerEvent(new PlainEvent()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanceledEvent_SkipsHandlersUnlessReceiveCanceled()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var order = new List<string>();
|
||||
|
||||
bus.RegisterEvent<CancelableEvent>(evt =>
|
||||
{
|
||||
order.Add("canceler");
|
||||
evt.SetCanceled(true);
|
||||
}, EventPriority.HIGHEST);
|
||||
bus.RegisterEvent<CancelableEvent>(_ => order.Add("skipped"), EventPriority.NORMAL);
|
||||
bus.RegisterEvent<CancelableEvent>(evt => order.Add($"monitor:{evt.IsCanceled}"),
|
||||
EventPriority.MONITOR, receiveCanceled: true);
|
||||
|
||||
var evt = new CancelableEvent();
|
||||
bus.TriggerEvent(evt);
|
||||
|
||||
Assert.IsTrue(evt.IsCanceled);
|
||||
CollectionAssert.AreEqual(new[] { "canceler", "monitor:True" }, order);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetCanceled_OnNonCancelableEvent_ThrowsWithMessage()
|
||||
{
|
||||
var evt = new PlainEvent();
|
||||
|
||||
var ex = Assert.Throws<UnsupportedOperationException>(() => evt.SetCanceled(true));
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(ex.Message));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetResult_OnEventWithoutResult_ThrowsWithMessage()
|
||||
{
|
||||
var evt = new PlainEvent();
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => evt.SetResult(EventResult.ALLOW));
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(ex.Message));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetResult_OnResultEvent_PersistsResult()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
bus.RegisterEvent<ResultEvent>(evt => evt.SetResult(EventResult.DENY), EventPriority.HIGH);
|
||||
|
||||
var evt = new ResultEvent();
|
||||
bus.TriggerEvent(evt);
|
||||
|
||||
Assert.AreEqual(EventResult.DENY, evt.Result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentSubscriber_ReceivesDerivedEvent()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var hits = 0;
|
||||
bus.RegisterEvent<PlainEvent>(_ => hits++, EventPriority.NORMAL);
|
||||
|
||||
bus.TriggerEvent(new DerivedPlainEvent());
|
||||
|
||||
Assert.AreEqual(1, hits);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentSubscriber_RegisteredAfterChildWasTriggered_StillReceivesChild()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
|
||||
// 先触发子事件,让子类型的监听列表先于父监听器创建(覆盖脏传播路径)
|
||||
bus.TriggerEvent(new DerivedPlainEvent());
|
||||
|
||||
var hits = 0;
|
||||
bus.RegisterEvent<PlainEvent>(_ => hits++, EventPriority.NORMAL);
|
||||
bus.TriggerEvent(new DerivedPlainEvent());
|
||||
|
||||
Assert.AreEqual(1, hits);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PerPhaseDispatch_OnlyRunsRequestedPhase()
|
||||
{
|
||||
var bus = EventBus.CreateBus(builder => builder.AllowPerPhaseDispatch());
|
||||
var order = new List<string>();
|
||||
|
||||
bus.RegisterEvent<PlainEvent>(_ => order.Add("high"), EventPriority.HIGH);
|
||||
bus.RegisterEvent<PlainEvent>(_ => order.Add("normal"), EventPriority.NORMAL);
|
||||
|
||||
bus.TriggerEvent(EventPriority.HIGH, new PlainEvent());
|
||||
|
||||
CollectionAssert.AreEqual(new[] { "high" }, order);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PerPhaseDispatch_ThrowsWhenNotEnabled()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => bus.TriggerEvent(EventPriority.HIGH, new PlainEvent()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TriggerEventAsync_RunsHandlersSequentiallyByPriority()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var order = new List<string>();
|
||||
|
||||
async UniTask AsyncHandler(PlainEvent evt)
|
||||
{
|
||||
order.Add("async-high");
|
||||
await UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
bus.RegisterEvent<PlainEvent>(AsyncHandler, EventPriority.HIGH, receiveCanceled: false);
|
||||
bus.RegisterEvent<PlainEvent>(_ => order.Add("sync-normal"), EventPriority.NORMAL);
|
||||
|
||||
var handled = bus.TriggerEventAsync(new PlainEvent()).GetAwaiter().GetResult();
|
||||
|
||||
Assert.IsTrue(handled);
|
||||
CollectionAssert.AreEqual(new[] { "async-high", "sync-normal" }, order);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TriggerEventAsync_AsyncHandlerCancellation_SkipsLaterHandlers()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var order = new List<string>();
|
||||
|
||||
async UniTask CancelingHandler(CancelableEvent evt)
|
||||
{
|
||||
evt.SetCanceled(true);
|
||||
order.Add("async-canceler");
|
||||
await UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
bus.RegisterEvent<CancelableEvent>(CancelingHandler, EventPriority.HIGHEST, receiveCanceled: false);
|
||||
bus.RegisterEvent<CancelableEvent>(_ => order.Add("skipped"), EventPriority.NORMAL);
|
||||
|
||||
bus.TriggerEventAsync(new CancelableEvent()).GetAwaiter().GetResult();
|
||||
|
||||
CollectionAssert.AreEqual(new[] { "async-canceler" }, order);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetSubscribers_ReturnsDefensiveCopyOfDispatchSnapshot()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
bus.RegisterEvent<PlainEvent>(_ => { }, EventPriority.NORMAL);
|
||||
|
||||
var evt = new PlainEvent();
|
||||
bus.TriggerEvent(evt);
|
||||
|
||||
var first = evt.GetSubscribers();
|
||||
Assert.AreEqual(1, first.Length);
|
||||
|
||||
first[0] = null;
|
||||
var second = evt.GetSubscribers();
|
||||
Assert.IsNotNull(second[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11b7dddad0d215b47a7249e74a574105
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,120 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ShrinkEventBus.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class EventBusPriorityTests
|
||||
{
|
||||
private class BaseEvent : EventBase
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class DerivedEvent : BaseEvent
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnumPriority_ExecutesPhasesInOrder()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var order = new List<string>();
|
||||
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("LOW"), EventPriority.LOW);
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("MONITOR"), EventPriority.MONITOR);
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("HIGHEST"), EventPriority.HIGHEST);
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("NORMAL"), EventPriority.NORMAL);
|
||||
|
||||
bus.TriggerEvent(new BaseEvent());
|
||||
|
||||
CollectionAssert.AreEqual(new[] { "HIGHEST", "NORMAL", "LOW", "MONITOR" }, order);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NumericPriority_HigherNumberRunsFirstWithinPhase()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var order = new List<string>();
|
||||
|
||||
// 5 与 10 都映射到 NORMAL 档
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("p5"), 5);
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("p10"), 10);
|
||||
|
||||
bus.TriggerEvent(new BaseEvent());
|
||||
|
||||
CollectionAssert.AreEqual(new[] { "p10", "p5" }, order);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NumericPriority_TieKeepsRegistrationOrder()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var order = new List<string>();
|
||||
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("first"), 0);
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("second"), 0);
|
||||
|
||||
bus.TriggerEvent(new BaseEvent());
|
||||
|
||||
CollectionAssert.AreEqual(new[] { "first", "second" }, order);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NumericPriorityZero_MapsToNormalPhase()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
bus.RegisterEvent<BaseEvent>(_ => { }, 0);
|
||||
|
||||
var subscribers = bus.GetEventSubscribers<BaseEvent>();
|
||||
|
||||
Assert.AreEqual(1, subscribers.Length);
|
||||
Assert.AreEqual(EventPriority.NORMAL, subscribers[0].Priority);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RegisterEvent_WithoutPriority_ResolvesToNormal()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
|
||||
void Handler(BaseEvent evt)
|
||||
{
|
||||
}
|
||||
|
||||
// 不带优先级的调用应唯一解析到枚举重载(NORMAL),不再有重载二义性
|
||||
bus.RegisterEvent<BaseEvent>(Handler);
|
||||
|
||||
var subscribers = bus.GetEventSubscribers<BaseEvent>();
|
||||
Assert.AreEqual(1, subscribers.Length);
|
||||
Assert.AreEqual(EventPriority.NORMAL, subscribers[0].Priority);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentAndChildHandlers_MergeByNumericPriorityWithinPhase()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var order = new List<string>();
|
||||
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("parent10"), 10);
|
||||
bus.RegisterEvent<DerivedEvent>(_ => order.Add("child5"), 5);
|
||||
bus.RegisterEvent<DerivedEvent>(_ => order.Add("child1"), 1);
|
||||
|
||||
bus.TriggerEvent(new DerivedEvent());
|
||||
|
||||
CollectionAssert.AreEqual(new[] { "parent10", "child5", "child1" }, order);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentAndChildHandlers_TiePrefersChild()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var order = new List<string>();
|
||||
|
||||
bus.RegisterEvent<BaseEvent>(_ => order.Add("parent0"), 0);
|
||||
bus.RegisterEvent<DerivedEvent>(_ => order.Add("child0"), 0);
|
||||
|
||||
bus.TriggerEvent(new DerivedEvent());
|
||||
|
||||
CollectionAssert.AreEqual(new[] { "child0", "parent0" }, order);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc534f0e8598cab47b393ccffc90ad98
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,228 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace ShrinkEventBus.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class EventBusRegistrationTests
|
||||
{
|
||||
private class ProbeEvent : EventBase
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class PooledEvent : EventBase
|
||||
{
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override void OnReset()
|
||||
{
|
||||
Value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class InstanceSubscriber
|
||||
{
|
||||
public int Hits;
|
||||
|
||||
[EventSubscribe(EventPriority.NORMAL)]
|
||||
private void OnProbe(ProbeEvent evt)
|
||||
{
|
||||
Hits++;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StaticSubscriber
|
||||
{
|
||||
public static int Hits;
|
||||
|
||||
[EventSubscribe(EventPriority.NORMAL)]
|
||||
private static void OnProbe(ProbeEvent evt)
|
||||
{
|
||||
Hits++;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class InvalidSignatureSubscriber
|
||||
{
|
||||
[EventSubscribe(EventPriority.NORMAL)]
|
||||
private void OnProbe(ProbeEvent evt, int extra)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[EventBusSubscriber]
|
||||
private sealed class EmptySubscriber
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class UnattributedSubscriber
|
||||
{
|
||||
[EventSubscribe(EventPriority.NORMAL)]
|
||||
private void OnProbe(ProbeEvent evt)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Register_InstanceScan_RegistersAndUnregisters()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var subscriber = new InstanceSubscriber();
|
||||
|
||||
bus.Register(subscriber);
|
||||
Assert.IsTrue(bus.IsInstanceRegistered(subscriber));
|
||||
bus.TriggerEvent(new ProbeEvent());
|
||||
Assert.AreEqual(1, subscriber.Hits);
|
||||
|
||||
bus.Unregister(subscriber);
|
||||
Assert.IsFalse(bus.IsInstanceRegistered(subscriber));
|
||||
bus.TriggerEvent(new ProbeEvent());
|
||||
Assert.AreEqual(1, subscriber.Hits);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Register_StaticTypeScan_RegistersAndUnregisters()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
StaticSubscriber.Hits = 0;
|
||||
|
||||
bus.Register(typeof(StaticSubscriber));
|
||||
bus.TriggerEvent(new ProbeEvent());
|
||||
Assert.AreEqual(1, StaticSubscriber.Hits);
|
||||
|
||||
bus.Unregister(typeof(StaticSubscriber));
|
||||
bus.TriggerEvent(new ProbeEvent());
|
||||
Assert.AreEqual(1, StaticSubscriber.Hits);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Register_MethodInfo_RegistersSingleHandler()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
StaticSubscriber.Hits = 0;
|
||||
var method = typeof(StaticSubscriber).GetMethod("OnProbe",
|
||||
BindingFlags.Static | BindingFlags.NonPublic);
|
||||
Assert.IsNotNull(method);
|
||||
|
||||
bus.Register(method);
|
||||
bus.TriggerEvent(new ProbeEvent());
|
||||
Assert.AreEqual(1, StaticSubscriber.Hits);
|
||||
|
||||
bus.Unregister(method);
|
||||
bus.TriggerEvent(new ProbeEvent());
|
||||
Assert.AreEqual(1, StaticSubscriber.Hits);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Register_InvalidHandlerSignature_Throws()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => bus.Register(new InvalidSignatureSubscriber()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AutoRegister_TypeWithoutSubscriberAttribute_Throws()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => bus.AutoRegister(new UnattributedSubscriber()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AutoRegister_SubscriberWithoutHandlers_WarnsInsteadOfThrowing()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
|
||||
LogAssert.Expect(LogType.Warning, new Regex(@"has no \[EventSubscribe\] methods"));
|
||||
Assert.DoesNotThrow(() => bus.AutoRegister(new EmptySubscriber()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetEventSubscribers_ReturnsDefensiveCopy()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
bus.RegisterEvent<ProbeEvent>(_ => { }, EventPriority.NORMAL);
|
||||
|
||||
var first = bus.GetEventSubscribers<ProbeEvent>();
|
||||
Assert.AreEqual(1, first.Length);
|
||||
|
||||
first[0] = null;
|
||||
var second = bus.GetEventSubscribers<ProbeEvent>();
|
||||
Assert.IsNotNull(second[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnregisterAllEventsForObject_RemovesScannedHandlers()
|
||||
{
|
||||
var bus = EventBus.CreateBus();
|
||||
var subscriber = new InstanceSubscriber();
|
||||
bus.Register(subscriber);
|
||||
|
||||
bus.UnregisterAllEventsForObject(subscriber);
|
||||
Assert.IsFalse(bus.IsInstanceRegistered(subscriber));
|
||||
bus.TriggerEvent(new ProbeEvent());
|
||||
|
||||
Assert.AreEqual(0, subscriber.Hits);
|
||||
|
||||
bus.Register(subscriber);
|
||||
bus.TriggerEvent(new ProbeEvent());
|
||||
|
||||
Assert.AreEqual(1, subscriber.Hits);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EventPool_ReleaseResetsAndReusesInstance()
|
||||
{
|
||||
var evt = EventPool<PooledEvent>.Get();
|
||||
evt.Value = 42;
|
||||
|
||||
EventPool<PooledEvent>.Release(evt);
|
||||
var reused = EventPool<PooledEvent>.Get();
|
||||
|
||||
Assert.AreSame(evt, reused);
|
||||
Assert.AreEqual(0, reused.Value);
|
||||
|
||||
EventPool<PooledEvent>.Release(reused);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EventPool_DoubleRelease_DoesNotDuplicatePoolEntry()
|
||||
{
|
||||
var evt = EventPool<PooledEvent>.Get();
|
||||
|
||||
EventPool<PooledEvent>.Release(evt);
|
||||
EventPool<PooledEvent>.Release(evt);
|
||||
|
||||
var first = EventPool<PooledEvent>.Get();
|
||||
var second = EventPool<PooledEvent>.Get();
|
||||
|
||||
Assert.AreSame(evt, first);
|
||||
Assert.AreNotSame(evt, second);
|
||||
|
||||
EventPool<PooledEvent>.Release(first);
|
||||
EventPool<PooledEvent>.Release(second);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EventPool_DisposeReturnsToPool()
|
||||
{
|
||||
PooledEvent captured;
|
||||
using (var evt = EventPool<PooledEvent>.Get())
|
||||
{
|
||||
captured = evt;
|
||||
evt.Value = 7;
|
||||
}
|
||||
|
||||
var reused = EventPool<PooledEvent>.Get();
|
||||
Assert.AreSame(captured, reused);
|
||||
Assert.AreEqual(0, reused.Value);
|
||||
|
||||
EventPool<PooledEvent>.Release(reused);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ShrinkEventBus.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class EventBusSubscriptionTests
|
||||
{
|
||||
private sealed class SampleEvent : EventBase
|
||||
{
|
||||
public int Value { get; set; }
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
EventBus.UnregisterAllEvents();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
EventBus.UnregisterAllEvents();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscribeEvent_Dispose_RemovesOnlyOwnedSubscription()
|
||||
{
|
||||
var firstHits = 0;
|
||||
var secondHits = 0;
|
||||
|
||||
using var first = EventBus.SubscribeEvent<SampleEvent>(_ => firstHits++);
|
||||
using var second = EventBus.SubscribeEvent<SampleEvent>(_ => secondHits++);
|
||||
|
||||
EventBus.TriggerEvent(new SampleEvent());
|
||||
Assert.AreEqual(1, firstHits);
|
||||
Assert.AreEqual(1, secondHits);
|
||||
|
||||
first.Dispose();
|
||||
|
||||
EventBus.TriggerEvent(new SampleEvent());
|
||||
Assert.AreEqual(1, firstHits);
|
||||
Assert.AreEqual(2, secondHits);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ActiveSubscriptionSnapshot_ContainsExpectedMetadata()
|
||||
{
|
||||
using var subscription = EventBus.SubscribeEvent<SampleEvent>(_ => { }, EventPriority.HIGH, true);
|
||||
|
||||
var snapshots = EventBus.GetActiveSubscriptionsSnapshot();
|
||||
|
||||
Assert.AreEqual(1, snapshots.Count);
|
||||
Assert.AreEqual(subscription.SubscriptionId, snapshots[0].SubscriptionId);
|
||||
Assert.AreEqual(typeof(SampleEvent), snapshots[0].EventType);
|
||||
Assert.AreEqual(EventPriority.HIGH, snapshots[0].Priority);
|
||||
Assert.IsTrue(snapshots[0].ReceiveCanceled);
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(snapshots[0].MethodName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RegisterEvent_StillWorksWithoutDisposableSubscription()
|
||||
{
|
||||
var hits = 0;
|
||||
|
||||
void Handler(SampleEvent evt)
|
||||
{
|
||||
hits += evt.Value;
|
||||
}
|
||||
|
||||
EventBus.RegisterEvent<SampleEvent>(Handler);
|
||||
EventBus.TriggerEvent(new SampleEvent { Value = 3 });
|
||||
|
||||
Assert.AreEqual(3, hits);
|
||||
|
||||
EventBus.UnregisterEvent<SampleEvent>(Handler);
|
||||
EventBus.TriggerEvent(new SampleEvent { Value = 5 });
|
||||
|
||||
Assert.AreEqual(3, hits);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ActiveSubscriptionSnapshot_EmptyAfterDispose()
|
||||
{
|
||||
var subscription = EventBus.SubscribeEvent<SampleEvent>(_ => Debug.Log("noop"));
|
||||
subscription.Dispose();
|
||||
|
||||
var snapshots = EventBus.GetActiveSubscriptionsSnapshot();
|
||||
|
||||
Assert.AreEqual(0, snapshots.Count);
|
||||
Assert.IsTrue(subscription.IsDisposed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5ea531619ceaeb46af4f56454624ef7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,440 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace ShrinkEventBus.Tests
|
||||
{
|
||||
internal readonly struct GeneratedPingEvent : IShrinkEvent
|
||||
{
|
||||
public GeneratedPingEvent(int value) => Value = value;
|
||||
public int Value { get; }
|
||||
}
|
||||
|
||||
[ShrinkEventSubscriber(DefaultBus = "game")]
|
||||
internal sealed class CodeGeneratedTarget
|
||||
{
|
||||
public int Sum { get; private set; }
|
||||
|
||||
[ShrinkSubscribe]
|
||||
private void OnPing(GeneratedPingEvent evt) => Sum += evt.Value;
|
||||
}
|
||||
|
||||
[ShrinkEventSubscriber(DefaultBus = "game")]
|
||||
internal static class StaticCodeGeneratedTarget
|
||||
{
|
||||
public static int Sum { get; set; }
|
||||
|
||||
[ShrinkSubscribe]
|
||||
private static void OnPing(GeneratedPingEvent evt) => Sum += evt.Value;
|
||||
}
|
||||
|
||||
[ShrinkEventSubscriber(DefaultBus = "mod:late-static")]
|
||||
internal static class DelayedStaticCodeGeneratedTarget
|
||||
{
|
||||
public static int Sum { get; set; }
|
||||
|
||||
[ShrinkSubscribe]
|
||||
private static void OnPing(GeneratedPingEvent evt) => Sum += evt.Value;
|
||||
}
|
||||
|
||||
[ShrinkEventSubscriber(DefaultBus = "game")]
|
||||
internal sealed class MonoCodeGeneratedTarget : MonoBehaviour
|
||||
{
|
||||
public int Sum { get; private set; }
|
||||
|
||||
[ShrinkSubscribe]
|
||||
private void OnPing(GeneratedPingEvent evt) => Sum += evt.Value;
|
||||
}
|
||||
|
||||
public sealed class ShrinkEventBusV2Tests
|
||||
{
|
||||
private readonly struct PingEvent : IShrinkEvent
|
||||
{
|
||||
public PingEvent(int value) => Value = value;
|
||||
public int Value { get; }
|
||||
}
|
||||
|
||||
private readonly struct DiagnosticEvent : IShrinkEvent
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class CancelEvent : IShrinkCancelableEvent
|
||||
{
|
||||
public bool IsCanceled { get; private set; }
|
||||
public void SetCanceled(bool value) => IsCanceled = value;
|
||||
}
|
||||
|
||||
private sealed class ResultEvent : IShrinkResultEvent<int>
|
||||
{
|
||||
public int Result { get; private set; }
|
||||
public void SetResult(int result) => Result = result;
|
||||
}
|
||||
|
||||
private sealed class GeneratedTarget : IShrinkGeneratedSubscriber
|
||||
{
|
||||
public int Sum;
|
||||
|
||||
public IDisposable AttachGenerated(IShrinkBusResolver resolver, ShrinkBusKey? defaultBus = null)
|
||||
{
|
||||
var binding = new ShrinkEventBinding();
|
||||
binding.Add(ShrinkGeneratedBinding.Subscribe<PingEvent>(resolver, defaultBus, string.Empty,
|
||||
this, OnPing, ShrinkEventPriority.Normal, 0, false));
|
||||
return binding;
|
||||
}
|
||||
|
||||
private void OnPing(PingEvent evt) => Sum += evt.Value;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultipleBusesAreIsolated()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var game = host.CreateBus(ShrinkBusKey.Game, ShrinkBusOptions.Inline());
|
||||
var mod = host.CreateBus(ShrinkBusKey.Mod("sample"), ShrinkBusOptions.Inline());
|
||||
var gameHits = 0;
|
||||
var modHits = 0;
|
||||
using var gameBinding = ShrinkGeneratedBinding.Subscribe<PingEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, evt => gameHits += evt.Value, ShrinkEventPriority.Normal, 0, false);
|
||||
using var modBinding = ShrinkGeneratedBinding.Subscribe<PingEvent>(host, ShrinkBusKey.Mod("sample"),
|
||||
string.Empty, this, evt => modHits += evt.Value, ShrinkEventPriority.Normal, 0, false);
|
||||
|
||||
game.Post(new PingEvent(2));
|
||||
mod.Post(new PingEvent(3));
|
||||
|
||||
Assert.AreEqual(2, gameHits);
|
||||
Assert.AreEqual(3, modHits);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GeneratedTargetAttachDisposesAllHandlers()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var game = host.CreateBus(ShrinkBusKey.Game, ShrinkBusOptions.Inline());
|
||||
var target = new GeneratedTarget();
|
||||
var binding = host.Attach(target, ShrinkBusKey.Game);
|
||||
|
||||
game.Post(new PingEvent(4));
|
||||
binding.Dispose();
|
||||
game.Post(new PingEvent(7));
|
||||
|
||||
Assert.AreEqual(4, target.Sum);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AttributeOnlyTargetUsesIlGeneratedBinding()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var game = host.CreateBus(ShrinkBusKey.Game, ShrinkBusOptions.Inline());
|
||||
var target = new CodeGeneratedTarget();
|
||||
|
||||
using (host.Attach(target, ShrinkBusKey.Game))
|
||||
game.Post(new GeneratedPingEvent(9));
|
||||
game.Post(new GeneratedPingEvent(4));
|
||||
|
||||
Assert.AreEqual(9, target.Sum);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StaticAttributeSubscriberUsesModuleInitializerBinding()
|
||||
{
|
||||
StaticCodeGeneratedTarget.Sum = 0;
|
||||
|
||||
EventBus.Post(new GeneratedPingEvent(11));
|
||||
|
||||
Assert.AreEqual(11, StaticCodeGeneratedTarget.Sum);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StaticSubscriberAttachesWhenNamedBusIsCreatedLater()
|
||||
{
|
||||
var key = ShrinkBusKey.Mod("late-static");
|
||||
EventBus.RemoveBus(key);
|
||||
DelayedStaticCodeGeneratedTarget.Sum = 0;
|
||||
var bus = EventBus.CreateBus(key, ShrinkBusOptions.Inline());
|
||||
|
||||
bus.Post(new GeneratedPingEvent(13));
|
||||
|
||||
Assert.AreEqual(13, DelayedStaticCodeGeneratedTarget.Sum);
|
||||
Assert.IsTrue(EventBus.RemoveBus(key));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator MonoScopeRefreshAndReleaseControlGeneratedBindings()
|
||||
{
|
||||
var gameObject = new GameObject("ShrinkEventBusV2-MonoScope");
|
||||
gameObject.SetActive(false);
|
||||
var target = gameObject.AddComponent<MonoCodeGeneratedTarget>();
|
||||
var scope = gameObject.AddComponent<ShrinkMonoEventScope>();
|
||||
Assert.IsInstanceOf<IShrinkGeneratedSubscriber>(target);
|
||||
|
||||
scope.RefreshBindings();
|
||||
yield return null;
|
||||
yield return EventBus.PostAsync(new GeneratedPingEvent(5)).ToCoroutine();
|
||||
Assert.AreEqual(5, target.Sum);
|
||||
|
||||
scope.ReleaseBindings();
|
||||
yield return EventBus.PostAsync(new GeneratedPingEvent(7)).ToCoroutine();
|
||||
Assert.AreEqual(5, target.Sum);
|
||||
UnityEngine.Object.DestroyImmediate(gameObject);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanceledEventsSkipNormalHandlersButReachMonitors()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var game = host.CreateBus(ShrinkBusKey.Game, ShrinkBusOptions.Inline());
|
||||
var normal = 0;
|
||||
var monitor = 0;
|
||||
using var first = ShrinkGeneratedBinding.Subscribe<CancelEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, evt => evt.SetCanceled(true), ShrinkEventPriority.Highest, 0, false);
|
||||
using var second = ShrinkGeneratedBinding.Subscribe<CancelEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, _ => normal++, ShrinkEventPriority.Normal, 0, false);
|
||||
using var third = ShrinkGeneratedBinding.Subscribe<CancelEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, _ => monitor++, ShrinkEventPriority.Monitor, 0, true);
|
||||
|
||||
var result = game.Post(new CancelEvent());
|
||||
|
||||
Assert.IsTrue(result.Canceled);
|
||||
Assert.AreEqual(0, normal);
|
||||
Assert.AreEqual(1, monitor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PriorityAndNumericPriorityAreStable()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var game = host.CreateBus(ShrinkBusKey.Game, ShrinkBusOptions.Inline());
|
||||
var order = new List<string>();
|
||||
using var normal = ShrinkGeneratedBinding.Subscribe<PingEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, _ => order.Add("normal"), ShrinkEventPriority.Normal, 0, false);
|
||||
using var high = ShrinkGeneratedBinding.Subscribe<PingEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, _ => order.Add("high"), ShrinkEventPriority.High, 0, false);
|
||||
using var numeric = ShrinkGeneratedBinding.Subscribe<PingEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, _ => order.Add("numeric"), ShrinkEventPriority.Normal, 75, false);
|
||||
|
||||
game.Post(new PingEvent(1));
|
||||
|
||||
CollectionAssert.AreEqual(new[] { "numeric", "high", "normal" }, order);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResultEventUsesTypedResultContract()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var game = host.CreateBus(ShrinkBusKey.Game, ShrinkBusOptions.Inline());
|
||||
using var binding = ShrinkGeneratedBinding.Subscribe<ResultEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, value => value.SetResult(42),
|
||||
ShrinkEventPriority.Normal, 0, false);
|
||||
var value = new ResultEvent();
|
||||
|
||||
var result = game.Post(value);
|
||||
|
||||
Assert.IsTrue(result.Handled);
|
||||
Assert.AreEqual(42, value.Result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HandlerExceptionPropagatesFromPost()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var game = host.CreateBus(ShrinkBusKey.Game, ShrinkBusOptions.Inline());
|
||||
using var binding = ShrinkGeneratedBinding.Subscribe<PingEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, _ => throw new InvalidOperationException("expected"),
|
||||
ShrinkEventPriority.Normal, 0, false);
|
||||
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => game.Post(new PingEvent(1)));
|
||||
|
||||
Assert.AreEqual("expected", exception!.Message);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator PostAsyncCancellationReturnsCanceledFailure()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var bus = host.CreateBus(new ShrinkBusKey("worker", "cancellation"),
|
||||
ShrinkBusOptions.DedicatedThread());
|
||||
using var source = new CancellationTokenSource();
|
||||
source.Cancel();
|
||||
ShrinkPostResult result = default;
|
||||
|
||||
yield return bus.PostAsync(new PingEvent(1), source.Token)
|
||||
.ContinueWith(value => result = value)
|
||||
.ToCoroutine();
|
||||
|
||||
Assert.AreEqual(ShrinkPostFailure.Canceled, result.Failure);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DedicatedThreadShutdownDrainsAcceptedWork()
|
||||
{
|
||||
var host = new ShrinkEventBusHost();
|
||||
var key = new ShrinkBusKey("worker", "drain");
|
||||
var bus = host.CreateBus(key, ShrinkBusOptions.DedicatedThread(queueCapacity: 4));
|
||||
var hits = 0;
|
||||
using var binding = ShrinkGeneratedBinding.Subscribe<PingEvent>(host, key,
|
||||
string.Empty, this, _ => Interlocked.Increment(ref hits),
|
||||
ShrinkEventPriority.Normal, 0, false);
|
||||
Assert.IsTrue(bus.Post(new PingEvent(1)).Accepted);
|
||||
Assert.IsTrue(bus.Post(new PingEvent(2)).Accepted);
|
||||
|
||||
yield return host.ShutdownAsync().ToCoroutine();
|
||||
|
||||
Assert.AreEqual(2, Volatile.Read(ref hits));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DedicatedThreadRejectsWhenBoundedQueueIsFull()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var key = new ShrinkBusKey("worker", "bounded");
|
||||
var options = ShrinkBusOptions.DedicatedThread(queueCapacity: 1);
|
||||
options.OverflowPolicy = ShrinkQueueOverflowPolicy.Reject;
|
||||
var bus = host.CreateBus(key, options);
|
||||
using var entered = new ManualResetEventSlim(false);
|
||||
using var release = new ManualResetEventSlim(false);
|
||||
var hits = 0;
|
||||
using var binding = ShrinkGeneratedBinding.Subscribe<PingEvent>(host, key, string.Empty,
|
||||
this, _ =>
|
||||
{
|
||||
Interlocked.Increment(ref hits);
|
||||
entered.Set();
|
||||
release.Wait(TimeSpan.FromSeconds(2));
|
||||
}, ShrinkEventPriority.Normal, 0, false);
|
||||
|
||||
Assert.IsTrue(bus.Post(new PingEvent(1)).Accepted);
|
||||
Assert.IsTrue(entered.Wait(TimeSpan.FromSeconds(2)));
|
||||
Assert.IsTrue(bus.Post(new PingEvent(2)).Accepted);
|
||||
var rejected = bus.Post(new PingEvent(3));
|
||||
release.Set();
|
||||
|
||||
Assert.IsFalse(rejected.Accepted);
|
||||
Assert.AreEqual(ShrinkPostFailure.QueueFull, rejected.Failure);
|
||||
Assert.IsTrue(SpinWait.SpinUntil(() => Volatile.Read(ref hits) == 2, TimeSpan.FromSeconds(2)));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator TaskPoolHonorsMaximumConcurrency()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var key = new ShrinkBusKey("worker", "parallel");
|
||||
var bus = host.CreateBus(key, ShrinkBusOptions.TaskPool(maxConcurrency: 2, queueCapacity: 8));
|
||||
var active = 0;
|
||||
var maxActive = 0;
|
||||
using var binding = ShrinkGeneratedBinding.SubscribeAsync<PingEvent>(host, key, string.Empty,
|
||||
this, async (_, _) =>
|
||||
{
|
||||
var current = Interlocked.Increment(ref active);
|
||||
UpdateMaximum(ref maxActive, current);
|
||||
Thread.Sleep(30);
|
||||
Interlocked.Decrement(ref active);
|
||||
await UniTask.CompletedTask;
|
||||
}, ShrinkEventPriority.Normal, 0, false);
|
||||
var posts = new List<UniTask<ShrinkPostResult>>();
|
||||
for (var i = 0; i < 4; i++)
|
||||
posts.Add(bus.PostAsync(new PingEvent(i)));
|
||||
|
||||
yield return UniTask.WhenAll(posts).ToCoroutine();
|
||||
|
||||
Assert.AreEqual(2, maxActive);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WarmStructPostDoesNotAllocate()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var game = host.CreateBus(ShrinkBusKey.Game, ShrinkBusOptions.Inline());
|
||||
var sum = 0;
|
||||
using var binding = ShrinkGeneratedBinding.Subscribe<PingEvent>(host, ShrinkBusKey.Game,
|
||||
string.Empty, this, evt => sum += evt.Value, ShrinkEventPriority.Normal, 0, false);
|
||||
for (var i = 0; i < 100; i++)
|
||||
game.Post(new PingEvent(i));
|
||||
|
||||
var before = GC.GetAllocatedBytesForCurrentThread();
|
||||
for (var i = 0; i < 10_000; i++)
|
||||
game.Post(new PingEvent(i));
|
||||
var allocated = GC.GetAllocatedBytesForCurrentThread() - before;
|
||||
|
||||
Assert.AreEqual(0, allocated);
|
||||
Assert.Greater(sum, 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GlobalDetailedObserverReportsDispatchMetadata()
|
||||
{
|
||||
var captured = false;
|
||||
var trace = default(ShrinkEventTrace);
|
||||
void Capture(ShrinkEventTrace value)
|
||||
{
|
||||
trace = value;
|
||||
captured = true;
|
||||
}
|
||||
|
||||
EventBus.DetailedPosted += Capture;
|
||||
try
|
||||
{
|
||||
EventBus.Post(new DiagnosticEvent());
|
||||
}
|
||||
finally
|
||||
{
|
||||
EventBus.DetailedPosted -= Capture;
|
||||
}
|
||||
|
||||
Assert.IsTrue(captured);
|
||||
Assert.AreEqual(typeof(DiagnosticEvent), trace.EventType);
|
||||
Assert.AreEqual(ShrinkBusKey.Game, trace.BusKey);
|
||||
Assert.AreEqual(ShrinkBusSchedulerKind.MainThread, trace.Scheduler);
|
||||
Assert.IsTrue(trace.Result.Accepted);
|
||||
Assert.GreaterOrEqual(trace.ElapsedTimestampTicks, 0L);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GlobalStructPostDoesNotAllocateWithoutObservers()
|
||||
{
|
||||
var value = new DiagnosticEvent();
|
||||
for (var i = 0; i < 100; i++)
|
||||
EventBus.Post(in value);
|
||||
|
||||
var before = GC.GetAllocatedBytesForCurrentThread();
|
||||
for (var i = 0; i < 10_000; i++)
|
||||
EventBus.Post(in value);
|
||||
var allocated = GC.GetAllocatedBytesForCurrentThread() - before;
|
||||
|
||||
Assert.AreEqual(0, allocated);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DedicatedThreadPostAsyncRunsOnDedicatedThread()
|
||||
{
|
||||
using var host = new ShrinkEventBusHost();
|
||||
var bus = host.CreateBus(new ShrinkBusKey("worker", "tests"),
|
||||
ShrinkBusOptions.DedicatedThread());
|
||||
var callerThread = Thread.CurrentThread.ManagedThreadId;
|
||||
var handlerThread = callerThread;
|
||||
using var binding = ShrinkGeneratedBinding.Subscribe<PingEvent>(host,
|
||||
new ShrinkBusKey("worker", "tests"), string.Empty, this,
|
||||
_ => handlerThread = Thread.CurrentThread.ManagedThreadId,
|
||||
ShrinkEventPriority.Normal, 0, false);
|
||||
|
||||
yield return bus.PostAsync(new PingEvent(1)).ToCoroutine();
|
||||
|
||||
Assert.AreNotEqual(callerThread, handlerThread);
|
||||
}
|
||||
|
||||
private static void UpdateMaximum(ref int location, int candidate)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var current = Volatile.Read(ref location);
|
||||
if (candidate <= current || Interlocked.CompareExchange(ref location, candidate, current) == current)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4570cac2813143c40b9a8edeadf19057
|
||||
guid: f11f5ea66801460ca4ec08d35763d627
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user