Skip to content

CI probe: concurrent aclose_forcefully() on UDP sockets and Process - #1

Open
graingert-coef wants to merge 8 commits into
masterfrom
test/udp-aclose-forcefully-windows
Open

CI probe: concurrent aclose_forcefully() on UDP sockets and Process#1
graingert-coef wants to merge 8 commits into
masterfrom
test/udp-aclose-forcefully-windows

Conversation

@graingert-coef

Copy link
Copy Markdown
Owner

Not for upstream — pushed to run the Windows CI matrix on a hypothesis.

Question

anyio's UDPSocket.aclose() / ConnectedUDPSocket.aclose() await self._protocol.closed_event.wait() unshielded. Under aclose_forcefully() that wait is cancelled — instrumenting it shows aclose() returning with closed_event never set — but on selector loops the fd is already released by then, because _call_connection_lost is queued one callback ahead of the cancelled task's resumption.

So the defect is invisible on Linux/macOS. Windows should differ:

  • _ProactorDatagramTransport.sendto() always buffers and starts an overlapped op, setting _write_fut (proactor_events.py:501-506).
  • _ProactorBasePipeTransport.close() only schedules _call_connection_lost if not self._buffer and self._write_fut is None (:108).
  • With a send in flight it is instead scheduled from _loop_writing (:524-525) — an IOCP completion callback on a later loop iteration.

That should remove the margin, so both closers see a live fd.

Expected results

Test Expectation
TestTCPStream::test_concurrent_aclose_forcefully_waits_for_socket_close passes everywhere — control for agronholm#1277's fix
Test{UDPSocket,ConnectedUDPSocket}::..._waits_for_socket_close passes everywhere
Test{UDPSocket,ConnectedUDPSocket}::..._with_pending_send fails on windows-latest, passes elsewhere
test_concurrent_aclose_forcefully_waits_for_process_exit passes everywhere

Windows 3.10 is also of interest: _force_close's _called_connection_lost guard (:140) looks like a 3.11 addition, which may matter for whether agronholm#1277's abort() is effective for the second concurrent caller.

If the pending-send variants pass on Windows too, the UDP wait is unshielded but unobservable, and the tests are guards rather than regression tests.

Guflly and others added 8 commits July 27, 2026 00:06
Extends the TCP regression test from agronholm#1277 to UDPSocket, ConnectedUDPSocket
and Process.

The plain variants pass on master too, so they are guards rather than
regression tests. The pending-send variants target Windows specifically:
_ProactorDatagramTransport.sendto() always buffers and starts an overlapped
operation, so transport.close() declines to schedule connection_lost while
_write_fut is pending, deferring it to an IOCP completion on a later loop
iteration. That should expose UDPSocket.aclose()'s unshielded wait on
closed_event, which is invisible on selector loops.

Pushed to run the Windows CI matrix; not intended for upstream as-is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/anyio/_backends/_asyncio.py
The previous versions queued sends and then entered a task group, but
start_soon() only schedules — the children first run when the host yields at
__aexit__, and one event loop iteration is all the proactor needs to reap the
overlapped send. close() then took the no-pending-write path and the tests
passed on Windows for the wrong reason.

anyio puts send()'s checkpoint before transport.sendto(), so the operation is
only still in flight if nothing yields afterwards. Drop the pointless 10x send
loop (each iteration yielded, so the buffer drained every time) and close
directly after a single send, as PR agronholm#1246's own reproducer does.

Adds a concurrent variant gated on an Event so the second closer only runs once
the first has yielded inside aclose(), which is where its unshielded wait on
closed_event is exposed. Neither PR covers that caller.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants