Repository navigation
fix(net): persistent epoll registration without reintroducing #22 - #23
Merged
Merged
Conversation
One-shot add_fd/remove_fd on every await was leftover churn after #22. Register once, re-arm with EPOLLONESHOT+MOD, and complete_io (clear waiters, no DEL) before set_value so a worker cannot lose its new handler. Drain the listen backlog on accept, default listen(SOMAXCONN), and add an accept-burst regression. Short-connection p99 under wave=32 is still mostly accept-queue physics. Co-authored-by: 李龙飞 <caixuf@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
After #22, persistent localhost echo is healthy (WSL ~43.7k req/s, fail=0). The remaining cost called out there still stood: every
Socket::read/write/accept/connectwait did one-shotadd_fd+remove_fd(EPOLL_CTL_ADD/DEL, replacehandlers_[fd]).That is extra syscalls on the steady-state path. It is also the shape of the #22 race: if the EventLoop thread
set_values (worker mayadd_fdthe next wait) and thenremove_fd, the new handler is erased and the waiter hangs.Short-connection
connect+echop99 underwave=32is a different bottleneck: oneaccept()per epoll event + listen-queue physics. ADD/DEL on the listen fd made that worse, but draining the backlog cannot make the tail of a 32-way handshake wave look like p50.Design
Keep the fd in the poll set for the life of the
Socket. Close stillremove_fd(DEL + erase).add_fd: first waitEPOLL_CTL_ADD(store handler before ctl soEPOLLONESHOTcannot fire with no waiter). Later waits update the existingIoEventHandlerin place andEPOLL_CTL_MOD. NoDEL.EPOLLONESHOT: kernel disables the fd after one event, so a registered fd with no waiter does not LT-spin. Re-arm is the nextadd_fd/MOD.complete_iobeforeset_value/set_exception(the fix(net): persistent localhost echo no longer times out #22 ordering): clear waiter callbacks and interest bits, do notDEL/erase. A CoroutinePool worker that resumes before the callback returns canadd_fda new waiter on the same handler slot.complete_iosetsevents=0and skips those fds when rebuilding the poll set (ONESHOT equivalent). Still compiles; no epoll.accept4untilEAGAINintopending_accepts_. Laterco_await accept()pops without another epoll wait. Defaultlisten(SOMAXCONN).Not done (on purpose): no CoroutinePool/Task rewrite; no timeout bumps; no fake p99 by shrinking
wave.Tests
get()timeouts) — fix(net): persistent localhost echo no longer times out #22 + re-arm.test_core).real_net_accept_burst: 16 concurrent connect+echo, 0 timeouts.How to verify (Linux; WSL is the performance source of truth)
Success:
Task::get timed out. Throughput on WSL is the number to quote — not a cloud VM.Do not treat cloud QPS as success. Loopback only; not NIC/wire; not the CPU-sim Echo rows in
professional_flowcoro_benchmark.