@@ -188,6 +188,9 @@ private async Task ExecuteAsync(HttpContext context, ConnectionDelegate connecti
188188 return ;
189189 }
190190
191+ // Create a new Tcs every poll to keep track of the poll finishing, so we can properly wait on previous polls
192+ var currentRequestTcs = new TaskCompletionSource < object > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
193+
191194 await connection . StateLock . WaitAsync ( ) ;
192195 try
193196 {
@@ -205,17 +208,17 @@ private async Task ExecuteAsync(HttpContext context, ConnectionDelegate connecti
205208 {
206209 var existing = connection . GetHttpContext ( ) ;
207210 Log . ConnectionAlreadyActive ( _logger , connection . ConnectionId , existing . TraceIdentifier ) ;
211+ }
208212
209- using ( connection . Cancellation )
210- {
211- // Cancel the previous request
212- connection . Cancellation ? . Cancel ( ) ;
213+ using ( connection . Cancellation )
214+ {
215+ // Cancel the previous request
216+ connection . Cancellation ? . Cancel ( ) ;
213217
214- // Wait for the previous request to drain
215- await connection . TransportTask ;
218+ // Wait for the previous request to drain
219+ await connection . PreviousPollTask ;
216220
217- Log . PollCanceled ( _logger , connection . ConnectionId , existing . TraceIdentifier ) ;
218- }
221+ connection . PreviousPollTask = currentRequestTcs . Task ;
219222 }
220223
221224 // Mark the connection as active
@@ -267,57 +270,49 @@ private async Task ExecuteAsync(HttpContext context, ConnectionDelegate connecti
267270
268271 var resultTask = await Task . WhenAny ( connection . ApplicationTask , connection . TransportTask ) ;
269272
270- var pollAgain = true ;
271-
272- // If the application ended before the transport task then we potentially need to end the connection
273- if ( resultTask == connection . ApplicationTask )
273+ try
274274 {
275- // Complete the transport (notifying it of the application error if there is one)
276- connection . Transport . Output . Complete ( connection . ApplicationTask . Exception ) ;
275+ var pollAgain = true ;
277276
278- // Wait for the transport to run
279- await connection . TransportTask ;
280-
281- // If the status code is a 204 it means the connection is done
282- if ( context . Response . StatusCode == StatusCodes . Status204NoContent )
277+ // If the application ended before the transport task then we potentially need to end the connection
278+ if ( resultTask == connection . ApplicationTask )
283279 {
284- // We should be able to safely dispose because there's no more data being written
285- // We don't need to wait for close here since we've already waited for both sides
286- await _manager . DisposeAndRemoveAsync ( connection , closeGracefully : false ) ;
280+ // Complete the transport (notifying it of the application error if there is one)
281+ connection . Transport . Output . Complete ( connection . ApplicationTask . Exception ) ;
287282
288- // Don't poll again if we've removed the connection completely
289- pollAgain = false ;
290- }
291- }
292- else if ( context . Response . StatusCode == StatusCodes . Status204NoContent )
293- {
294- // Don't poll if the transport task was canceled
295- pollAgain = false ;
296- }
283+ // Wait for the transport to run
284+ await connection . TransportTask ;
297285
298- if ( pollAgain )
299- {
300- // Otherwise, we update the state to inactive again and wait for the next poll
301- await connection . StateLock . WaitAsync ( ) ;
302- try
303- {
304- if ( connection . Status == HttpConnectionStatus . Active )
286+ // If the status code is a 204 it means the connection is done
287+ if ( context . Response . StatusCode == StatusCodes . Status204NoContent )
305288 {
306- // Mark the connection as inactive
307- connection . LastSeenUtc = DateTime . UtcNow ;
308-
309- connection . Status = HttpConnectionStatus . Inactive ;
289+ // We should be able to safely dispose because there's no more data being written
290+ // We don't need to wait for close here since we've already waited for both sides
291+ await _manager . DisposeAndRemoveAsync ( connection , closeGracefully : false ) ;
310292
311- // Dispose the cancellation token
312- connection . Cancellation ? . Dispose ( ) ;
313-
314- connection . Cancellation = null ;
293+ // Don't poll again if we've removed the connection completely
294+ pollAgain = false ;
315295 }
316296 }
317- finally
297+ else if ( context . Response . StatusCode == StatusCodes . Status204NoContent )
318298 {
319- connection . StateLock . Release ( ) ;
299+ // Don't poll if the transport task was canceled
300+ pollAgain = false ;
320301 }
302+
303+ if ( pollAgain )
304+ {
305+ // Mark the connection as inactive
306+ connection . LastSeenUtc = DateTime . UtcNow ;
307+
308+ connection . Status = HttpConnectionStatus . Inactive ;
309+ }
310+ }
311+ finally
312+ {
313+ // Artificial task queue
314+ // This will cause incoming polls to wait until the previous poll has finished updating internal state info
315+ currentRequestTcs . TrySetResult ( null ) ;
321316 }
322317 }
323318 }
0 commit comments