Skip to content

Localhost Relay: non-blocking IO on Linux side, IOCP with fully async reads and writes on Windows side#40171

Open
wangxin12 wants to merge 2 commits intomasterfrom
user/wangxin12/localhostrelay
Open

Localhost Relay: non-blocking IO on Linux side, IOCP with fully async reads and writes on Windows side#40171
wangxin12 wants to merge 2 commits intomasterfrom
user/wangxin12/localhostrelay

Conversation

@wangxin12
Copy link
Copy Markdown
Contributor

Summary of the Pull Request

Linux — non-blocking poll-based state machine:

  • Switch both sockets to O_NONBLOCK via fcntl
  • Per-direction RelayDirection buffers with head/tail cursors
  • poll() loop that independently tracks read-readiness and write-readiness per direction
  • Back-pressure via Available() > 0 guard (buffer full → stop reading → TCP flow control throttles sender)
  • Compact() to handle short writes (slides unwritten data to buffer front)
  • Proper TCP half-close: each direction drains independently, calls shutdown(SHUT_WR) when done
  • POLLIN checked before POLLERR/POLLHUP on read entries to drain buffered data at EOF

Windows — IOCP with fully async reads and writes:

  • Replace WaitForMultipleObjects + InterruptableWrite with CreateIoCompletionPort + GetQueuedCompletionStatus
  • Per-direction RelayDirection with separate IoContext (extended OVERLAPPED) for read and write
  • Pipelined I/O: async read and write can run concurrently on the same direction's buffer (read appends at Tail, write consumes from Head, no overlap)
  • Buffer reset guard: Head = Tail = 0 only when Pending() == 0 && !ReadPending to prevent offset corruption with concurrent async operations
  • Proper TCP half-close: shutdown(SD_SEND) when a direction completes, other direction continues
  • Scope guard cancels all pending I/O and drains IOCP on exit

Testing

  • Verified with full duplex TCP stress test (50,000 x 32KB frames each direction)
  • TCP half-close works end-to-end: one direction can finish while the other continues
  • No sequence errors, no data corruption
image image

Without the change, the test tool hangs. image

PR Checklist

Detailed Description of the Pull Request / Additional comments

Validation Steps Performed

Xin Wang (from Dev Box) added 2 commits April 14, 2026 02:04
@wangxin12 wangxin12 requested a review from a team as a code owner April 13, 2026 18:50
Copilot AI review requested due to automatic review settings April 13, 2026 18:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Refactors the localhost relay implementation to use non-blocking, back-pressure-aware I/O on Linux and IOCP-based fully asynchronous I/O on Windows, improving full-duplex throughput and correct half-close behavior.

Changes:

  • Windows: replaced wait/event-based overlapped I/O with IOCP dispatch and per-direction read/write state machines.
  • Linux: switched sockets to non-blocking mode and implemented a poll-driven relay with per-direction buffering and compaction.
  • Added per-direction half-close handling so each direction can complete independently.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/windows/common/relay.cpp Reworks BidirectionalRelay to use IOCP with per-direction async read/write contexts and completion processing.
src/linux/init/localhost.cpp Implements a non-blocking poll() relay loop with per-direction buffers, back-pressure, and half-close support.

@guanwenbin123
Copy link
Copy Markdown

guanwenbin123 commented Apr 13, 2026 via email

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