@@ -8,6 +8,7 @@ namespace PatternKit.Creational.ObjectPool;
88public sealed class ObjectPool < T > : IDisposable
99{
1010 private readonly ConcurrentQueue < T > _items = new ( ) ;
11+ private readonly object _gate = new ( ) ;
1112 private readonly Func < T > _factory ;
1213 private readonly Action < T > ? _onRent ;
1314 private readonly Action < T > ? _onReturn ;
@@ -34,17 +35,38 @@ private ObjectPool(Func<T> factory, Action<T>? onRent, Action<T>? onReturn, Func
3435 /// <summary>Rents an item from the pool. Dispose the returned lease to return the item.</summary>
3536 public ObjectPoolLease < T > Rent ( )
3637 {
37- ThrowIfDisposed ( ) ;
38-
3938 T value ;
40- if ( _items . TryDequeue ( out var pooled ) )
39+ var hasValue = false ;
40+ lock ( _gate )
4141 {
42- Interlocked . Decrement ( ref _retained ) ;
43- value = pooled ;
42+ ThrowIfDisposed ( ) ;
43+
44+ if ( _items . TryDequeue ( out var pooled ) )
45+ {
46+ Interlocked . Decrement ( ref _retained ) ;
47+ value = pooled ;
48+ hasValue = true ;
49+ }
50+ else
51+ {
52+ value = default ! ;
53+ }
4454 }
45- else
46- {
55+
56+ if ( ! hasValue )
4757 value = _factory ( ) ;
58+
59+ var disposeAfterRent = false ;
60+ lock ( _gate )
61+ {
62+ if ( _disposed )
63+ disposeAfterRent = true ;
64+ }
65+
66+ if ( disposeAfterRent )
67+ {
68+ DisposeIfNeeded ( value ) ;
69+ throw new ObjectDisposedException ( nameof ( ObjectPool < T > ) ) ;
4870 }
4971
5072 _onRent ? . Invoke ( value ) ;
@@ -53,44 +75,82 @@ public ObjectPoolLease<T> Rent()
5375
5476 internal void Return ( T value )
5577 {
56- if ( _disposed )
78+ var disposeImmediately = false ;
79+ lock ( _gate )
80+ {
81+ disposeImmediately = _disposed ;
82+ }
83+
84+ if ( disposeImmediately )
5785 {
5886 DisposeIfNeeded ( value ) ;
5987 return ;
6088 }
6189
62- _onReturn ? . Invoke ( value ) ;
63- if ( _shouldReturn is not null && ! _shouldReturn ( value ) )
90+ try
91+ {
92+ _onReturn ? . Invoke ( value ) ;
93+ if ( _shouldReturn is not null && ! _shouldReturn ( value ) )
94+ {
95+ DisposeIfNeeded ( value ) ;
96+ return ;
97+ }
98+ }
99+ catch
64100 {
65101 DisposeIfNeeded ( value ) ;
66- return ;
102+ throw ;
67103 }
68104
69- var retained = Interlocked . Increment ( ref _retained ) ;
70- if ( retained <= _maxRetained )
105+ var dispose = false ;
106+ lock ( _gate )
71107 {
72- _items . Enqueue ( value ) ;
73- return ;
108+ if ( _disposed )
109+ {
110+ dispose = true ;
111+ }
112+ else
113+ {
114+ var retained = Interlocked . Increment ( ref _retained ) ;
115+ if ( retained <= _maxRetained )
116+ {
117+ _items . Enqueue ( value ) ;
118+ return ;
119+ }
120+
121+ Interlocked . Decrement ( ref _retained ) ;
122+ dispose = true ;
123+ }
74124 }
75125
76- Interlocked . Decrement ( ref _retained ) ;
77- DisposeIfNeeded ( value ) ;
126+ if ( dispose )
127+ DisposeIfNeeded ( value ) ;
78128 }
79129
80130 /// <inheritdoc />
81131 public void Dispose ( )
82132 {
83- _disposed = true ;
84- while ( _items . TryDequeue ( out var value ) )
133+ var drained = new List < T > ( ) ;
134+ lock ( _gate )
85135 {
86- Interlocked . Decrement ( ref _retained ) ;
87- DisposeIfNeeded ( value ) ;
136+ if ( _disposed )
137+ return ;
138+
139+ _disposed = true ;
140+ while ( _items . TryDequeue ( out var value ) )
141+ {
142+ Interlocked . Decrement ( ref _retained ) ;
143+ drained . Add ( value ) ;
144+ }
88145 }
146+
147+ foreach ( var value in drained )
148+ DisposeIfNeeded ( value ) ;
89149 }
90150
91151 private void ThrowIfDisposed ( )
92152 {
93- if ( _disposed )
153+ if ( Volatile . Read ( ref _disposed ) )
94154 throw new ObjectDisposedException ( nameof ( ObjectPool < T > ) ) ;
95155 }
96156
0 commit comments