Skip to content

Commit 1651c84

Browse files
committed
gh-142403: Avoid leaking file descriptor in socket.py
1 parent 2db9573 commit 1651c84

File tree

2 files changed

+39
-37
lines changed

2 files changed

+39
-37
lines changed

Lib/socket.py

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -376,45 +376,46 @@ def _sendfile_zerocopy(self, zerocopy_func, giveup_exc_type, file,
376376
# poll/select have the advantage of not requiring any
377377
# extra file descriptor, contrarily to epoll/kqueue
378378
# (also, they require a single syscall).
379-
if hasattr(selectors, 'PollSelector'):
380-
selector = selectors.PollSelector()
381-
else:
382-
selector = selectors.SelectSelector()
383-
selector.register(sockno, selectors.EVENT_WRITE)
384-
385379
total_sent = 0
386-
# localize variable access to minimize overhead
387-
selector_select = selector.select
388380
try:
389-
while True:
390-
if timeout and not selector_select(timeout):
391-
raise TimeoutError('timed out')
392-
if count:
393-
blocksize = min(count - total_sent, blocksize)
394-
if blocksize <= 0:
395-
break
396-
try:
397-
sent = zerocopy_func(fileno, offset, blocksize)
398-
except BlockingIOError:
399-
if not timeout:
400-
# Block until the socket is ready to send some
401-
# data; avoids hogging CPU resources.
402-
selector_select()
403-
continue
404-
except OSError as err:
405-
if total_sent == 0:
406-
# We can get here for different reasons, the main
407-
# one being 'file' is not a regular mmap(2)-like
408-
# file, in which case we'll fall back on using
409-
# plain send().
410-
raise giveup_exc_type(err)
411-
raise err from None
412-
else:
413-
if sent == 0:
414-
break # EOF
415-
offset += sent
416-
total_sent += sent
417-
return total_sent
381+
with (
382+
selectors.PollSelector()
383+
if hasattr(selectors, 'PollSelector')
384+
else selectors.SelectSelector()
385+
) as selector:
386+
selector.register(sockno, selectors.EVENT_WRITE)
387+
388+
# localize variable access to minimize overhead
389+
selector_select = selector.select
390+
while True:
391+
if timeout and not selector_select(timeout):
392+
raise TimeoutError('timed out')
393+
if count:
394+
blocksize = min(count - total_sent, blocksize)
395+
if blocksize <= 0:
396+
break
397+
try:
398+
sent = zerocopy_func(fileno, offset, blocksize)
399+
except BlockingIOError:
400+
if not timeout:
401+
# Block until the socket is ready to send some
402+
# data; avoids hogging CPU resources.
403+
selector_select()
404+
continue
405+
except OSError as err:
406+
if total_sent == 0:
407+
# We can get here for different reasons, the main
408+
# one being 'file' is not a regular mmap(2)-like
409+
# file, in which case we'll fall back on using
410+
# plain send().
411+
raise giveup_exc_type(err)
412+
raise err from None
413+
else:
414+
if sent == 0:
415+
break # EOF
416+
offset += sent
417+
total_sent += sent
418+
return total_sent
418419
finally:
419420
if total_sent > 0 and hasattr(file, 'seek'):
420421
file.seek(offset)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Stop leaking the selector file descriptor, and release it when done.

0 commit comments

Comments
 (0)