@@ -10,6 +10,7 @@ internal sealed class PoolingSessionSource<T> : ISessionSource where T : Pooling
1010{
1111 private readonly ConcurrentStack < T > _idleSessions = new ( ) ;
1212 private readonly ConcurrentQueue < TaskCompletionSource < T ? > > _waiters = new ( ) ;
13+ private readonly CancellationTokenSource _disposeCts = new ( ) ;
1314
1415 private readonly IPoolingSessionFactory < T > _sessionFactory ;
1516 private readonly int _minSessionSize ;
@@ -20,7 +21,9 @@ internal sealed class PoolingSessionSource<T> : ISessionSource where T : Pooling
2021 private readonly Timer _cleanerTimer ;
2122
2223 private volatile int _numSessions ;
23- private volatile int _waiterSize ;
24+ private volatile int _disposed ;
25+
26+ private bool IsDisposed => _disposed == 1 ;
2427
2528 public PoolingSessionSource (
2629 IPoolingSessionFactory < T > sessionFactory ,
@@ -43,10 +46,15 @@ YdbConnectionStringBuilder settings
4346 _cleanerTimer = new Timer ( CleanIdleSessions , this , _sessionIdleTimeout , _sessionIdleTimeout ) ;
4447 }
4548
46- public ValueTask < ISession > OpenSession ( CancellationToken cancellationToken = default ) =>
47- TryGetIdleSession ( out var session )
49+ public ValueTask < ISession > OpenSession ( CancellationToken cancellationToken = default )
50+ {
51+ if ( IsDisposed )
52+ throw new YdbException ( "Session Source is disposed." ) ;
53+
54+ return TryGetIdleSession ( out var session )
4855 ? new ValueTask < ISession > ( session )
4956 : RentAsync ( cancellationToken ) ;
57+ }
5058
5159 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
5260 private bool TryGetIdleSession ( [ NotNullWhen ( true ) ] out T ? session )
@@ -119,7 +127,14 @@ private async ValueTask<ISession> RentAsync(CancellationToken cancellationToken)
119127 continue ;
120128 }
121129
122- await using var _ = finalToken . Register ( ( ) => waiterTcs . TrySetCanceled ( ) , useSynchronizationContext : false ) ;
130+ await using var _ = finalToken . Register (
131+ ( ) => waiterTcs . TrySetCanceled ( ) ,
132+ useSynchronizationContext : false
133+ ) ;
134+ await using var disposedCancellationTokenRegistration = _disposeCts . Token . Register (
135+ ( ) => waiterTcs . TrySetException ( new YdbException ( "Session Source is disposed." ) ) ,
136+ useSynchronizationContext : false
137+ ) ;
123138 session = await waiterTcs . Task . ConfigureAwait ( false ) ;
124139
125140 if ( CheckIdleSession ( session ) || TryGetIdleSession ( out session ) )
@@ -174,10 +189,15 @@ private void WakeUpWaiter()
174189
175190 public void Return ( T session )
176191 {
177- if ( session . IsBroken )
192+ if ( session . IsBroken || IsDisposed )
178193 {
179194 CloseSession ( session ) ;
180195
196+ if ( IsDisposed )
197+ {
198+ _ = TryDisposeCore ( ) ;
199+ }
200+
181201 return ;
182202 }
183203
@@ -237,9 +257,27 @@ private static void CleanIdleSessions(object? state)
237257 }
238258 }
239259 }
260+
261+ public async ValueTask DisposeAsync ( )
262+ {
263+ if ( Interlocked . CompareExchange ( ref _disposed , 1 , 0 ) != 0 )
264+ {
265+ return ;
266+ }
267+
268+ await _cleanerTimer . DisposeAsync ( ) ;
269+ _disposeCts . Cancel ( ) ;
270+
271+ CleanIdleSessions ( _idleSessions ) ;
272+
273+ await TryDisposeCore ( ) ;
274+ }
275+
276+ private ValueTask TryDisposeCore ( ) =>
277+ _numSessions == 0 ? _sessionFactory . DisposeAsync ( ) : ValueTask . CompletedTask ;
240278}
241279
242- internal interface IPoolingSessionFactory < T > where T : PoolingSessionBase < T >
280+ internal interface IPoolingSessionFactory < T > : IAsyncDisposable where T : PoolingSessionBase < T >
243281{
244282 T NewSession ( PoolingSessionSource < T > source ) ;
245283}
0 commit comments