diff --git a/Coroutine/CoroutineHandlerInstance.cs b/Coroutine/CoroutineHandlerInstance.cs index 5db850c..3c77dc1 100644 --- a/Coroutine/CoroutineHandlerInstance.cs +++ b/Coroutine/CoroutineHandlerInstance.cs @@ -13,6 +13,7 @@ namespace Coroutine { private readonly List tickingCoroutines = new List(); private readonly List eventCoroutines = new List(); private readonly Queue outstandingCoroutines = new Queue(); + private readonly Dictionary deletedCoroutinesIndexes = new Dictionary(); private readonly Stopwatch stopwatch = new Stopwatch(); /// @@ -68,6 +69,7 @@ namespace Coroutine { /// /// The amount of seconds that have passed since the last time this method was invoked public void Tick(double deltaSeconds) { + this.RemoveDeletedCoroutines(); this.AddOutstandingCoroutines(); this.tickingCoroutines.RemoveAll(c => { if (c.Tick(deltaSeconds)) { @@ -85,16 +87,15 @@ namespace Coroutine { /// /// The event to raise public void RaiseEvent(Event evt) { - this.AddOutstandingCoroutines(); - this.eventCoroutines.RemoveAll(c => { - if (c.OnEvent(evt)) { - return true; - } else if (!c.IsWaitingForEvent()) { - this.outstandingCoroutines.Enqueue(c); - return true; + for (int i = 0; i < this.eventCoroutines.Count; i++) { + var eventCoroutine = this.eventCoroutines[i]; + if (eventCoroutine.OnEvent(evt)) { + this.deletedCoroutinesIndexes[i] = 1; + } else if (!eventCoroutine.IsWaitingForEvent()) { + this.outstandingCoroutines.Enqueue(eventCoroutine); + this.deletedCoroutinesIndexes[i] = 1; } - return false; - }); + } } /// @@ -114,6 +115,15 @@ namespace Coroutine { } } + private void RemoveDeletedCoroutines() { + int counter = 0; + this.eventCoroutines.RemoveAll(c => { + return this.deletedCoroutinesIndexes.ContainsKey(counter++); + }); + + this.deletedCoroutinesIndexes.Clear(); + } + private static IEnumerator InvokeLaterImpl(Wait wait, Action action) { yield return wait; action(); diff --git a/Tests/EventBasedCoroutineTests.cs b/Tests/EventBasedCoroutineTests.cs index 5c5cb72..5a2c283 100644 --- a/Tests/EventBasedCoroutineTests.cs +++ b/Tests/EventBasedCoroutineTests.cs @@ -18,8 +18,10 @@ namespace Tests { var cr = CoroutineHandler.Start(OnEventTriggered()); Assert.AreEqual(1, counter, "instruction before yield is not executed."); + CoroutineHandler.Tick(1); CoroutineHandler.RaiseEvent(myEvent); Assert.AreEqual(2, counter, "instruction after yield is not executed."); + CoroutineHandler.Tick(1); CoroutineHandler.RaiseEvent(myEvent); Assert.AreEqual(2, counter, "instruction after yield is not executed."); @@ -46,6 +48,7 @@ namespace Tests { } var cr = CoroutineHandler.Start(OnEventTriggeredInfinite()); + CoroutineHandler.Tick(1); cr.OnFinished += SetCounterToUnreachableValue; for (var i = 0; i < 50; i++) CoroutineHandler.RaiseEvent(myOtherEvent); @@ -80,6 +83,7 @@ namespace Tests { } var cr = CoroutineHandler.Start(OnEvent()); + CoroutineHandler.Tick(1); cr.OnFinished += SetCounterToUnreachableValue; for (int i = 0; i < 10; i++) CoroutineHandler.RaiseEvent(myEvent); @@ -136,21 +140,25 @@ namespace Tests { Assert.AreEqual(0, counterGrandParent, "Grand Parent counter is invalid at event 0."); Assert.AreEqual(0, counterParent, "Parent counter is invalid at event 0."); Assert.AreEqual(0, counterChild, "Child counter is invalid at event 0."); + CoroutineHandler.Tick(1); CoroutineHandler.RaiseEvent(myEvent); Assert.AreEqual(1, counterAlwaysRunning, "Always running counter is invalid at event 1."); Assert.AreEqual(1, counterGrandParent, "Grand Parent counter is invalid at event 1."); Assert.AreEqual(0, counterParent, "Parent counter is invalid at event 1."); Assert.AreEqual(0, counterChild, "Child counter is invalid at event 1."); + CoroutineHandler.Tick(1); CoroutineHandler.RaiseEvent(myEvent); Assert.AreEqual(2, counterAlwaysRunning, "Always running counter is invalid at event 2."); Assert.AreEqual(1, counterGrandParent, "Grand Parent counter is invalid at event 2."); Assert.AreEqual(1, counterParent, "Parent counter is invalid at event 2."); Assert.AreEqual(0, counterChild, "Child counter is invalid at event 2."); + CoroutineHandler.Tick(1); CoroutineHandler.RaiseEvent(myEvent); Assert.AreEqual(3, counterAlwaysRunning, "Always running counter is invalid at event 3."); Assert.AreEqual(1, counterGrandParent, "Grand Parent counter is invalid at event 3."); Assert.AreEqual(1, counterParent, "Parent counter is invalid at event 3."); Assert.AreEqual(1, counterChild, "Child counter is invalid at event 3."); + CoroutineHandler.Tick(1); CoroutineHandler.RaiseEvent(myEvent); Assert.AreEqual(4, counterAlwaysRunning, "Always running counter is invalid at event 4."); Assert.AreEqual(1, counterGrandParent, "Grand Parent counter is invalid at event 4."); @@ -158,6 +166,54 @@ namespace Tests { Assert.AreEqual(1, counterChild, "Child counter is invalid at event 4."); } + [Test] + public void TestNestedRaiseEvent() { + var event1 = new Event(); + var event2 = new Event(); + var event3 = new Event(); + var CoroutineCreated = new Event(); + int counterCoroutineA = 0; + int counter = 0; + + CoroutineHandler.Start(OnCoroutineCreatedInfinite()); + CoroutineHandler.Start(OnEvent1()); + CoroutineHandler.Tick(1); + CoroutineHandler.RaiseEvent(event1); + CoroutineHandler.Tick(1); + CoroutineHandler.RaiseEvent(event2); + CoroutineHandler.Tick(1); + CoroutineHandler.RaiseEvent(event3); + Assert.AreEqual(3, counter); + Assert.AreEqual(2, counterCoroutineA); + + IEnumerator OnCoroutineCreatedInfinite() { + while (true) + { + yield return new Wait(CoroutineCreated); + counterCoroutineA++; + } + } + + IEnumerator OnEvent1() { + yield return new Wait(event1); + counter++; + CoroutineHandler.Start(OnEvent2()); + CoroutineHandler.RaiseEvent(CoroutineCreated); + } + + IEnumerator OnEvent2() { + yield return new Wait(event2); + counter++; + CoroutineHandler.Start(OnEvent3()); + CoroutineHandler.RaiseEvent(CoroutineCreated); + } + + IEnumerator OnEvent3() { + yield return new Wait(event3); + counter++; + } + } + [Test] public void TestPriority() { var myEvent = new Event(); @@ -207,6 +263,7 @@ namespace Tests { CoroutineHandler.Start(ShouldExecuteAfter()); CoroutineHandler.Start(ShouldExecuteBefore0(), priority: highPriority); CoroutineHandler.Start(ShouldExecuteFinally(), priority: -1); + CoroutineHandler.Tick(1); CoroutineHandler.RaiseEvent(myEvent); Assert.AreEqual(1, counterShouldExecuteAfter, $"ShouldExecuteAfter counter {counterShouldExecuteAfter} is invalid."); Assert.AreEqual(1, counterShouldExecuteFinally, $"ShouldExecuteFinally counter {counterShouldExecuteFinally} is invalid."); @@ -229,6 +286,7 @@ namespace Tests { }); Assert.AreEqual(0, counter, "Incorrect counter value after 5 seconds."); + CoroutineHandler.Tick(1); CoroutineHandler.RaiseEvent(myEvent); Assert.AreEqual(3, counter, "Incorrect counter value after 10 seconds."); Assert.AreEqual(true, cr.IsFinished, "Incorrect IsFinished value.");