@@ -88,8 +88,10 @@ struct PoolStateMachine<
8888 case runKeepAlive( Connection , TimerCancellationToken ? )
8989 case cancelTimers( TinyFastSequence < TimerCancellationToken > )
9090 case closeConnection( Connection , Max2Sequence < TimerCancellationToken > )
91- case shutdown( Shutdown )
92-
91+ /// Start process of shutting down the connection pool. Close connections, cancel timers.
92+ case initiateShutdown( Shutdown )
93+ /// All connections have been closed, the pool event stream can be ended.
94+ case cancelEventStreamAndFinalCleanup( [ TimerCancellationToken ] )
9395 case none
9496 }
9597
@@ -256,11 +258,16 @@ struct PoolStateMachine<
256258 @inlinable
257259 mutating func connectionEstablished( _ connection: Connection , maxStreams: UInt16 ) -> Action {
258260 switch self . poolState {
259- case . running, . shuttingDown ( graceful : true ) :
261+ case . running:
260262 let ( index, context) = self . connections. newConnectionEstablished ( connection, maxStreams: maxStreams)
261263 return self . handleAvailableConnection ( index: index, availableContext: context)
262- case . shuttingDown( graceful: false ) , . shutDown:
263- return . init( request: . none, connection: . closeConnection( connection, [ ] ) )
264+
265+ case . shuttingDown:
266+ let ( index, context) = self . connections. newConnectionEstablished ( connection, maxStreams: maxStreams)
267+ return self . handleAvailableConnection ( index: index, availableContext: context)
268+
269+ case . shutDown:
270+ fatalError ( " Connection pool is not running " )
264271 }
265272 }
266273
@@ -326,7 +333,18 @@ struct PoolStateMachine<
326333 return . init( request: . none, connection: . scheduleTimers( . init( timer) ) )
327334
328335 case . shuttingDown( graceful: false ) , . shutDown:
329- return . none( )
336+ let timerToCancel = self . connections. destroyFailedConnection ( request. connectionID)
337+ let connectionAction : ConnectionAction
338+ if self . connections. isEmpty {
339+ self . poolState = . shutDown
340+ connectionAction = . cancelEventStreamAndFinalCleanup( timerToCancel. map { [ $0] } ?? [ ] )
341+ } else {
342+ connectionAction = . cancelTimers( timerToCancel. map { [ $0] } ?? [ ] )
343+ }
344+ return . init(
345+ request: . none,
346+ connection: connectionAction
347+ )
330348 }
331349 }
332350
@@ -348,7 +366,17 @@ struct PoolStateMachine<
348366 return . init( request: . none, connection: . makeConnection( request, timers) )
349367
350368 case . cancelTimers( let timers) :
351- return . init( request: . none, connection: . cancelTimers( . init( timers) ) )
369+ let connectionAction : ConnectionAction
370+ if self . connections. isEmpty {
371+ self . poolState = . shutDown
372+ connectionAction = . cancelEventStreamAndFinalCleanup( . init( timers) )
373+ } else {
374+ connectionAction = . cancelTimers( . init( timers) )
375+ }
376+ return . init(
377+ request: . none,
378+ connection: connectionAction
379+ )
352380 }
353381
354382 case . shuttingDown( graceful: false ) , . shutDown:
@@ -403,7 +431,7 @@ struct PoolStateMachine<
403431 case . running, . shuttingDown( graceful: true ) :
404432 self . cacheNoMoreConnectionsAllowed = false
405433
406- let closedConnectionAction = self . connections. connectionClosed ( connection. id)
434+ let closedConnectionAction = self . connections. connectionClosed ( connection. id, shuttingDown : false )
407435
408436 let connectionAction : ConnectionAction
409437 if let newRequest = closedConnectionAction. newConnectionRequest {
@@ -414,7 +442,19 @@ struct PoolStateMachine<
414442
415443 return . init( request: . none, connection: connectionAction)
416444
417- case . shuttingDown( graceful: false ) , . shutDown:
445+ case . shuttingDown( graceful: false ) :
446+ let closedConnectionAction = self . connections. connectionClosed ( connection. id, shuttingDown: true )
447+
448+ let connectionAction : ConnectionAction
449+ if self . connections. isEmpty {
450+ self . poolState = . shutDown
451+ connectionAction = . cancelEventStreamAndFinalCleanup( . init( closedConnectionAction. timersToCancel) )
452+ } else {
453+ connectionAction = . cancelTimers( closedConnectionAction. timersToCancel)
454+ }
455+
456+ return . init( request: . none, connection: connectionAction)
457+ case . shutDown:
418458 return . none( )
419459 }
420460 }
@@ -442,13 +482,17 @@ struct PoolStateMachine<
442482 var shutdown = ConnectionAction . Shutdown ( )
443483 self . connections. triggerForceShutdown ( & shutdown)
444484
445- if shutdown. connections. isEmpty {
485+ if self . connections . isEmpty , shutdown. connections. isEmpty {
446486 self . poolState = . shutDown
487+ return . init(
488+ request: . failRequests( self . requestQueue. removeAll ( ) , ConnectionPoolError . poolShutdown) ,
489+ connection: . cancelEventStreamAndFinalCleanup( shutdown. timersToCancel)
490+ )
447491 }
448492
449493 return . init(
450494 request: . failRequests( self . requestQueue. removeAll ( ) , ConnectionPoolError . poolShutdown) ,
451- connection: . shutdown ( shutdown)
495+ connection: . initiateShutdown ( shutdown)
452496 )
453497
454498 case . shuttingDown:
@@ -481,6 +525,22 @@ struct PoolStateMachine<
481525 return . none( )
482526
483527 case . idle( _, let newIdle) :
528+ if case . shuttingDown = self . poolState {
529+ switch self . connections. closeConnection ( at: index) {
530+ case . close( let closeAction) :
531+ return . init(
532+ request: . none,
533+ connection: . closeConnection( closeAction. connection, closeAction. timersToCancel)
534+ )
535+ case . cancelTimers( let timers) :
536+ return . init(
537+ request: . none,
538+ connection: . cancelTimers( . init( timers) )
539+ )
540+ case . doNothing:
541+ return . none( )
542+ }
543+ }
484544 let timers = self . connections. parkConnection ( at: index, hasBecomeIdle: newIdle) . map ( self . mapTimers)
485545
486546 return . init(
@@ -569,6 +629,9 @@ extension PoolStateMachine {
569629@available ( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * )
570630extension PoolStateMachine . Action : Equatable where TimerCancellationToken: Equatable , Request: Equatable { }
571631
632+ @available ( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * )
633+ extension PoolStateMachine . PoolState : Equatable { }
634+
572635@available ( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * )
573636extension PoolStateMachine . ConnectionAction : Equatable where TimerCancellationToken: Equatable {
574637 @usableFromInline
@@ -582,7 +645,9 @@ extension PoolStateMachine.ConnectionAction: Equatable where TimerCancellationTo
582645 return lhsConn === rhsConn && lhsToken == rhsToken
583646 case ( . closeConnection( let lhsConn, let lhsTimers) , . closeConnection( let rhsConn, let rhsTimers) ) :
584647 return lhsConn === rhsConn && lhsTimers == rhsTimers
585- case ( . shutdown( let lhs) , . shutdown( let rhs) ) :
648+ case ( . initiateShutdown( let lhs) , . initiateShutdown( let rhs) ) :
649+ return lhs == rhs
650+ case ( . cancelEventStreamAndFinalCleanup( let lhs) , . cancelEventStreamAndFinalCleanup( let rhs) ) :
586651 return lhs == rhs
587652 case ( . cancelTimers( let lhs) , . cancelTimers( let rhs) ) :
588653 return lhs == rhs
0 commit comments