From a76fbe9526155d8e03586498ce2f831cb9df3950 Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 14 Dec 2023 17:42:47 +0900 Subject: [PATCH] EveryUpdate --- src/R3/Factories/EveryUpdate.cs | 88 +++++++++++++++++++ src/R3/Factories/_EventFactory.cs | 86 +----------------- src/R3/FrameProvider.cs | 8 +- tests/R3.Tests/FactoryTests/EmptyTest.cs | 2 +- .../R3.Tests/FactoryTests/EveryUpdateTest.cs | 71 +++++++++++++++ .../FactoryTests/ReturnOnCompletedTest.cs | 2 +- tests/R3.Tests/FactoryTests/ReturnTest.cs | 4 +- tests/R3.Tests/FactoryTests/ThrowTest.cs | 2 +- tests/R3.Tests/OperatorTests/WhereTest.cs | 4 +- tests/R3.Tests/_TestHelper.cs | 2 +- 10 files changed, 175 insertions(+), 94 deletions(-) create mode 100644 src/R3/Factories/EveryUpdate.cs create mode 100644 tests/R3.Tests/FactoryTests/EveryUpdateTest.cs diff --git a/src/R3/Factories/EveryUpdate.cs b/src/R3/Factories/EveryUpdate.cs new file mode 100644 index 00000000..da19e16e --- /dev/null +++ b/src/R3/Factories/EveryUpdate.cs @@ -0,0 +1,88 @@ +namespace R3; + +public static partial class Event +{ + public static Event EveryUpdate() + { + return new EveryUpdate(EventSystem.DefaultFrameProvider, CancellationToken.None, cancelImmediately: false); + } + + public static Event EveryUpdate(CancellationToken cancellationToken) + { + return new EveryUpdate(EventSystem.DefaultFrameProvider, cancellationToken, cancelImmediately: false); + } + + public static Event EveryUpdate(FrameProvider frameProvider) + { + return new EveryUpdate(frameProvider, CancellationToken.None, cancelImmediately: false); + } + + public static Event EveryUpdate(FrameProvider frameProvider, CancellationToken cancellationToken) + { + return new EveryUpdate(frameProvider, cancellationToken, cancelImmediately: false); + } + + public static Event EveryUpdate(FrameProvider frameProvider, CancellationToken cancellationToken, bool cancelImmediately) + { + return new EveryUpdate(frameProvider, cancellationToken, cancelImmediately: cancelImmediately); + } +} + + +internal sealed class EveryUpdate(FrameProvider frameProvider, CancellationToken cancellationToken, bool cancelImmediately) : Event +{ + protected override IDisposable SubscribeCore(Subscriber subscriber) + { + var runner = new EveryUpdateRunnerWorkItem(subscriber, cancellationToken, cancelImmediately); + frameProvider.Register(runner); + return runner; + } + + class EveryUpdateRunnerWorkItem : IFrameRunnerWorkItem, IDisposable + { + Subscriber subscriber; + CancellationToken cancellationToken; + CancellationTokenRegistration cancellationTokenRegistration; + bool isDisposed; + + public EveryUpdateRunnerWorkItem(Subscriber subscriber, CancellationToken cancellationToken, bool cancelImmediately) + { + this.subscriber = subscriber; + this.cancellationToken = cancellationToken; + + if (cancelImmediately && cancellationToken.CanBeCanceled) + { + cancellationTokenRegistration = cancellationToken.UnsafeRegister(static state => + { + var s = (EveryUpdateRunnerWorkItem)state!; + s.subscriber.OnCompleted(); + s.Dispose(); + }, this); + } + } + + public bool MoveNext(long frameCount) + { + if (isDisposed) + { + return false; + } + + if (cancellationToken.IsCancellationRequested) + { + subscriber.OnCompleted(); + Dispose(); + return false; + } + + subscriber.OnNext(default); + return true; + } + + public void Dispose() + { + isDisposed = true; + cancellationTokenRegistration.Dispose(); + } + } +} diff --git a/src/R3/Factories/_EventFactory.cs b/src/R3/Factories/_EventFactory.cs index e33b1604..73c8f1ff 100644 --- a/src/R3/Factories/_EventFactory.cs +++ b/src/R3/Factories/_EventFactory.cs @@ -28,89 +28,5 @@ public static partial class Event // AsNeverComplete // TODO: use SystemDefault - - public static Event EveryUpdate() - { - return new EveryUpdate(EventSystem.DefaultFrameProvider, CancellationToken.None, cancelImmediately: false); - } - - public static Event EveryUpdate(CancellationToken cancellationToken) - { - return new EveryUpdate(EventSystem.DefaultFrameProvider, cancellationToken, cancelImmediately: false); - } - - public static Event EveryUpdate(FrameProvider frameProvider) - { - return new EveryUpdate(frameProvider, CancellationToken.None, cancelImmediately: false); - } - - public static Event EveryUpdate(FrameProvider frameProvider, CancellationToken cancellationToken) - { - return new EveryUpdate(frameProvider, cancellationToken, cancelImmediately: false); - } - - public static Event EveryUpdate(FrameProvider frameProvider, CancellationToken cancellationToken, bool cancelImmediately) - { - return new EveryUpdate(frameProvider, cancellationToken, cancelImmediately: cancelImmediately); - } -} - - - -internal sealed class EveryUpdate(FrameProvider frameProvider, CancellationToken cancellationToken, bool cancelImmediately) : Event -{ - protected override IDisposable SubscribeCore(Subscriber subscriber) - { - var runner = new EveryUpdateRunnerWorkItem(subscriber, cancellationToken, cancelImmediately); - frameProvider.Register(runner); - return runner; - } - - class EveryUpdateRunnerWorkItem : IFrameRunnerWorkItem, IDisposable - { - Subscriber subscriber; - CancellationToken cancellationToken; - CancellationTokenRegistration cancellationTokenRegistration; - bool isDisposed; - - public EveryUpdateRunnerWorkItem(Subscriber subscriber, CancellationToken cancellationToken, bool cancelImmediately) - { - this.subscriber = subscriber; - this.cancellationToken = cancellationToken; - - if (cancelImmediately && cancellationToken.CanBeCanceled) - { - cancellationTokenRegistration = cancellationToken.UnsafeRegister(static state => - { - var s = (EveryUpdateRunnerWorkItem)state!; - s.subscriber.OnCompleted(); - s.Dispose(); - }, this); - } - } - - public bool MoveNext(long frameCount) - { - if (isDisposed) - { - return false; - } - - if (cancellationToken.IsCancellationRequested) - { - subscriber.OnCompleted(); - Dispose(); - return false; - } - - subscriber.OnNext(default); - return true; - } - - public void Dispose() - { - isDisposed = true; - cancellationTokenRegistration.Dispose(); - } - } + } diff --git a/src/R3/FrameProvider.cs b/src/R3/FrameProvider.cs index 7253a71b..391997e8 100644 --- a/src/R3/FrameProvider.cs +++ b/src/R3/FrameProvider.cs @@ -15,7 +15,13 @@ public interface IFrameRunnerWorkItem public sealed class ManualFrameProvider : FrameProvider { long frameCount; - FreeListCore list = new FreeListCore(); + readonly object gate = new object(); + FreeListCore list; + + public ManualFrameProvider() + { + list = new FreeListCore(gate); + } public override long GetFrameCount() { diff --git a/tests/R3.Tests/FactoryTests/EmptyTest.cs b/tests/R3.Tests/FactoryTests/EmptyTest.cs index 146daf1e..36f41e96 100644 --- a/tests/R3.Tests/FactoryTests/EmptyTest.cs +++ b/tests/R3.Tests/FactoryTests/EmptyTest.cs @@ -18,7 +18,7 @@ public void EmptyWithTime() using var list = Event.Empty(TimeSpan.FromSeconds(5), fakeTime).ToLiveList(); fakeTime.Advance(TimeSpan.FromSeconds(4)); - list.AssertIsNoResulted(); + list.AssertIsNotCompleted(); fakeTime.Advance(TimeSpan.FromSeconds(1)); list.AssertIsCompleted(); diff --git a/tests/R3.Tests/FactoryTests/EveryUpdateTest.cs b/tests/R3.Tests/FactoryTests/EveryUpdateTest.cs new file mode 100644 index 00000000..2eccd8a9 --- /dev/null +++ b/tests/R3.Tests/FactoryTests/EveryUpdateTest.cs @@ -0,0 +1,71 @@ +namespace R3.Tests.FactoryTests; + +public class EveryUpdateTest +{ + [Fact] + public void EveryUpdate() + { + var cts = new CancellationTokenSource(); + var frameProvider = new ManualFrameProvider(); + + var list = Event.EveryUpdate(frameProvider, cts.Token).Select(_ => frameProvider.GetFrameCount()).ToLiveList(); + + list.AssertEqual([]); + + frameProvider.Advance(); + list.AssertEqual([0]); + + frameProvider.Advance(3); + list.AssertEqual([0, 1, 2, 3]); + + cts.Cancel(); + list.AssertIsNotCompleted(); + + frameProvider.Advance(); + list.AssertIsCompleted(); + } + + [Fact] + public void EveryUpdateCancelImmediate() + { + var cts = new CancellationTokenSource(); + var frameProvider = new ManualFrameProvider(); + + var list = Event.EveryUpdate(frameProvider, cts.Token, cancelImmediately: true).Select(_ => frameProvider.GetFrameCount()).ToLiveList(); + + list.AssertEqual([]); + + frameProvider.Advance(); + list.AssertEqual([0]); + + frameProvider.Advance(3); + list.AssertEqual([0, 1, 2, 3]); + + cts.Cancel(); + list.AssertIsCompleted(); + + frameProvider.Advance(); + list.AssertEqual([0, 1, 2, 3]); + list.AssertIsCompleted(); + } + + [Fact] + public void EveryUpdateDispose() + { + var frameProvider = new ManualFrameProvider(); + + var list = Event.EveryUpdate(frameProvider).Select(_ => frameProvider.GetFrameCount()).ToLiveList(); + + list.AssertEqual([]); + + frameProvider.Advance(); + list.AssertEqual([0]); + + frameProvider.Advance(3); + list.AssertEqual([0, 1, 2, 3]); + + list.Dispose(); + frameProvider.Advance(); + list.AssertEqual([0, 1, 2, 3]); + } +} diff --git a/tests/R3.Tests/FactoryTests/ReturnOnCompletedTest.cs b/tests/R3.Tests/FactoryTests/ReturnOnCompletedTest.cs index 7bf3269e..983b7a11 100644 --- a/tests/R3.Tests/FactoryTests/ReturnOnCompletedTest.cs +++ b/tests/R3.Tests/FactoryTests/ReturnOnCompletedTest.cs @@ -19,7 +19,7 @@ public void ReturnOnCompleted() fakeTime.Advance(TimeSpan.FromSeconds(4)); list.AssertEqual([]); - list.AssertIsNoResulted(); + list.AssertIsNotCompleted(); fakeTime.Advance(TimeSpan.FromSeconds(1)); list.AssertEqual([]); diff --git a/tests/R3.Tests/FactoryTests/ReturnTest.cs b/tests/R3.Tests/FactoryTests/ReturnTest.cs index 130b2fe0..dc0dbf85 100644 --- a/tests/R3.Tests/FactoryTests/ReturnTest.cs +++ b/tests/R3.Tests/FactoryTests/ReturnTest.cs @@ -27,7 +27,7 @@ public void Return() fakeTime.Advance(TimeSpan.FromSeconds(4)); list.AssertEqual([]); - list.AssertIsNoResulted(); + list.AssertIsNotCompleted(); fakeTime.Advance(TimeSpan.FromSeconds(1)); list.AssertEqual([10]); @@ -63,7 +63,7 @@ public void ReturnOnCompleted() fakeTime.Advance(TimeSpan.FromSeconds(4)); list.AssertEqual([]); - list.AssertIsNoResulted(); + list.AssertIsNotCompleted(); fakeTime.Advance(TimeSpan.FromSeconds(1)); list.AssertEqual([10]); diff --git a/tests/R3.Tests/FactoryTests/ThrowTest.cs b/tests/R3.Tests/FactoryTests/ThrowTest.cs index 18fe276d..5800870f 100644 --- a/tests/R3.Tests/FactoryTests/ThrowTest.cs +++ b/tests/R3.Tests/FactoryTests/ThrowTest.cs @@ -21,7 +21,7 @@ public void Throw() fakeTime.Advance(TimeSpan.FromSeconds(4)); list.AssertEqual([]); - list.AssertIsNoResulted(); + list.AssertIsNotCompleted(); fakeTime.Advance(TimeSpan.FromSeconds(1)); list.AssertEqual([]); diff --git a/tests/R3.Tests/OperatorTests/WhereTest.cs b/tests/R3.Tests/OperatorTests/WhereTest.cs index 7af07ffa..c3eccd37 100644 --- a/tests/R3.Tests/OperatorTests/WhereTest.cs +++ b/tests/R3.Tests/OperatorTests/WhereTest.cs @@ -50,7 +50,7 @@ public void WhereCompletable() p.PublishOnNext(30); list.AssertEqual([1, 3]); - list.AssertIsNoResulted(); + list.AssertIsNotCompleted(); p.PublishOnCompleted(default); @@ -84,7 +84,7 @@ public void WhereCompletableIndexed() p.PublishOnNext(8); list.AssertEqual([1, 5, 8]); - list.AssertIsNoResulted(); + list.AssertIsNotCompleted(); p.PublishOnCompleted(default); diff --git a/tests/R3.Tests/_TestHelper.cs b/tests/R3.Tests/_TestHelper.cs index 7e8ce0cc..d2a7b8f8 100644 --- a/tests/R3.Tests/_TestHelper.cs +++ b/tests/R3.Tests/_TestHelper.cs @@ -15,7 +15,7 @@ public static void AssertIsCompleted(this LiveList list) list.IsCompleted.Should().BeTrue(); } - public static void AssertIsNoResulted(this LiveList list) + public static void AssertIsNotCompleted(this LiveList list) { list.IsCompleted.Should().BeFalse(); }