Skip to content

WebSocket shutdown can close the termination event while accepted clients still wait on it

Summary

Accepted WebSocket client threads wait on the global s_hTerminate handle, but shutdown only waits for the listener thread before closing that handle. If any accepted client thread is still alive, it can continue waiting on a handle that has been closed by another thread.

This blocks Beta 0.7.3 because Win32 explicitly does not make cross-thread handle close while waiting safe; shutdown can become nondeterministic under active Web UI or REST clients.

Evidence

  • srchybrid/WebSocket.cpp:360 includes s_hTerminate in accepted-client wait handles.
  • srchybrid/WebSocket.cpp:516 creates one accepted-client CWinThread per accepted socket.
  • srchybrid/WebSocket.cpp:522 does not store the accepted thread for later join/drain.
  • srchybrid/WebSocket.cpp:644 closes s_hTerminate after listener shutdown, with no accepted-client join.

Execution Plan

  1. Revalidate the current thread ownership model for listener and accepted WebSocket threads.
  2. Introduce explicit accepted-client lifetime tracking or another narrow ownership mechanism that lets shutdown know when client threads have exited.
  3. Signal termination first, then wake listener and accepted clients.
  4. Wait for accepted-client exits before closing the shared termination event.
  5. Ensure failure paths for thread creation close sockets and free SocketData.
  6. Add shutdown coverage with at least one idle accepted connection and one request in progress.

Acceptance Criteria

  • s_hTerminate is never closed while accepted-client threads can wait on it.
  • WebServer shutdown completes cleanly with active HTTP and REST clients.
  • No accepted-client thread leaks past WebServer shutdown.
  • No behavior change to request routing or Web UI responses.

Validation

  • 2026-05-08: Done in app commit aa66699.
  • python -m emule_workspace validate --workspace-root .
  • python -m emule_workspace build app --workspace-root . --config Release --platform x64 --variant main
  • Code validation: accepted WebSocket client CWinThread objects are tracked with m_bAutoDelete = FALSE, joined/reaped before s_hTerminate closes, and the termination handle is kept open if any socket thread fails to exit.