@@ -595,7 +595,14 @@ def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int:
595595 self ._buffer = self ._buffer [bytes_to_read :]
596596 # explicitly recheck num_to_read to avoid extra checks
597597 continue
598-
598+ # We need to read the socket status here before seeing if any bytes are available.
599+ # Otherwise, we have a bad race condition in the if/elif/... logic below
600+ # that can cause recv_into to fail when the other side closes the connection after
601+ # sending bytes. The problem is that initially we can have num_avail=0 while the
602+ # socket state is not in CLOSED or CLOSED_WAIT. Then after the if but before the elif
603+ # that checks the socket state, bytes arrive and the other end closes the connection.
604+ # So now bytes are available but we see the CLOSED/CLOSED_WAIT state and ignore them.
605+ status_before_getting_available = self ._status
599606 num_avail = self ._available ()
600607 if num_avail > 0 :
601608 last_read_time = ticks_ms ()
@@ -610,7 +617,9 @@ def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int:
610617 elif num_read > 0 :
611618 # We got a message, but there are no more bytes to read, so we can stop.
612619 break
613- elif self ._status in {
620+ # See note where we set status_before_getting_available for why we can't just check
621+ # _status here
622+ elif status_before_getting_available in {
614623 wiznet5k .adafruit_wiznet5k .SNSR_SOCK_CLOSED ,
615624 wiznet5k .adafruit_wiznet5k .SNSR_SOCK_CLOSE_WAIT ,
616625 }:
0 commit comments