Files
RustPython/extra_tests/snippets/stdlib_socket_fork_accept.py
Jeong, YunWon f1b8b09492 Don't hold socket locks across blocking I/O (#8746)
accept/recv/send kept the socket RwLock during the syscall. After
fork, the child copied that held lock, so _ResourceSharer._afterfork
hung in listener.close().

Unskip fork connection pickling, pool task ref release, and spawn
thread-pool terminate. Add extra_tests for fork+accept close and
fork after DupFd.

Assisted-by: Grok:4.6
2026-09-20 10:34:09 +09:00

56 lines
1.3 KiB
Python

"""Closing a listening socket after fork must not hang.
A thread blocked in accept() must not keep the socket lock across the
syscall. After fork the child copies that lock; close() in the child
would wait forever for a thread that does not exist there.
"""
import os
import socket
import threading
import time
if not hasattr(os, "fork"):
print("skipped (no fork)")
raise SystemExit(0)
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind(("127.0.0.1", 0))
listener.listen(1)
started = threading.Event()
def accept_forever():
started.set()
try:
listener.accept()
except OSError:
pass
t = threading.Thread(target=accept_forever, daemon=True)
t.start()
assert started.wait(5)
# Give accept() time to enter the syscall.
time.sleep(0.05)
pid = os.fork()
if pid == 0:
listener.close()
os._exit(0)
_, status = os.waitpid(pid, 0)
assert os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0, status
# Wake the parent accept thread. Closing the fd from another thread is
# not a portable way to interrupt accept().
addr = listener.getsockname()
with socket.create_connection(addr, timeout=2):
pass
t.join(timeout=5)
assert not t.is_alive()
listener.close()
print("ok")