@@ -32,6 +32,9 @@ struct PoolConfiguration: Sendable {
3232
3333 @usableFromInline
3434 var idleTimeoutDuration : Duration = . seconds( 30 )
35+
36+ @usableFromInline
37+ var maximumConcurrentConnectionRequests : Int = 20
3538}
3639
3740@usableFromInline
@@ -90,6 +93,7 @@ struct PoolStateMachine<
9093
9194 case scheduleTimers( Max2Sequence < Timer > )
9295 case makeConnection( ConnectionRequest , TinyFastSequence < TimerCancellationToken > )
96+ case makeConnectionsCancelAndScheduleTimers( TinyFastSequence < ConnectionRequest > , TinyFastSequence < TimerCancellationToken > , Max2Sequence < Timer > )
9397 case runKeepAlive( Connection , TimerCancellationToken ? )
9498 case cancelTimers( TinyFastSequence < TimerCancellationToken > )
9599 case closeConnection( Connection , Max2Sequence < TimerCancellationToken > )
@@ -312,6 +316,12 @@ struct PoolStateMachine<
312316 request: requestAction,
313317 connection: . none
314318 )
319+ } else if self . connections. stats. connecting >= self . configuration. maximumConcurrentConnectionRequests {
320+ // We have too many connection requests, lets delay creating any new connections
321+ return . init(
322+ request: requestAction,
323+ connection: . none
324+ )
315325 } else if let request = self . connections. createNewDemandConnectionIfPossible ( ) {
316326 // Can we create a demand connection
317327 return . init(
@@ -673,9 +683,20 @@ struct PoolStateMachine<
673683 let requests = self . requestQueue. pop ( max: availableContext. info. availableStreams)
674684 if !requests. isEmpty {
675685 let leaseResult = self . connections. leaseConnection ( at: index, streams: UInt16 ( requests. count) )
686+ let connectionsRequired : Int
687+ if self . requestQueue. count <= self . connections. stats. availableStreams + self . connections. stats. leasedStreams {
688+ connectionsRequired = self . configuration. minimumConnectionCount - Int( self . connections. stats. active)
689+ } else {
690+ connectionsRequired = 1
691+ }
692+ let connectionAction = self . createMultipleConnectionsAction (
693+ connectionsRequired,
694+ cancelledTimers: . init( leaseResult. timersToCancel) ,
695+ scheduledTimers: [ ]
696+ ) ?? . cancelTimers( . init( leaseResult. timersToCancel) )
676697 return . init(
677698 request: . leaseConnection( requests, leaseResult. connection) ,
678- connection: . cancelTimers ( . init ( leaseResult . timersToCancel ) )
699+ connection: connectionAction
679700 )
680701 }
681702
@@ -704,10 +725,13 @@ struct PoolStateMachine<
704725 }
705726 let timers = self . connections. parkConnection ( at: index, hasBecomeIdle: newIdle) . map ( self . mapTimers)
706727
707- return . init(
708- request: . none,
709- connection: . scheduleTimers( timers)
710- )
728+ let connectionsRequired = self . configuration. minimumConnectionCount - Int( self . connections. stats. active)
729+ let connectionAction = self . createMultipleConnectionsAction (
730+ connectionsRequired,
731+ cancelledTimers: [ ] ,
732+ scheduledTimers: timers
733+ ) ?? . scheduleTimers( timers)
734+ return . init( request: . none, connection: connectionAction)
711735 }
712736
713737 case . overflow:
@@ -723,6 +747,31 @@ struct PoolStateMachine<
723747
724748 }
725749
750+ @inlinable
751+ /* private */ mutating func createMultipleConnectionsAction(
752+ _ connectionCount: Int ,
753+ cancelledTimers: TinyFastSequence < TimerCancellationToken > ,
754+ scheduledTimers: Max2Sequence < Timer >
755+ ) -> ConnectionAction ? {
756+ let connectionCountLimitedByNumberOfRequests = min (
757+ connectionCount,
758+ self . configuration. maximumConcurrentConnectionRequests - Int( self . connections. stats. connecting)
759+ )
760+ let connectionCountLimitedByHardLimit = min (
761+ connectionCountLimitedByNumberOfRequests,
762+ self . configuration. maximumConnectionHardLimit - Int( self . connections. stats. active)
763+ )
764+ guard connectionCountLimitedByHardLimit > 0 else { return nil }
765+
766+ var connectionRequests = TinyFastSequence < ConnectionRequest > ( )
767+ connectionRequests. reserveCapacity ( connectionCountLimitedByHardLimit)
768+ for _ in 0 ..< connectionCountLimitedByHardLimit {
769+ connectionRequests. append ( self . connections. createNewConnection ( ) )
770+ }
771+ return . makeConnectionsCancelAndScheduleTimers( connectionRequests, cancelledTimers, scheduledTimers)
772+
773+ }
774+
726775 @inlinable
727776 /* private */ func mapTimers( _ connectionTimer: ConnectionTimer ) -> Timer {
728777 switch connectionTimer. usecase {
@@ -796,9 +845,6 @@ extension PoolStateMachine {
796845@available ( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * )
797846extension PoolStateMachine . Action : Equatable where TimerCancellationToken: Equatable , Request: Equatable { }
798847
799- @available ( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * )
800- //extension PoolStateMachine.PoolState: Equatable {}
801-
802848@available ( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * )
803849extension PoolStateMachine . ConnectionAction : Equatable where TimerCancellationToken: Equatable {
804850 @usableFromInline
@@ -808,6 +854,9 @@ extension PoolStateMachine.ConnectionAction: Equatable where TimerCancellationTo
808854 return lhs == rhs
809855 case ( . makeConnection( let lhsRequest, let lhsToken) , . makeConnection( let rhsRequest, let rhsToken) ) :
810856 return lhsRequest == rhsRequest && lhsToken == rhsToken
857+ case ( . makeConnectionsCancelAndScheduleTimers( let lhsRequests, let lhsTokens, let lhsTimers) ,
858+ . makeConnectionsCancelAndScheduleTimers( let rhsRequests, let rhsTokens, let rhsTimers) ) :
859+ return lhsRequests == rhsRequests && lhsTokens == rhsTokens && lhsTimers == rhsTimers
811860 case ( . runKeepAlive( let lhsConn, let lhsToken) , . runKeepAlive( let rhsConn, let rhsToken) ) :
812861 return lhsConn === rhsConn && lhsToken == rhsToken
813862 case ( . closeConnection( let lhsConn, let lhsTimers) , . closeConnection( let rhsConn, let rhsTimers) ) :
0 commit comments