diff --git a/src/LogicLooper/LogicLooperPool.cs b/src/LogicLooper/LogicLooperPool.cs
index 446c4e9..0d152ca 100644
--- a/src/LogicLooper/LogicLooperPool.cs
+++ b/src/LogicLooper/LogicLooperPool.cs
@@ -9,10 +9,17 @@ public sealed partial class LogicLooperPool : ILogicLooperPool, IDisposable
{
private readonly PooledLogicLooper[] _loopers;
private readonly ILogicLooperPoolBalancer _balancer;
- private readonly CancellationTokenSource _shutdownTokenSource = new();
+ private bool _isDisposed;
///
- public IReadOnlyList Loopers => _loopers;
+ public IReadOnlyList Loopers
+ {
+ get
+ {
+ ThrowIfDisposed();
+ return _loopers;
+ }
+ }
///
/// Initialize the looper pool with specified configurations.
@@ -101,15 +108,25 @@ public Task RegisterActionAsync(LogicLooperAsyncActionWithStateDelegate<
///
public async Task ShutdownAsync(TimeSpan shutdownDelay)
{
+ ThrowIfDisposed();
await Task.WhenAll(_loopers.Select(x => x.WrappedLooper.ShutdownAsync(shutdownDelay)));
}
///
public ILogicLooper GetLooper()
- => _balancer.GetPooledLooper(_loopers);
+ {
+ ThrowIfDisposed();
+ return _balancer.GetPooledLooper(_loopers);
+ }
public void Dispose()
{
+ if (_isDisposed)
+ {
+ return;
+ }
+ _isDisposed = true;
+
foreach (var looper in _loopers)
{
try
@@ -120,7 +137,14 @@ public void Dispose()
{
}
}
- _shutdownTokenSource.Cancel();
+ }
+
+ private void ThrowIfDisposed()
+ {
+ if (_isDisposed)
+ {
+ throw new ObjectDisposedException(nameof(LogicLooperPool));
+ }
}
private class PooledLogicLooper : ILogicLooper
diff --git a/test/LogicLooper.Test/LogicLooperPoolTest.cs b/test/LogicLooper.Test/LogicLooperPoolTest.cs
index d98c3c2..15a58e4 100644
--- a/test/LogicLooper.Test/LogicLooperPoolTest.cs
+++ b/test/LogicLooper.Test/LogicLooperPoolTest.cs
@@ -141,6 +141,34 @@ public async Task PooledLogicLooper_ShutdownAsyncFromLooperPool()
Assert.True(looperFactory.CreatedLoopers[3].IsShutdownRequested);
}
+ [Fact]
+ public async Task OperationsAfterDispose_ThrowObjectDisposedException()
+ {
+ var looperFactory = new FakeLogicLooperPoolLooperFactory();
+ var pool = new LogicLooperPool(60, 4, RoundRobinLogicLooperPoolBalancer.Instance, looperFactory);
+
+ pool.Dispose();
+
+ LogicLooperActionDelegate action = (in LogicLooperActionContext _) => false;
+ LogicLooperActionWithStateDelegate actionWithState = (in LogicLooperActionContext _, int _) => false;
+ LogicLooperAsyncActionDelegate asyncAction = _ => ValueTask.FromResult(false);
+ LogicLooperAsyncActionWithStateDelegate asyncActionWithState = (_, _) => ValueTask.FromResult(false);
+
+ Assert.Throws(() => { _ = pool.RegisterActionAsync(action); });
+ Assert.Throws(() => { _ = pool.RegisterActionAsync(action, LooperActionOptions.Default); });
+ Assert.Throws(() => { _ = pool.RegisterActionAsync(actionWithState, 0); });
+ Assert.Throws(() => { _ = pool.RegisterActionAsync(actionWithState, 0, LooperActionOptions.Default); });
+ Assert.Throws(() => { _ = pool.RegisterActionAsync(asyncAction); });
+ Assert.Throws(() => { _ = pool.RegisterActionAsync(asyncAction, LooperActionOptions.Default); });
+ Assert.Throws(() => { _ = pool.RegisterActionAsync(asyncActionWithState, 0); });
+ Assert.Throws(() => { _ = pool.RegisterActionAsync(asyncActionWithState, 0, LooperActionOptions.Default); });
+ Assert.Throws(() => pool.GetLooper());
+ Assert.Throws(() => _ = pool.Loopers);
+ await Assert.ThrowsAsync(() => pool.ShutdownAsync(TimeSpan.Zero));
+
+ pool.Dispose();
+ }
+
class FakeSequentialLogicLooperPoolBalancer : ILogicLooperPoolBalancer
{
private int _count;