@@ -161,66 +161,6 @@ public void ThrowIfNotInitialized()
161
161
public override string ToString ( ) => State . ToString ( ) ;
162
162
}
163
163
164
- internal sealed class MaintenanceHelper : IDisposable
165
- {
166
- private CancellationTokenSource _cancellationTokenSource = null ;
167
- private readonly Action < CancellationToken > _maintenanceAction ;
168
- private Thread _maintenanceThread ;
169
- private readonly TimeSpan _interval ;
170
-
171
- public MaintenanceHelper ( Action < CancellationToken > maintenanceAction , TimeSpan interval )
172
- {
173
- _interval = interval ;
174
- _maintenanceAction = Ensure . IsNotNull ( maintenanceAction , nameof ( maintenanceAction ) ) ;
175
- }
176
-
177
- public bool IsRunning => _maintenanceThread != null ;
178
-
179
- public void Cancel ( )
180
- {
181
- if ( _interval == Timeout . InfiniteTimeSpan )
182
- {
183
- return ;
184
- }
185
-
186
- CancelAndDispose ( ) ;
187
- _cancellationTokenSource = null ;
188
- _maintenanceThread = null ;
189
- // the previous _maintenanceThread might not be stopped yet, but it will be soon
190
- }
191
-
192
- public void Start ( )
193
- {
194
- if ( _interval == Timeout . InfiniteTimeSpan )
195
- {
196
- return ;
197
- }
198
-
199
- CancelAndDispose ( ) ;
200
- _cancellationTokenSource = new CancellationTokenSource ( ) ;
201
- var cancellationToken = _cancellationTokenSource . Token ;
202
-
203
- _maintenanceThread = new Thread ( new ParameterizedThreadStart ( ThreadStart ) ) { IsBackground = true } ;
204
- _maintenanceThread . Start ( cancellationToken ) ;
205
-
206
- void ThreadStart ( object cancellationToken )
207
- {
208
- _maintenanceAction ( ( CancellationToken ) cancellationToken ) ;
209
- }
210
- }
211
-
212
- public void Dispose ( )
213
- {
214
- CancelAndDispose ( ) ;
215
- }
216
-
217
- private void CancelAndDispose ( )
218
- {
219
- _cancellationTokenSource ? . Cancel ( ) ;
220
- _cancellationTokenSource ? . Dispose ( ) ;
221
- }
222
- }
223
-
224
164
private sealed class AcquireConnectionHelper : IDisposable
225
165
{
226
166
// private fields
@@ -406,7 +346,7 @@ private void HandleException(Exception ex)
406
346
}
407
347
}
408
348
409
- private sealed class PooledConnection : IConnection , ICheckOutReasonTracker
349
+ internal sealed class PooledConnection : IConnection , ICheckOutReasonTracker
410
350
{
411
351
private CheckOutReason ? _checkOutReason ;
412
352
private readonly IConnection _connection ;
@@ -712,11 +652,13 @@ private void ThrowIfDisposed()
712
652
}
713
653
}
714
654
715
- private sealed class ListConnectionHolder
655
+ internal sealed class ListConnectionHolder
716
656
{
717
657
private readonly SemaphoreSlimSignalable _semaphoreSlimSignalable ;
718
658
private readonly object _lock = new object ( ) ;
659
+ private readonly object _lockInUse = new object ( ) ;
719
660
private readonly List < PooledConnection > _connections ;
661
+ private readonly List < PooledConnection > _connectionsInUse ;
720
662
721
663
private readonly Action < ConnectionPoolRemovingConnectionEvent > _removingConnectionEventHandler ;
722
664
private readonly Action < ConnectionPoolRemovedConnectionEvent > _removedConnectionEventHandler ;
@@ -725,6 +667,7 @@ public ListConnectionHolder(IEventSubscriber eventSubscriber, SemaphoreSlimSigna
725
667
{
726
668
_semaphoreSlimSignalable = semaphoreSlimSignalable ;
727
669
_connections = new List < PooledConnection > ( ) ;
670
+ _connectionsInUse = new List < PooledConnection > ( ) ;
728
671
729
672
eventSubscriber . TryGetEventHandler ( out _removingConnectionEventHandler ) ;
730
673
eventSubscriber . TryGetEventHandler ( out _removedConnectionEventHandler ) ;
@@ -745,6 +688,7 @@ public void Clear()
745
688
{
746
689
lock ( _lock )
747
690
{
691
+ // In use Connections MUST be closed when they are checked in to the closed pool.
748
692
foreach ( var connection in _connections )
749
693
{
750
694
RemoveConnection ( connection ) ;
@@ -755,30 +699,44 @@ public void Clear()
755
699
}
756
700
}
757
701
758
- public void Prune ( CancellationToken cancellationToken )
702
+ public void Prune ( int ? maxExpiredGenerationInUse , CancellationToken cancellationToken )
759
703
{
760
- PooledConnection [ ] expiredConnections ;
761
- lock ( _lock )
704
+ RemoveExpiredConnections ( _connections , generation : null , _lock , signal : true ) ;
705
+
706
+ if ( maxExpiredGenerationInUse . HasValue )
762
707
{
763
- expiredConnections = _connections . Where ( c => c . IsExpired ) . ToArray ( ) ;
708
+ RemoveExpiredConnections ( _connectionsInUse , generation : maxExpiredGenerationInUse . Value , _lockInUse , signal : false ) ;
764
709
}
765
710
766
- foreach ( var connection in expiredConnections )
711
+ void RemoveExpiredConnections ( List < PooledConnection > connections , int ? generation , object @lock , bool signal )
767
712
{
768
- cancellationToken . ThrowIfCancellationRequested ( ) ;
713
+ PooledConnection [ ] expiredConnections ;
714
+ lock ( @lock )
715
+ {
716
+ expiredConnections = connections . Where ( c => c . IsExpired && ( generation == null || c . Generation <= generation ) ) . ToArray ( ) ;
717
+ }
769
718
770
- lock ( _lock )
719
+ foreach ( var connection in expiredConnections )
771
720
{
772
- // At this point connection is always expired and might be disposed
773
- // If connection is already disposed the removal logic was already executed
774
- if ( connection . IsDisposed )
721
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
722
+
723
+ lock ( @lock )
775
724
{
776
- continue ;
777
- }
725
+ // At this point connection is always expired and might be disposed
726
+ // If connection is already disposed the removal logic was already executed
727
+ if ( connection . IsDisposed )
728
+ {
729
+ continue ;
730
+ }
778
731
779
- RemoveConnection ( connection ) ;
780
- _connections . Remove ( connection ) ;
781
- SignalOrReset ( ) ;
732
+ RemoveConnection ( connection ) ;
733
+ connections . Remove ( connection ) ;
734
+
735
+ if ( signal )
736
+ {
737
+ SignalOrReset ( ) ;
738
+ }
739
+ }
782
740
}
783
741
}
784
742
}
@@ -806,11 +764,26 @@ public PooledConnection Acquire()
806
764
807
765
SignalOrReset ( ) ;
808
766
}
767
+
768
+ if ( result != null )
769
+ {
770
+ TrackInUseConnection ( result ) ;
771
+
772
+ // This connection can be expired and not disposed by Prune. Dispose if needed
773
+ if ( result . IsExpired )
774
+ {
775
+ RemoveConnection ( result ) ;
776
+ result = null ;
777
+ }
778
+ }
779
+
809
780
return result ;
810
781
}
811
782
812
783
public void Return ( PooledConnection connection )
813
784
{
785
+ UntrackInUseConnection ( connection ) ;
786
+
814
787
lock ( _lock )
815
788
{
816
789
_connections . Add ( connection ) ;
@@ -826,6 +799,7 @@ public void RemoveConnection(PooledConnection connection)
826
799
}
827
800
828
801
var stopwatch = Stopwatch . StartNew ( ) ;
802
+ UntrackInUseConnection ( connection ) ; // no op if connection is not in use
829
803
connection . Dispose ( ) ;
830
804
stopwatch . Stop ( ) ;
831
805
@@ -849,9 +823,25 @@ private void SignalOrReset()
849
823
_semaphoreSlimSignalable . Signal ( ) ;
850
824
}
851
825
}
826
+
827
+ public void TrackInUseConnection ( PooledConnection connection )
828
+ {
829
+ lock ( _lockInUse )
830
+ {
831
+ _connectionsInUse . Add ( connection ) ;
832
+ }
833
+ }
834
+
835
+ public void UntrackInUseConnection ( PooledConnection connection )
836
+ {
837
+ lock ( _lockInUse )
838
+ {
839
+ _connectionsInUse . Remove ( connection ) ;
840
+ }
841
+ }
852
842
}
853
843
854
- private sealed class ConnectionCreator : IDisposable
844
+ internal sealed class ConnectionCreator : IDisposable
855
845
{
856
846
private readonly ExclusiveConnectionPool _pool ;
857
847
private readonly TimeSpan _connectingTimeout ;
@@ -888,8 +878,7 @@ public PooledConnection CreateOpened(CancellationToken cancellationToken)
888
878
_pool . CreateTimeoutException ( stopwatch , $ "Timed out waiting for in connecting queue after { stopwatch . ElapsedMilliseconds } ms.") ;
889
879
}
890
880
891
- var connection = CreateOpenedInternal ( cancellationToken ) ;
892
- return connection ;
881
+ return CreateOpenedInternal ( cancellationToken ) ;
893
882
}
894
883
catch ( Exception ex )
895
884
{
@@ -992,9 +981,10 @@ public void Dispose()
992
981
_pool . _maxConnectingQueue . Release ( ) ;
993
982
}
994
983
995
- if ( _disposeConnection )
984
+ if ( _disposeConnection && _connection != null )
996
985
{
997
- _connection ? . Dispose ( ) ;
986
+ _pool . ConnectionHolder . UntrackInUseConnection ( _connection ) ;
987
+ _connection . Dispose ( ) ;
998
988
}
999
989
}
1000
990
@@ -1029,7 +1019,10 @@ private void StartCreating(CancellationToken cancellationToken)
1029
1019
cancellationToken . ThrowIfCancellationRequested ( ) ;
1030
1020
1031
1021
_stopwatch = Stopwatch . StartNew ( ) ;
1032
- _connection = _pool . CreateNewConnection ( ) ;
1022
+
1023
+ var connection = _pool . CreateNewConnection ( ) ;
1024
+ _pool . ConnectionHolder . TrackInUseConnection ( connection ) ;
1025
+ _connection = connection ;
1033
1026
}
1034
1027
1035
1028
private void FinishCreating ( ConnectionDescription description )
0 commit comments