@@ -63,7 +63,27 @@ internal override bool IsRunning
63
63
64
64
internal override void Clear ( )
65
65
{
66
- //TODO
66
+ Interlocked . Increment ( ref _clearCounter ) ;
67
+
68
+ if ( Interlocked . CompareExchange ( ref _isClearing , 1 , 0 ) == 1 )
69
+ return ;
70
+
71
+ try
72
+ {
73
+ var count = _idleCount ;
74
+ while ( count > 0 && _idleConnectorReader . TryRead ( out var connector ) )
75
+ {
76
+ if ( CheckIdleConnector ( connector ) )
77
+ {
78
+ CloseConnector ( connector ) ;
79
+ count -- ;
80
+ }
81
+ }
82
+ }
83
+ finally
84
+ {
85
+ _isClearing = 0 ;
86
+ }
67
87
}
68
88
69
89
internal override bool TryGetConnection ( DbConnection owningObject , TaskCompletionSource < DbConnectionInternal > taskCompletionSource , DbConnectionOptions userOptions , out DbConnectionInternal ? connection )
@@ -113,8 +133,7 @@ internal override void PutObjectFromTransactedPool(DbConnectionInternal obj)
113
133
114
134
internal override void Startup ( )
115
135
{
116
- // NOTE: this occupies a thread for the whole duration of the warmup process.
117
- ThreadPool . QueueUserWorkItem ( async ( _ ) => { await WarmUp ( ) ; } ) ;
136
+ _ = WarmUp ( ) ;
118
137
}
119
138
120
139
internal override void Shutdown ( )
@@ -134,7 +153,6 @@ internal override void TransactionEnded(Transaction transaction, DbConnectionInt
134
153
#endregion
135
154
136
155
#region Implementation
137
-
138
156
private readonly DbConnectionPoolIdentity _identity ;
139
157
140
158
private readonly DbConnectionFactory _connectionFactory ;
@@ -180,7 +198,13 @@ internal override void TransactionEnded(Transaction transaction, DbConnectionInt
180
198
private Task _warmupTask ;
181
199
private readonly SemaphoreSlim _warmupLock ;
182
200
183
- private PoolPruningParameters _pruningParameters ;
201
+ internal int _minIdleCount ;
202
+ internal PeriodicTimer PruningTimer { get ; init ; }
203
+ internal PeriodicTimer MinIdleCountTimer { get ; init ; }
204
+ internal ValueTask PruningTimerListener { get ; init ; }
205
+ internal Task PruningTask { get ; set ; }
206
+ internal ValueTask UpdateMinIdleCountTask { get ; init ; }
207
+ internal SemaphoreSlim PruningLock { get ; init ; }
184
208
#endregion
185
209
186
210
// Counts the total number of open connectors tracked by the pool.
@@ -189,6 +213,13 @@ internal override void TransactionEnded(Transaction transaction, DbConnectionInt
189
213
// Counts the number of connectors currently sitting idle in the pool.
190
214
private volatile int _idleCount ;
191
215
216
+ /// <summary>
217
+ /// Incremented every time this pool is cleared. Allows us to identify connections which were
218
+ /// created before the clear.
219
+ /// </summary>
220
+ private volatile int _clearCounter ;
221
+ private volatile int _isClearing ;
222
+
192
223
/// <summary>
193
224
/// Initializes a new PoolingDataSource.
194
225
/// </summary>
@@ -200,6 +231,8 @@ internal BetterSyncPool(
200
231
DbConnectionPoolProviderInfo connectionPoolProviderInfo ,
201
232
RateLimiterBase connectionRateLimiter )
202
233
{
234
+ State = Initializing ;
235
+
203
236
_connectionRateLimiter = connectionRateLimiter ;
204
237
_connectionFactory = connectionFactory ;
205
238
_connectionPoolGroup = connectionPoolGroup ;
@@ -227,19 +260,20 @@ internal BetterSyncPool(
227
260
_warmupTask = Task . CompletedTask ;
228
261
_warmupLock = new SemaphoreSlim ( 1 ) ;
229
262
263
+ var pruningTimer = new PeriodicTimer ( DefaultPruningPeriod ) ;
230
264
231
- _pruningParameters = new PoolPruningParameters
232
- {
233
- _minIdleCount = int . MaxValue ,
234
-
235
- // TODO: base this on a user provided param?
236
- PruningTimer = new PeriodicTimer ( DefaultPruningPeriod ) ,
237
- MinIdleCountTimer = new PeriodicTimer ( MinIdleCountPeriod ) ,
238
- PruningLock = new SemaphoreSlim ( 1 ) ,
239
- PruningTimerListener = InitiatePruningTimerListener ( ) ,
240
- PruningTask = Task . CompletedTask ,
241
- UpdateMinIdleCountTask = UpdateMinIdleCount ( ) ,
242
- } ;
265
+ _minIdleCount = int . MaxValue ;
266
+
267
+ // TODO: make these private readonly if possible
268
+ // TODO: base pruning timer on a user provided param?
269
+ PruningTimer = pruningTimer ;
270
+ MinIdleCountTimer = new PeriodicTimer ( MinIdleCountPeriod ) ;
271
+ PruningLock = new SemaphoreSlim ( 1 ) ;
272
+ PruningTimerListener = InitiatePruningTimerListener ( ) ;
273
+ PruningTask = Task . CompletedTask ;
274
+ UpdateMinIdleCountTask = UpdateMinIdleCount ( ) ;
275
+
276
+ State = Running ;
243
277
}
244
278
245
279
#region properties
@@ -443,6 +477,7 @@ private void CloseConnector(DbConnectionInternal connector)
443
477
//TODO: log error
444
478
}
445
479
480
+ // TODO: check clear counter so that we don't clear new connections
446
481
447
482
int i ;
448
483
for ( i = 0 ; i < MaxPoolSize ; i++ )
@@ -523,10 +558,10 @@ internal readonly struct OpenInternalConnectionState
523
558
// It's better to block this thread and keep throughput high than to queue all of our opens onto a single worker thread.
524
559
// Add an async path when this support is added to DbConnectionInternal.
525
560
DbConnectionInternal ? newConnection = state. Pool. ConnectionFactory. CreatePooledConnection(
526
- state . Pool ,
527
- state . OwningConnection ,
528
- state . Pool . _connectionPoolGroup . ConnectionOptions ,
529
- state . Pool . _connectionPoolGroup . PoolKey ,
561
+ state . Pool ,
562
+ state . OwningConnection ,
563
+ state . Pool . _connectionPoolGroup . ConnectionOptions ,
564
+ state . Pool . _connectionPoolGroup . PoolKey ,
530
565
state . UserOptions ) ;
531
566
532
567
if ( newConnection == null )
@@ -599,7 +634,7 @@ internal async ValueTask InitiatePruningTimerListener()
599
634
{
600
635
_shutdownCT. ThrowIfCancellationRequested ( ) ;
601
636
602
- while ( await _pruningParameters . PruningTimer . WaitForNextTickAsync ( _shutdownCT ) )
637
+ while ( await PruningTimer . WaitForNextTickAsync ( _shutdownCT ) )
603
638
{
604
639
await await PruneIdleConnections( ) ;
605
640
}
@@ -613,35 +648,35 @@ internal async ValueTask InitiatePruningTimerListener()
613
648
/// <returns>A ValueTask containing a Task that represents the pruning operation.</returns>
614
649
internal async ValueTask< Task > PruneIdleConnections ( )
615
650
{
616
- if ( ! _pruningParameters . PruningTask . IsCompleted )
651
+ if ( ! PruningTask . IsCompleted )
617
652
{
618
- return _pruningParameters . PruningTask;
653
+ return PruningTask;
619
654
}
620
655
621
- await _pruningParameters . PruningLock . WaitAsync ( ) ;
656
+ await PruningLock . WaitAsync ( ) ;
622
657
623
658
try
624
659
{
625
- if ( _pruningParameters . PruningTask . IsCompleted )
660
+ if ( PruningTask . IsCompleted && State is Running )
626
661
{
627
- _pruningParameters . PruningTask = _PruneIdleConnections ( ) ;
662
+ PruningTask = _PruneIdleConnections ( ) ;
628
663
}
629
664
}
630
665
finally
631
666
{
632
- _pruningParameters . PruningLock . Release ( ) ;
667
+ PruningLock . Release ( ) ;
633
668
}
634
669
635
- return _pruningParameters . PruningTask ;
670
+ return PruningTask;
636
671
637
672
async Task _PruneIdleConnections ( )
638
673
{
639
674
try
640
675
{
641
- int numConnectionsToPrune = _pruningParameters . _minIdleCount;
676
+ int numConnectionsToPrune = _minIdleCount;
642
677
643
678
// Reset _minIdleCount for the next pruning period
644
- _pruningParameters . _minIdleCount = int . MaxValue;
679
+ _minIdleCount = int . MaxValue;
645
680
646
681
// If we don't stop on null, we might cycle a bit?
647
682
// we might read out all of the nulls we just wrote into the channel
@@ -686,22 +721,26 @@ internal async ValueTask UpdateMinIdleCount()
686
721
{
687
722
_shutdownCT . ThrowIfCancellationRequested ( ) ;
688
723
689
- while ( await _pruningParameters . MinIdleCountTimer. WaitForNextTickAsync( _shutdownCT) )
724
+ while ( await MinIdleCountTimer . WaitForNextTickAsync( _shutdownCT) )
690
725
{
726
+ if ( State is not Running)
727
+ {
728
+ continue ;
729
+ }
691
730
try
692
731
{
693
732
int currentMinIdle ;
694
733
int currentIdle ;
695
734
do
696
735
{
697
- currentMinIdle = _pruningParameters . _minIdleCount ;
736
+ currentMinIdle = _minIdleCount ;
698
737
currentIdle = _idleCount ;
699
738
if ( currentIdle > = currentMinIdle )
700
739
{
701
740
break ;
702
741
}
703
742
}
704
- while ( Interlocked . CompareExchange ( ref _pruningParameters . _minIdleCount , currentIdle , currentMinIdle ) != currentMinIdle ) ;
743
+ while ( Interlocked . CompareExchange ( ref _minIdleCount , currentIdle , currentMinIdle ) != currentMinIdle ) ;
705
744
}
706
745
catch
707
746
{
@@ -736,7 +775,7 @@ internal async ValueTask<Task> WarmUp()
736
775
{
737
776
// The task may have been started by another thread while we were
738
777
// waiting on the semaphore
739
- if ( _warmupTask . IsCompleted )
778
+ if ( _warmupTask . IsCompleted && State is Running )
740
779
{
741
780
_warmupTask = _WarmUp ( _shutdownCT ) ;
742
781
}
@@ -790,12 +829,14 @@ internal async ValueTask ShutdownAsync()
790
829
{
791
830
SqlClientEventSource. Log . TryPoolerTraceEvent ( "<prov.DbConnectionPool.Shutdown|RES|INFO|CPOOL> {0}" , ObjectID ) ;
792
831
832
+ State = ShuttingDown;
833
+
793
834
// Cancel background tasks
794
835
_shutdownCTS. Cancel ( ) ;
795
836
await Task. WhenAll (
796
- _pruningParameters . PruningTimerListener . AsTask ( ) ,
797
- _pruningParameters . PruningTask ,
798
- _pruningParameters . UpdateMinIdleCountTask . AsTask ( ) ,
837
+ PruningTimerListener . AsTask ( ) ,
838
+ PruningTask ,
839
+ UpdateMinIdleCountTask . AsTask ( ) ,
799
840
_warmupTask ) ;
800
841
801
842
// Clean pool state
@@ -804,24 +845,13 @@ await Task.WhenAll(
804
845
// Handle disposable resources
805
846
_shutdownCTS. Dispose ( ) ;
806
847
_warmupLock. Dispose ( ) ;
807
- _pruningParameters . PruningTimer . Dispose ( ) ;
808
- _pruningParameters . MinIdleCountTimer . Dispose ( ) ;
848
+ PruningTimer. Dispose ( ) ;
849
+ MinIdleCountTimer. Dispose ( ) ;
809
850
_connectionRateLimiter? . Dispose ( ) ;
810
851
}
811
852
812
853
// TODO: override clear method
813
854
#endregion
814
-
815
- internal struct PoolPruningParameters
816
- {
817
- internal int _minIdleCount;
818
- internal readonly PeriodicTimer PruningTimer { get ; init ; }
819
- internal readonly PeriodicTimer MinIdleCountTimer { get ; init ; }
820
- internal readonly ValueTask PruningTimerListener { get ; init ; }
821
- internal Task PruningTask { get ; set ; }
822
- internal readonly ValueTask UpdateMinIdleCountTask { get ; init ; }
823
- internal readonly SemaphoreSlim PruningLock { get ; init ; }
824
- }
825
855
}
826
856
}
827
857
#endif
0 commit comments