@@ -186,83 +186,92 @@ private static SslClientAuthenticationOptions ConstructSslOptions(HttpConnection
186186 TimeSpan pooledConnectionIdleTimeout = _poolManager . Settings . _pooledConnectionIdleTimeout ;
187187 DateTimeOffset now = DateTimeOffset . UtcNow ;
188188 List < CachedConnection > list = _idleConnections ;
189- lock ( SyncObj )
189+
190+ // Try to get a cached connection. If we can and if it's usable, return it. If we can but it's not usable,
191+ // try again. And if we can't because there aren't any valid ones, create a new one and return it.
192+ while ( true )
190193 {
191- // Try to return a cached connection. We need to loop in case the connection
192- // we get from the list is unusable.
193- while ( list . Count > 0 )
194+ CachedConnection cachedConnection ;
195+ lock ( SyncObj )
194196 {
195- CachedConnection cachedConnection = list [ list . Count - 1 ] ;
196- HttpConnection conn = cachedConnection . _connection ;
197-
198- list . RemoveAt ( list . Count - 1 ) ;
199- if ( cachedConnection . IsUsable ( now , pooledConnectionLifetime , pooledConnectionIdleTimeout ) &&
200- ! conn . EnsureReadAheadAndPollRead ( ) )
197+ if ( list . Count > 0 )
201198 {
202- // We found a valid connection. Return it.
203- if ( NetEventSource . IsEnabled ) conn . Trace ( "Found usable connection in pool." ) ;
204- return Task . FromResult < ( HttpConnection , HttpResponseMessage ) > ( ( conn , null ) ) ;
199+ // Pop off the next connection to try. We'll test it outside of the lock
200+ // to avoid doing expensive validation while holding the lock.
201+ cachedConnection = list [ list . Count - 1 ] ;
202+ list . RemoveAt ( list . Count - 1 ) ;
205203 }
206-
207- // We got a connection, but it was already closed by the server or the
208- // server sent unexpected data or the connection is too old. In any case,
209- // we can't use the connection, so get rid of it and try again.
210- if ( NetEventSource . IsEnabled ) conn . Trace ( "Found invalid connection in pool." ) ;
211- conn . Dispose ( ) ;
212- }
213-
214- // No valid cached connections, so we need to create a new one. If
215- // there's no limit on the number of connections associated with this
216- // pool, or if we haven't reached such a limit, simply create a new
217- // connection.
218- if ( _associatedConnectionCount < _maxConnections )
219- {
220- if ( NetEventSource . IsEnabled ) Trace ( "Creating new connection for pool." ) ;
221- IncrementConnectionCountNoLock ( ) ;
222- return WaitForCreatedConnectionAsync ( CreateConnectionAsync ( request , cancellationToken ) ) ;
223- }
224- else
225- {
226- // There is a limit, and we've reached it, which means we need to
227- // wait for a connection to be returned to the pool or for a connection
228- // associated with the pool to be dropped before we can create a
229- // new one. Create a waiter object and register it with the pool; it'll
230- // be signaled with the created connection when one is returned or
231- // space is available and the provided creation func has successfully
232- // created the connection to be used.
233- if ( NetEventSource . IsEnabled ) Trace ( "Limit reached. Waiting to create new connection." ) ;
234- var waiter = new ConnectionWaiter ( this , request , cancellationToken ) ;
235- EnqueueWaiter ( waiter ) ;
236- if ( cancellationToken . CanBeCanceled )
204+ else
237205 {
238- // If cancellation could be requested, register a callback for it that'll cancel
239- // the waiter and remove the waiter from the queue. Note that this registration needs
240- // to happen under the reentrant lock and after enqueueing the waiter.
241- waiter . _cancellationTokenRegistration = cancellationToken . Register ( s =>
206+ // No valid cached connections, so we need to create a new one. If
207+ // there's no limit on the number of connections associated with this
208+ // pool, or if we haven't reached such a limit, simply create a new
209+ // connection.
210+ if ( _associatedConnectionCount < _maxConnections )
242211 {
243- var innerWaiter = ( ConnectionWaiter ) s ;
244- lock ( innerWaiter . _pool . SyncObj )
212+ if ( NetEventSource . IsEnabled ) Trace ( "Creating new connection for pool." ) ;
213+ IncrementConnectionCountNoLock ( ) ;
214+ return WaitForCreatedConnectionAsync ( CreateConnectionAsync ( request , cancellationToken ) ) ;
215+ }
216+ else
217+ {
218+ // There is a limit, and we've reached it, which means we need to
219+ // wait for a connection to be returned to the pool or for a connection
220+ // associated with the pool to be dropped before we can create a
221+ // new one. Create a waiter object and register it with the pool; it'll
222+ // be signaled with the created connection when one is returned or
223+ // space is available and the provided creation func has successfully
224+ // created the connection to be used.
225+ if ( NetEventSource . IsEnabled ) Trace ( "Limit reached. Waiting to create new connection." ) ;
226+ var waiter = new ConnectionWaiter ( this , request , cancellationToken ) ;
227+ EnqueueWaiter ( waiter ) ;
228+ if ( cancellationToken . CanBeCanceled )
245229 {
246- // If it's in the list, remove it and cancel it.
247- if ( innerWaiter . _pool . RemoveWaiterForCancellation ( innerWaiter ) )
230+ // If cancellation could be requested, register a callback for it that'll cancel
231+ // the waiter and remove the waiter from the queue. Note that this registration needs
232+ // to happen under the reentrant lock and after enqueueing the waiter.
233+ waiter . _cancellationTokenRegistration = cancellationToken . Register ( s =>
248234 {
249- bool canceled = innerWaiter . TrySetCanceled ( innerWaiter . _cancellationToken ) ;
250- Debug . Assert ( canceled ) ;
251- }
235+ var innerWaiter = ( ConnectionWaiter ) s ;
236+ lock ( innerWaiter . _pool . SyncObj )
237+ {
238+ // If it's in the list, remove it and cancel it.
239+ if ( innerWaiter . _pool . RemoveWaiterForCancellation ( innerWaiter ) )
240+ {
241+ bool canceled = innerWaiter . TrySetCanceled ( innerWaiter . _cancellationToken ) ;
242+ Debug . Assert ( canceled ) ;
243+ }
244+ }
245+ } , waiter ) ;
252246 }
253- } , waiter ) ;
247+ return waiter . Task ;
248+ }
249+
250+ // Note that we don't check for _disposed. We may end up disposing the
251+ // created connection when it's returned, but we don't want to block use
252+ // of the pool if it's already been disposed, as there's a race condition
253+ // between getting a pool and someone disposing of it, and we don't want
254+ // to complicate the logic about trying to get a different pool when the
255+ // retrieved one has been disposed of. In the future we could alternatively
256+ // try returning such connections to whatever pool is currently considered
257+ // current for that endpoint, if there is one.
254258 }
255- return waiter . Task ;
256259 }
257260
258- // Note that we don't check for _disposed. We may end up disposing the
259- // created connection when it's returned, but we don't want to block use
260- // of the pool if it's already been disposed, as there's a race condition
261- // between getting a pool and someone disposing of it, and we don't want
262- // to complicate the logic about trying to get a different pool when the
263- // retrieved one has been disposed of. In the future we could alternatively
264- // try returning such connections to whatever pool is currently considered
265- // current for that endpoint, if there is one.
261+ HttpConnection conn = cachedConnection . _connection ;
262+ if ( cachedConnection . IsUsable ( now , pooledConnectionLifetime , pooledConnectionIdleTimeout ) &&
263+ ! conn . EnsureReadAheadAndPollRead ( ) )
264+ {
265+ // We found a valid connection. Return it.
266+ if ( NetEventSource . IsEnabled ) conn . Trace ( "Found usable connection in pool." ) ;
267+ return Task . FromResult < ( HttpConnection , HttpResponseMessage ) > ( ( conn , null ) ) ;
268+ }
269+
270+ // We got a connection, but it was already closed by the server or the
271+ // server sent unexpected data or the connection is too old. In any case,
272+ // we can't use the connection, so get rid of it and loop around to try again.
273+ if ( NetEventSource . IsEnabled ) conn . Trace ( "Found invalid connection in pool." ) ;
274+ conn . Dispose ( ) ;
266275 }
267276 }
268277
0 commit comments