.known-couplings/windows-deferred-socket-close.md
The Windows asio backend (src/libponyrt/asio/sock_notify.c) uses ProcessSocketNotifications readiness, mirroring epoll, and closes subscribed sockets through a deferred handshake. A socket unsubscribe (pony_asio_event_unsubscribe) issues a PSN REMOVE and sets the event's removing marker but does not close the fd or dispose the event; the backend closes ev->fd and sends ASIO_DISPOSABLE only when it later dequeues the SOCK_NOTIFY_EVENT_REMOVE packet for it (closesocket itself emits no REMOVE, so closing earlier would strand the handshake — the fd and event leak and the owning actor never quiesces). The stdlib must therefore keep its subscribed-fd close sites POSIX-only: TCPConnection._close_event_fd/hard_close, UDPSocket._close, and TCPListener.close (packages/net/) all guard @pony_os_socket_close with ifdef not windows; only raw (never-subscribed) fds are closed via pony_os_socket_close on Windows. The C side reinforces this: os_connect/os_listen (src/libponyrt/lang/socket.c) do the connect/listen syscall before pony_asio_event_create, so every C failure-path close is on a raw fd. Add a subscribed-fd close on Windows, or move the backend's close off the REMOVE packet, and you reintroduce the leak or a double-close, invisibly to a Linux/macOS maintainer. Run on a Windows host: make test (stdlib-debug and stdlib-release — the net suite) and make test-core (the live_asio_events == 0 actor-destroy assert in debug is the lifecycle oracle for the disposal handshake).