Move async socket readiness to WSAPoll backends
Historical reference only:
stale-v0.72a-experimental-cleanandanalysis\stale-v0.72a-experimental-cleanare retired reference sources, not active branch targets or current baselines. Use them only as provenance or idea-extraction sources; landed status is determined againstmain. See Historical References.
Summary¶
Move eMuleBB's async socket readiness away from legacy helper-window /
WSAAsyncSelect dispatch and toward explicit WSAPoll-owned backends.
The first concrete implementation target remains UDP via
CAsyncDatagramSocket, because that is the available experimental reference
and the least invasive way to remove helper-window readiness from high-volume
datagram paths. The backlog scope is now explicitly broader: after the UDP
bridge is validated, the TCP CAsyncSocketEx readiness path should be audited
and either aligned to the same WSAPoll ownership model or retained with a
documented reason.
CClientUDPSocket and CUDPSocket currently use WSAAsyncSelect
(window-message-based socket readiness) inherited from CAsyncSocketEx. This
model is:
- Tied to the hidden helper window of
CAsyncSocketEx, a legacy Win32 design - Serialized through the main thread's message pump for readiness events
- Single-threaded for all UDP I/O readiness notifications
- Subject to message-queue back-pressure under high load
The experimental branch implements CAsyncDatagramSocket, a new class that owns a UDP socket
on a dedicated WSAPoll backend thread, marshalling readiness back to the main thread via
PostMessage only for protocol handling. This eliminates the helper-window dependency for
UDP while retaining backward compatibility with the protocol handler layer.
The async-socket modernization lane should use this UDP work as the first repeatable pattern, then review TCP readiness, listen/accept, connect, close, and send-buffer paths for the same helper-window and message-queue pressure risks.
Scope Update - Shared Async Socket Lane¶
The implementation should be treated as staged socket infrastructure work:
- UDP WSAPoll bridge:
introduce or revalidate
CAsyncDatagramSocket, switchCClientUDPSocketandCUDPSocket, and preserve app-thread protocol handling. - TCP readiness audit:
document the current
CAsyncSocketExhelper-window, readiness, connect, close, and send paths against currentmain. - TCP WSAPoll alignment: if the audit confirms the helper-window path is still active or still a bottleneck, move TCP readiness to the same explicit poll backend while preserving current socket owner and teardown semantics.
- Lifecycle hardening: ensure socket registration, deregistration, close, and owner teardown cannot dispatch callbacks into freed socket or owner objects.
- Future IOCP decision: keep IOCP or a larger socket rewrite as a separate future decision. WSAPoll is the pragmatic compatibility-preserving bridge, not necessarily the final high-scale Windows socket design.
Experimental Reference Implementation¶
Source: stale-v0.72a-experimental-clean, commit b6ffaa6 FIX: move UDP sockets onto
the WSAPoll backend (2026-04-01, 595 insertions / 378 deletions)
New files:
- srchybrid/AsyncDatagramSocket.h — CAsyncDatagramSocket class inheriting from
CAsyncSocketEx, owning a WSAPoll-based receive thread
- srchybrid/AsyncDatagramSocket.cpp — implementation (101 lines)
- srchybrid/AsyncDatagramSocketSeams.h — test seam interface (24 lines)
Modified files (major changes):
- srchybrid/ClientUDPSocket.cpp — switched to CAsyncDatagramSocket; reorganized
send/receive paths (245 lines changed)
- srchybrid/UDPSocket.cpp — CUDPSocket switched to WSAPoll backend (391 lines changed)
- srchybrid/AsyncSocketEx.cpp / .h — small extensions to support the datagram subclass
- srchybrid/EmuleDlg.cpp / .h — WSAPoll backend lifecycle management
Architecture:
[Old] UDP socket → WSAAsyncSelect → helper window → WM_SOCKET → OnReceive/OnSend
[New] UDP socket → WSAPoll thread → PostMessage(WM_UDPREADY) → OnReceive/OnSend
The WSAPoll thread polls the socket fd with a configurable timeout, fires PostMessage to
the main window on readiness, and the main thread calls RecvFrom/SendTo as before.
Benefits¶
- Removes UDP I/O from the main message pump hot path
- WSAPoll scales better than WSAAsyncSelect under many concurrent events
- Matches the community-0.72 direction (they use IOCP for disk; WSAPoll is the next step)
- Prerequisite for any future multi-socket or IPv6/dual-stack work
- Eliminates the hidden helper window dependency for UDP (partial WWMOD_013 resolution)
- Creates one documented async-socket readiness pattern for later TCP review
- Makes socket stress evidence easier to reason about by separating readiness ownership from UI message dispatch
Dependencies And Related Work¶
- WWMOD_013 — historical WSAAsyncSelect networking model: REF-029 resolves the UDP
half of this; TCP (
CAsyncSocketEx) remains on WSAAsyncSelect until REF-008 (Boost.Asio) - CI-001 (CMake) — easier to validate cross-platform with CMake + CTest
- BUG-017 (UDP throttler deadlock) — the WSAPoll migration changes the send path; port BUG-017 fix before or in the same PR
- FEAT-035 — IPv6 dual-stack work should not grow the old readiness model; this refactor is a prerequisite or companion for broad socket-family changes
- CI-019, CI-021, and CI-029 — socket adversity and leak-churn evidence should be reused or extended for this migration
Acceptance Criteria¶
- [ ]
CAsyncDatagramSocketimplemented and passes smoke tests - [ ]
CClientUDPSocketswitched toCAsyncDatagramSocket - [ ]
CUDPSocketswitched toCAsyncDatagramSocket - [ ] Helper window not used for UDP readiness events
- [ ] TCP
CAsyncSocketExreadiness, connect, send, close, and teardown paths audited against currentmain - [ ] If TCP remains on helper-window dispatch, the reason is documented with measurable risk and migration blockers
- [ ] If TCP is moved to WSAPoll, socket callbacks preserve existing owner threading and lifetime contracts
- [ ] No regression in Kad UDP (send/receive, obfuscation, throughput)
- [ ] No regression in eMule UDP (QueueFull, ReAsk, FileNotFound)
- [ ] Raw socket adversity, WebSocket/REST socket churn, and live UDP protocol smoke evidence pass after migration
Validation¶
- Focused native socket/unit tests for registration, readiness, close, and teardown edge cases.
- UDP protocol parity smoke for Kad and server/client UDP paths.
- REST/WebSocket raw socket adversity smoke if TCP readiness is touched.
- Live E2E release socket adversity suites when the change touches shared socket lifecycle or shutdown behavior.