diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs index 503e9db..9f46b10 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs @@ -20,8 +20,6 @@ namespace Cysharp.Threading.Tasks { ITriggerHandler head; // head.prev is last ITriggerHandler iteratingHead; - - bool preserveRemoveSelf; ITriggerHandler iteratingNode; void LogError(Exception ex) @@ -44,6 +42,7 @@ namespace Cysharp.Threading.Tasks while (h != null) { iteratingNode = h; + var next = h.Next; try { @@ -55,18 +54,7 @@ namespace Cysharp.Threading.Tasks Remove(h); } - if (preserveRemoveSelf) - { - preserveRemoveSelf = false; - iteratingNode = null; - var next = h.Next; - Remove(h); - h = next; - } - else - { - h = h.Next; - } + h = next; } iteratingNode = null; @@ -96,8 +84,7 @@ namespace Cysharp.Threading.Tasks { LogError(ex); } - - preserveRemoveSelf = false; + iteratingNode = null; var next = h.Next; Remove(h); @@ -131,8 +118,7 @@ namespace Cysharp.Threading.Tasks { LogError(ex); } - - preserveRemoveSelf = false; + iteratingNode = null; var next = h.Next; Remove(h); @@ -166,8 +152,7 @@ namespace Cysharp.Threading.Tasks { LogError(ex); } - - preserveRemoveSelf = false; + iteratingNode = null; var next = h.Next; Remove(h); @@ -240,72 +225,64 @@ namespace Cysharp.Threading.Tasks public void Remove(ITriggerHandler handler) { if (handler == null) throw new ArgumentNullException(nameof(handler)); + + var prev = handler.Prev; + var next = handler.Next; - if (iteratingNode != null && iteratingNode == handler) + if (next != null) { - // if remove self, reserve remove self after invoke completed. - preserveRemoveSelf = true; + next.Prev = prev; + } + + if (handler == head) + { + head = next; + } + else if (handler == iteratingHead) + { + iteratingHead = next; } else { - var prev = handler.Prev; - var next = handler.Next; - - if (next != null) + // when handler is head, prev indicate last so don't use it. + if (prev != null) { - next.Prev = prev; + prev.Next = next; } - - if (handler == head) - { - head = next; - } - else if (handler == iteratingHead) - { - iteratingHead = next; - } - else - { - // when handler is head, prev indicate last so don't use it. - if (prev != null) - { - prev.Next = next; - } - } - - if (head != null) - { - if (head.Prev == handler) - { - if (prev != head) - { - head.Prev = prev; - } - else - { - head.Prev = null; - } - } - } - - if (iteratingHead != null) - { - if (iteratingHead.Prev == handler) - { - if (prev != iteratingHead.Prev) - { - iteratingHead.Prev = prev; - } - else - { - iteratingHead.Prev = null; - } - } - } - - handler.Prev = null; - handler.Next = null; } + + if (head != null) + { + if (head.Prev == handler) + { + if (prev != head) + { + head.Prev = prev; + } + else + { + head.Prev = null; + } + } + } + + if (iteratingHead != null) + { + if (iteratingHead.Prev == handler) + { + if (prev != iteratingHead.Prev) + { + iteratingHead.Prev = prev; + } + else + { + iteratingHead.Prev = null; + } + } + } + + handler.Prev = null; + handler.Next = null; } } } diff --git a/src/UniTask/Assets/Tests/AsyncReactivePropertyTest.cs b/src/UniTask/Assets/Tests/AsyncReactivePropertyTest.cs new file mode 100644 index 0000000..f582443 --- /dev/null +++ b/src/UniTask/Assets/Tests/AsyncReactivePropertyTest.cs @@ -0,0 +1,63 @@ +using Cysharp.Threading.Tasks; +using FluentAssertions; +using System; +using System.Collections; +using System.Threading; +using UnityEngine.TestTools; + +namespace Cysharp.Threading.TasksTests +{ + public class AsyncReactivePropertyTest + { + private int _callCounter; + + [UnityTest] + public IEnumerator WaitCancelWait() => UniTask.ToCoroutine(async () => + { + // Test case for https://github.com/Cysharp/UniTask/issues/444 + + var property = new AsyncReactiveProperty(0); + + var cts1 = new CancellationTokenSource(); + var cts2 = new CancellationTokenSource(); + WaitForProperty(property, cts1.Token); + WaitForProperty(property, cts2.Token); + + _callCounter = 0; + property.Value = 1; + _callCounter.Should().Be(2); + + cts2.Cancel(); + cts2.Dispose(); + cts1.Cancel(); + cts1.Dispose(); + + var cts3 = new CancellationTokenSource(); + WaitForProperty(property, cts3.Token); + + _callCounter = 0; + property.Value = 2; + _callCounter.Should().Be(1); + + cts3.Cancel(); + cts3.Dispose(); + await UniTask.CompletedTask; + }); + + private async void WaitForProperty(AsyncReactiveProperty property, CancellationToken token) + { + while (true) + { + try + { + await property.WaitAsync(token); + _callCounter++; + } + catch (OperationCanceledException) + { + break; + } + } + } + } +} diff --git a/src/UniTask/Assets/Tests/AsyncReactivePropertyTest.cs.meta b/src/UniTask/Assets/Tests/AsyncReactivePropertyTest.cs.meta new file mode 100644 index 0000000..0c29ebf --- /dev/null +++ b/src/UniTask/Assets/Tests/AsyncReactivePropertyTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 27665955eefb4448969b8cc4dd204600 +timeCreated: 1676129650 \ No newline at end of file