diff --git a/src/LogicLooper/ILogicLooper.cs b/src/LogicLooper/ILogicLooper.cs index 94bbaf8..72cdc72 100644 --- a/src/LogicLooper/ILogicLooper.cs +++ b/src/LogicLooper/ILogicLooper.cs @@ -41,7 +41,7 @@ public interface ILogicLooper : IDisposable Task RegisterActionAsync(LogicLooperActionWithStateDelegate loopAction, TState state); /// - /// [Experimental] Registers a loop-frame action to the looper and returns to wait for completion. + /// [Experimental] Registers an async-aware loop-frame action to the looper and returns to wait for completion. /// An asynchronous action is executed across multiple frames, differ from the synchronous version. /// /// @@ -49,7 +49,7 @@ public interface ILogicLooper : IDisposable Task RegisterActionAsync(LogicLooperAsyncActionDelegate loopAction); /// - /// [Experimental] Registers a loop-frame action with state object to the looper and returns to wait for completion. + /// [Experimental] Registers an async-aware loop-frame action with state object to the looper and returns to wait for completion. /// An asynchronous action is executed across multiple frames, differ from the synchronous version. /// /// diff --git a/src/LogicLooper/ILogicLooperPool.cs b/src/LogicLooper/ILogicLooperPool.cs index 4273f90..2c324bf 100644 --- a/src/LogicLooper/ILogicLooperPool.cs +++ b/src/LogicLooper/ILogicLooperPool.cs @@ -45,4 +45,10 @@ public interface ILogicLooperPool : IDisposable /// /// Task ShutdownAsync(TimeSpan shutdownDelay); + + /// + /// Gets a instance from the pool. This is useful when you want to explicitly register multiple actions on the same loop thread. + /// + /// + ILogicLooper GetLooper(); } diff --git a/src/LogicLooper/Internal/NotInitializedLogicLooperPool.cs b/src/LogicLooper/Internal/NotInitializedLogicLooperPool.cs index 61eecf2..48bd742 100644 --- a/src/LogicLooper/Internal/NotInitializedLogicLooperPool.cs +++ b/src/LogicLooper/Internal/NotInitializedLogicLooperPool.cs @@ -19,5 +19,8 @@ public Task RegisterActionAsync(LogicLooperAsyncActionWithStateDelegate< public Task ShutdownAsync(TimeSpan shutdownDelay) => throw new InvalidOperationException("LogicLooper.Shared is not initialized yet."); + public ILogicLooper GetLooper() + => throw new InvalidOperationException("LogicLooper.Shared is not initialized yet."); + public void Dispose() { } } diff --git a/src/LogicLooper/LogicLooperPool.cs b/src/LogicLooper/LogicLooperPool.cs index 1660c48..cdd9511 100644 --- a/src/LogicLooper/LogicLooperPool.cs +++ b/src/LogicLooper/LogicLooperPool.cs @@ -41,19 +41,19 @@ public LogicLooperPool(TimeSpan targetFrameTime, int looperCount, ILogicLooperPo /// public Task RegisterActionAsync(LogicLooperActionDelegate loopAction) - => _balancer.GetPooledLooper(_loopers).RegisterActionAsync(loopAction); + => GetLooper().RegisterActionAsync(loopAction); /// public Task RegisterActionAsync(LogicLooperActionWithStateDelegate loopAction, TState state) - => _balancer.GetPooledLooper(_loopers).RegisterActionAsync(loopAction, state); + => GetLooper().RegisterActionAsync(loopAction, state); /// public Task RegisterActionAsync(LogicLooperAsyncActionDelegate loopAction) - => _balancer.GetPooledLooper(_loopers).RegisterActionAsync(loopAction); + => GetLooper().RegisterActionAsync(loopAction); /// public Task RegisterActionAsync(LogicLooperAsyncActionWithStateDelegate loopAction, TState state) - => _balancer.GetPooledLooper(_loopers).RegisterActionAsync(loopAction, state); + => GetLooper().RegisterActionAsync(loopAction, state); /// public async Task ShutdownAsync(TimeSpan shutdownDelay) @@ -61,6 +61,10 @@ public async Task ShutdownAsync(TimeSpan shutdownDelay) await Task.WhenAll(_loopers.Select(x => x.ShutdownAsync(shutdownDelay))); } + /// + public ILogicLooper GetLooper() + => _balancer.GetPooledLooper(_loopers); + public void Dispose() { foreach (var looper in _loopers) diff --git a/src/LogicLooper/ManualLogicLooperPool.cs b/src/LogicLooper/ManualLogicLooperPool.cs index a7b332b..dfbba71 100644 --- a/src/LogicLooper/ManualLogicLooperPool.cs +++ b/src/LogicLooper/ManualLogicLooperPool.cs @@ -43,37 +43,29 @@ public void TickWhile(Func predicate) /// public void Dispose() - { - Loopers[0].Dispose(); - } + => Loopers[0].Dispose(); /// public Task RegisterActionAsync(LogicLooperActionDelegate loopAction) - { - return Loopers[0].RegisterActionAsync(loopAction); - } + => Loopers[0].RegisterActionAsync(loopAction); /// public Task RegisterActionAsync(LogicLooperActionWithStateDelegate loopAction, TState state) - { - return Loopers[0].RegisterActionAsync(loopAction, state); - } + => Loopers[0].RegisterActionAsync(loopAction, state); /// public Task RegisterActionAsync(LogicLooperAsyncActionDelegate loopAction) - { - return Loopers[0].RegisterActionAsync(loopAction); - } + => Loopers[0].RegisterActionAsync(loopAction); /// public Task RegisterActionAsync(LogicLooperAsyncActionWithStateDelegate loopAction, TState state) - { - return Loopers[0].RegisterActionAsync(loopAction, state); - } + => Loopers[0].RegisterActionAsync(loopAction, state); /// public Task ShutdownAsync(TimeSpan shutdownDelay) - { - return Loopers[0].ShutdownAsync(shutdownDelay); - } + => Loopers[0].ShutdownAsync(shutdownDelay); + + /// + public ILogicLooper GetLooper() + => Loopers[0]; } diff --git a/test/LogicLooper.Test/LogicLooperPoolTest.cs b/test/LogicLooper.Test/LogicLooperPoolTest.cs index 7f3600c..4c73144 100644 --- a/test/LogicLooper.Test/LogicLooperPoolTest.cs +++ b/test/LogicLooper.Test/LogicLooperPoolTest.cs @@ -43,4 +43,24 @@ public void RegisterActionAsync() executedCount.Should().Be(actionCount * loopCount); } + + [Fact] + public void GetLooper() + { + using var pool = new LogicLooperPool(60, 4, new FakeSequentialLogicLooperPoolBalancer()); + pool.GetLooper().Should().Be(pool.Loopers[0]); + pool.GetLooper().Should().Be(pool.Loopers[1]); + pool.GetLooper().Should().Be(pool.Loopers[2]); + pool.GetLooper().Should().Be(pool.Loopers[3]); + pool.GetLooper().Should().Be(pool.Loopers[0]); + } + + class FakeSequentialLogicLooperPoolBalancer : ILogicLooperPoolBalancer + { + private int _count; + public Cysharp.Threading.LogicLooper GetPooledLooper(Cysharp.Threading.LogicLooper[] pooledLoopers) + { + return pooledLoopers[_count++ % pooledLoopers.Length]; + } + } } diff --git a/test/LogicLooper.Test/ManualLogicLooperPoolTest.cs b/test/LogicLooper.Test/ManualLogicLooperPoolTest.cs index 178c62a..0ec286f 100644 --- a/test/LogicLooper.Test/ManualLogicLooperPoolTest.cs +++ b/test/LogicLooper.Test/ManualLogicLooperPoolTest.cs @@ -26,4 +26,13 @@ public void RegisterActionAsync() pool.Loopers[0].ApproximatelyRunningActions.Should().Be(0); t1.IsCompletedSuccessfully.Should().BeTrue(); } + + [Fact] + public void GetLooper() + { + var pool = new ManualLogicLooperPool(60.0); + var looper = pool.GetLooper(); + + looper.Should().Be(pool.FakeLooper); + } }