kqueue: add speculative I/O pump and eliminate cached_initiator#138
Conversation
Add a speculative I/O fast path ("the pump") to the kqueue backend,
matching the epoll backend's design. read_some() and write_some() now
attempt the syscall before suspending the caller. On success, the
result is returned via symmetric transfer — bypassing the scheduler
queue, mutex, and reactor entirely.
An inline budget (default 2) limits consecutive inline completions
per scheduler-dispatched handler to prevent starvation of other
connections. Budget is reset each time a handler is popped from the
scheduler queue.
Replace the cached_initiator + do_read_io/do_write_io pattern with
register_op(), which directly parks operations in the descriptor_state
under the per-descriptor mutex. This eliminates a reusable coroutine
frame per socket, a redundant syscall on the EAGAIN path (the
initiator retried I/O after we already got EAGAIN), and the retry
loop complexity. The connect EINPROGRESS path also uses register_op()
now instead of inline registration logic.
📝 WalkthroughWalkthroughThis PR introduces an inline-budget mechanism to the kqueue scheduler to control consecutive inline operation completions. The kqueue_op operator() implementation is moved from header to cpp file, and speculative I/O is added to socket read/write operations with budget-aware fairness constraints and a new operation registration pathway. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Socket as kqueue_socket
participant Scheduler
participant Reactor
Client->>Socket: read_some()
activate Socket
Socket->>Socket: prepare_buffers()
Socket->>Socket: attempt speculative readv()
alt Data available
Socket->>Scheduler: try_consume_inline_budget()
alt Budget available
Scheduler-->>Socket: true (budget consumed)
Socket->>Socket: complete inline
Socket-->>Client: return data
else Budget exhausted
Scheduler-->>Socket: false
Socket->>Socket: register_op()
Socket->>Reactor: queue operation
Socket-->>Client: suspend coroutine
end
else No data (EAGAIN)
Socket->>Socket: register_op()
Socket->>Reactor: queue operation
Socket-->>Client: suspend coroutine
end
deactivate Socket
Reactor->>Socket: wakeup on readiness
activate Socket
Socket->>Scheduler: reset_inline_budget()
Scheduler-->>Socket: budget reset to 2
Socket->>Socket: operator()() - perform_io()
Socket->>Socket: complete operation
Socket-->>Client: resume coroutine
deactivate Socket
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts (beta)
No actionable comments were generated in the recent review. 🎉 🧹 Recent nitpick comments
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #138 +/- ##
===========================================
+ Coverage 81.03% 81.26% +0.22%
===========================================
Files 64 64
Lines 5685 5710 +25
===========================================
+ Hits 4607 4640 +33
+ Misses 1078 1070 -8 see 5 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
|
An automated preview of the documentation is available at https://138.corosio.prtest3.cppalliance.org/index.html If more commits are pushed to the pull request, the docs will rebuild at the same URL. 2026-02-12 23:35:38 UTC |
|
GCOVR code coverage report https://138.corosio.prtest3.cppalliance.org/gcovr/index.html Build time: 2026-02-12 23:39:15 UTC |
Add a speculative I/O fast path ("the pump") to the kqueue backend, matching the epoll backend's design. read_some() and write_some() now attempt the syscall before suspending the caller. On success, the result is returned via symmetric transfer — bypassing the scheduler queue, mutex, and reactor entirely.
An inline budget (default 2) limits consecutive inline completions per scheduler-dispatched handler to prevent starvation of other connections. Budget is reset each time a handler is popped from the scheduler queue.
Replace the cached_initiator + do_read_io/do_write_io pattern with register_op(), which directly parks operations in the descriptor_state under the per-descriptor mutex. This eliminates a reusable coroutine frame per socket, a redundant syscall on the EAGAIN path (the initiator retried I/O after we already got EAGAIN), and the retry loop complexity. The connect EINPROGRESS path also uses register_op() now instead of inline registration logic.
Summary by CodeRabbit
Performance
Refactor