Skip to content

Commit 3ee108d

Browse files
authored
Merge pull request #53 from Cysharp/codex/throw-after-pool-disposal
Throw ObjectDisposedException after looper pool disposal
2 parents ed87115 + 175b72a commit 3ee108d

2 files changed

Lines changed: 56 additions & 4 deletions

File tree

src/LogicLooper/LogicLooperPool.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,17 @@ public sealed partial class LogicLooperPool : ILogicLooperPool, IDisposable
99
{
1010
private readonly PooledLogicLooper[] _loopers;
1111
private readonly ILogicLooperPoolBalancer _balancer;
12-
private readonly CancellationTokenSource _shutdownTokenSource = new();
12+
private bool _isDisposed;
1313

1414
/// <inheritdoc />
15-
public IReadOnlyList<ILogicLooper> Loopers => _loopers;
15+
public IReadOnlyList<ILogicLooper> Loopers
16+
{
17+
get
18+
{
19+
ThrowIfDisposed();
20+
return _loopers;
21+
}
22+
}
1623

1724
/// <summary>
1825
/// Initialize the looper pool with specified configurations.
@@ -101,15 +108,25 @@ public Task RegisterActionAsync<TState>(LogicLooperAsyncActionWithStateDelegate<
101108
/// <inheritdoc />
102109
public async Task ShutdownAsync(TimeSpan shutdownDelay)
103110
{
111+
ThrowIfDisposed();
104112
await Task.WhenAll(_loopers.Select(x => x.WrappedLooper.ShutdownAsync(shutdownDelay)));
105113
}
106114

107115
/// <inheritdoc />
108116
public ILogicLooper GetLooper()
109-
=> _balancer.GetPooledLooper(_loopers);
117+
{
118+
ThrowIfDisposed();
119+
return _balancer.GetPooledLooper(_loopers);
120+
}
110121

111122
public void Dispose()
112123
{
124+
if (_isDisposed)
125+
{
126+
return;
127+
}
128+
_isDisposed = true;
129+
113130
foreach (var looper in _loopers)
114131
{
115132
try
@@ -120,7 +137,14 @@ public void Dispose()
120137
{
121138
}
122139
}
123-
_shutdownTokenSource.Cancel();
140+
}
141+
142+
private void ThrowIfDisposed()
143+
{
144+
if (_isDisposed)
145+
{
146+
throw new ObjectDisposedException(nameof(LogicLooperPool));
147+
}
124148
}
125149

126150
private class PooledLogicLooper : ILogicLooper

test/LogicLooper.Test/LogicLooperPoolTest.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,34 @@ public async Task PooledLogicLooper_ShutdownAsyncFromLooperPool()
141141
Assert.True(looperFactory.CreatedLoopers[3].IsShutdownRequested);
142142
}
143143

144+
[Fact]
145+
public async Task OperationsAfterDispose_ThrowObjectDisposedException()
146+
{
147+
var looperFactory = new FakeLogicLooperPoolLooperFactory();
148+
var pool = new LogicLooperPool(60, 4, RoundRobinLogicLooperPoolBalancer.Instance, looperFactory);
149+
150+
pool.Dispose();
151+
152+
LogicLooperActionDelegate action = (in LogicLooperActionContext _) => false;
153+
LogicLooperActionWithStateDelegate<int> actionWithState = (in LogicLooperActionContext _, int _) => false;
154+
LogicLooperAsyncActionDelegate asyncAction = _ => ValueTask.FromResult(false);
155+
LogicLooperAsyncActionWithStateDelegate<int> asyncActionWithState = (_, _) => ValueTask.FromResult(false);
156+
157+
Assert.Throws<ObjectDisposedException>(() => { _ = pool.RegisterActionAsync(action); });
158+
Assert.Throws<ObjectDisposedException>(() => { _ = pool.RegisterActionAsync(action, LooperActionOptions.Default); });
159+
Assert.Throws<ObjectDisposedException>(() => { _ = pool.RegisterActionAsync(actionWithState, 0); });
160+
Assert.Throws<ObjectDisposedException>(() => { _ = pool.RegisterActionAsync(actionWithState, 0, LooperActionOptions.Default); });
161+
Assert.Throws<ObjectDisposedException>(() => { _ = pool.RegisterActionAsync(asyncAction); });
162+
Assert.Throws<ObjectDisposedException>(() => { _ = pool.RegisterActionAsync(asyncAction, LooperActionOptions.Default); });
163+
Assert.Throws<ObjectDisposedException>(() => { _ = pool.RegisterActionAsync(asyncActionWithState, 0); });
164+
Assert.Throws<ObjectDisposedException>(() => { _ = pool.RegisterActionAsync(asyncActionWithState, 0, LooperActionOptions.Default); });
165+
Assert.Throws<ObjectDisposedException>(() => pool.GetLooper());
166+
Assert.Throws<ObjectDisposedException>(() => _ = pool.Loopers);
167+
await Assert.ThrowsAsync<ObjectDisposedException>(() => pool.ShutdownAsync(TimeSpan.Zero));
168+
169+
pool.Dispose();
170+
}
171+
144172
class FakeSequentialLogicLooperPoolBalancer : ILogicLooperPoolBalancer
145173
{
146174
private int _count;

0 commit comments

Comments
 (0)