@@ -316,108 +316,90 @@ async def _async_receive(conn: socket.socket, length: int, loop: AbstractEventLo
316
316
return mv
317
317
318
318
319
- if "PyPy" not in sys .version :
320
-
321
- def receive_data (conn : Connection , length : int , deadline : Optional [float ]) -> memoryview :
322
- buf = bytearray (length )
323
- mv = memoryview (buf )
324
- bytes_read = 0
325
- # To support cancelling a network read, we shorten the socket timeout and
326
- # check for the cancellation signal after each timeout. Alternatively we
327
- # could close the socket but that does not reliably cancel recv() calls
328
- # on all OSes.
329
- orig_timeout = conn .conn .gettimeout ()
330
- try :
331
- while bytes_read < length :
332
- if deadline is not None :
333
- # CSOT: Update timeout. When the timeout has expired perform one
334
- # final non-blocking recv. This helps avoid spurious timeouts when
335
- # the response is actually already buffered on the client.
336
- short_timeout = min (max (deadline - time .monotonic (), 0 ), _POLL_TIMEOUT )
337
- else :
338
- short_timeout = _POLL_TIMEOUT
339
- conn .set_conn_timeout (short_timeout )
340
- try :
341
- chunk_length = conn .conn .recv_into (mv [bytes_read :])
342
- except BLOCKING_IO_ERRORS :
343
- if conn .cancel_context .cancelled :
344
- raise _OperationCancelled ("operation cancelled" ) from None
345
- # We reached the true deadline.
346
- raise socket .timeout ("timed out" ) from None
347
- except socket .timeout :
348
- if conn .cancel_context .cancelled :
349
- raise _OperationCancelled ("operation cancelled" ) from None
350
- continue
351
- except OSError as exc :
352
- if conn .cancel_context .cancelled :
353
- raise _OperationCancelled ("operation cancelled" ) from None
354
- if _errno_from_exception (exc ) == errno .EINTR :
355
- continue
356
- raise
357
- if chunk_length == 0 :
358
- raise OSError ("connection closed" )
319
+ _PYPY = "PyPy" in sys .version
359
320
360
- bytes_read += chunk_length
361
- finally :
362
- conn .set_conn_timeout (orig_timeout )
363
-
364
- return mv
365
- else :
366
321
367
- def wait_for_read (conn : Connection , deadline : Optional [float ]) -> None :
368
- """Block until at least one byte is read, or a timeout, or a cancel."""
369
- sock = conn .conn
370
- timed_out = False
371
- # Check if the connection's socket has been manually closed
372
- if sock .fileno () == - 1 :
373
- return
374
- while True :
375
- # SSLSocket can have buffered data which won't be caught by select.
376
- if hasattr (sock , "pending" ) and sock .pending () > 0 :
377
- readable = True
322
+ def wait_for_read (conn : Connection , deadline : Optional [float ]) -> None :
323
+ """Block until at least one byte is read, or a timeout, or a cancel."""
324
+ sock = conn .conn
325
+ timed_out = False
326
+ # Check if the connection's socket has been manually closed
327
+ if sock .fileno () == - 1 :
328
+ return
329
+ while True :
330
+ # SSLSocket can have buffered data which won't be caught by select.
331
+ if hasattr (sock , "pending" ) and sock .pending () > 0 :
332
+ readable = True
333
+ else :
334
+ # Wait up to 500ms for the socket to become readable and then
335
+ # check for cancellation.
336
+ if deadline :
337
+ remaining = deadline - time .monotonic ()
338
+ # When the timeout has expired perform one final check to
339
+ # see if the socket is readable. This helps avoid spurious
340
+ # timeouts on AWS Lambda and other FaaS environments.
341
+ if remaining <= 0 :
342
+ timed_out = True
343
+ timeout = max (min (remaining , _POLL_TIMEOUT ), 0 )
378
344
else :
379
- # Wait up to 500ms for the socket to become readable and then
380
- # check for cancellation.
381
- if deadline :
382
- remaining = deadline - time .monotonic ()
383
- # When the timeout has expired perform one final check to
384
- # see if the socket is readable. This helps avoid spurious
385
- # timeouts on AWS Lambda and other FaaS environments.
386
- if remaining <= 0 :
387
- timed_out = True
388
- timeout = max (min (remaining , _POLL_TIMEOUT ), 0 )
389
- else :
390
- timeout = _POLL_TIMEOUT
391
- readable = conn .socket_checker .select (sock , read = True , timeout = timeout )
392
- if conn .cancel_context .cancelled :
393
- raise _OperationCancelled ("operation cancelled" )
394
- if readable :
395
- return
396
- if timed_out :
397
- raise socket .timeout ("timed out" )
345
+ timeout = _POLL_TIMEOUT
346
+ readable = conn .socket_checker .select (sock , read = True , timeout = timeout )
347
+ if conn .cancel_context .cancelled :
348
+ raise _OperationCancelled ("operation cancelled" )
349
+ if readable :
350
+ return
351
+ if timed_out :
352
+ raise socket .timeout ("timed out" )
398
353
399
- def receive_data (conn : Connection , length : int , deadline : Optional [float ]) -> memoryview :
400
- buf = bytearray (length )
401
- mv = memoryview (buf )
402
- bytes_read = 0
354
+
355
+ def receive_data (conn : Connection , length : int , deadline : Optional [float ]) -> memoryview :
356
+ buf = bytearray (length )
357
+ mv = memoryview (buf )
358
+ bytes_read = 0
359
+ # To support cancelling a network read, we shorten the socket timeout and
360
+ # check for the cancellation signal after each timeout. Alternatively we
361
+ # could close the socket but that does not reliably cancel recv() calls
362
+ # on all OSes.
363
+ # When the timeout has expired we perform one final non-blocking recv.
364
+ # This helps avoid spurious timeouts when the response is actually already
365
+ # buffered on the client.
366
+ orig_timeout = conn .conn .gettimeout ()
367
+ try :
403
368
while bytes_read < length :
404
369
try :
405
- wait_for_read (conn , deadline )
406
- # CSOT: Update timeout. When the timeout has expired perform one
407
- # final non-blocking recv. This helps avoid spurious timeouts when
408
- # the response is actually already buffered on the client.
409
- if _csot .get_timeout () and deadline is not None :
410
- conn .set_conn_timeout (max (deadline - time .monotonic (), 0 ))
370
+ # Use the legacy wait_for_read cancellation approach on PyPy due to PYTHON-5011.
371
+ if _PYPY :
372
+ wait_for_read (conn , deadline )
373
+ if _csot .get_timeout () and deadline is not None :
374
+ conn .set_conn_timeout (max (deadline - time .monotonic (), 0 ))
375
+ else :
376
+ if deadline is not None :
377
+ short_timeout = min (max (deadline - time .monotonic (), 0 ), _POLL_TIMEOUT )
378
+ else :
379
+ short_timeout = _POLL_TIMEOUT
380
+ conn .set_conn_timeout (short_timeout )
381
+
411
382
chunk_length = conn .conn .recv_into (mv [bytes_read :])
412
383
except BLOCKING_IO_ERRORS :
384
+ if conn .cancel_context .cancelled :
385
+ raise _OperationCancelled ("operation cancelled" ) from None
386
+ # We reached the true deadline.
413
387
raise socket .timeout ("timed out" ) from None
388
+ except socket .timeout :
389
+ if conn .cancel_context .cancelled :
390
+ raise _OperationCancelled ("operation cancelled" ) from None
391
+ continue
414
392
except OSError as exc :
393
+ if conn .cancel_context .cancelled :
394
+ raise _OperationCancelled ("operation cancelled" ) from None
415
395
if _errno_from_exception (exc ) == errno .EINTR :
416
396
continue
417
397
raise
418
398
if chunk_length == 0 :
419
399
raise OSError ("connection closed" )
420
400
421
401
bytes_read += chunk_length
402
+ finally :
403
+ conn .set_conn_timeout (orig_timeout )
422
404
423
- return mv
405
+ return mv
0 commit comments