Skip to content

kqueue: add speculative I/O pump and eliminate cached_initiator#138

Merged
mvandeberg merged 1 commit intocppalliance:developfrom
mvandeberg:feature/kqueue-pump
Feb 12, 2026
Merged

kqueue: add speculative I/O pump and eliminate cached_initiator#138
mvandeberg merged 1 commit intocppalliance:developfrom
mvandeberg:feature/kqueue-pump

Conversation

@mvandeberg
Copy link
Contributor

@mvandeberg mvandeberg commented Feb 12, 2026

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

    • Improved socket I/O efficiency through speculative read/write operations that attempt completion before suspension, reducing latency for immediate data availability.
  • Refactor

    • Restructured scheduler budget management with fairer inline execution constraints to prevent starvation in high-throughput scenarios.
    • Simplified internal operation registration and completion handling for socket operations.

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.
@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

This 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

Cohort / File(s) Summary
Inline Budget Mechanism
src/corosio/src/detail/kqueue/scheduler.hpp, src/corosio/src/detail/kqueue/scheduler.cpp
Introduces inline-budget infrastructure with max_inline_budget_ constant (value 2), plus reset_inline_budget() and try_consume_inline_budget() methods for per-context budget management.
Operation Implementation Migration
src/corosio/src/detail/kqueue/op.hpp, src/corosio/src/detail/kqueue/acceptors.cpp
Moves kqueue_op::operator() definition from header to sockets.cpp, reducing header dependencies; adds reset_inline_budget() call at start of kqueue_accept_op::operator().
Socket I/O Refactoring
src/corosio/src/detail/kqueue/sockets.hpp, src/corosio/src/detail/kqueue/sockets.cpp
Replaces do_read_io()/do_write_io() with new register_op() helper; introduces speculative I/O in read_some()/write_some(); implements kqueue_op/kqueue_connect_op operators with inline-budget-aware completion, result signaling, and reactor queuing logic.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • kqueue/epoll: make cancel() reliable when ops complete inline #132: Both PRs modify kqueue inline-completion paths and alter kqueue_op/kqueue_connect_op operator behavior affecting the same inline-completion code paths.
  • Add kqueue backend #114: Both PRs target the same kqueue backend implementation—adding/adjusting kqueue_accept_op::operator() and introducing inline-budget methods on kqueue_scheduler.
  • Symmetric transfer inlining #130: Both PRs introduce identical inline-budget mechanisms and move acceptor/op operator() implementations to call scheduler::reset_inline_budget (epoll backend in PR#130 vs kqueue here).

Suggested reviewers

  • sgerbino

Poem

🐰 Inline budgets now keep fairness true,
With speculative I/O pumped through,
No more spinning 'round the ring,
Just budget checks and register_op's swing,
The scheduler hops from fast to queue! 🎯

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (9 files):

⚔️ include/boost/corosio/timer.hpp (content)
⚔️ src/corosio/src/detail/kqueue/acceptors.cpp (content)
⚔️ src/corosio/src/detail/kqueue/op.hpp (content)
⚔️ src/corosio/src/detail/kqueue/scheduler.cpp (content)
⚔️ src/corosio/src/detail/kqueue/scheduler.hpp (content)
⚔️ src/corosio/src/detail/kqueue/sockets.cpp (content)
⚔️ src/corosio/src/detail/kqueue/sockets.hpp (content)
⚔️ src/corosio/src/detail/timer_service.cpp (content)
⚔️ src/corosio/src/timer.cpp (content)

These conflicts must be resolved before merging into develop.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main changes: adding a speculative I/O pump and eliminating cached_initiator.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch feature/kqueue-pump
  • Post resolved changes as copyable diffs in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
src/corosio/src/detail/kqueue/sockets.cpp (1)

148-158: Nit: redundant static_castsocket_impl_ is already kqueue_socket_impl*.

socket_impl_ is declared as kqueue_socket_impl* in kqueue_op (see op.hpp line 197), so the cast at line 158 is unnecessary.

Suggested simplification
-        static_cast<kqueue_socket_impl*>(socket_impl_)->set_endpoints(local_ep, target_endpoint);
+        socket_impl_->set_endpoints(local_ep, target_endpoint);

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.26%. Comparing base (477e92f) to head (93bae07).
⚠️ Report is 2 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             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.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 477e92f...93bae07. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cppalliance-bot
Copy link

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

@cppalliance-bot
Copy link

GCOVR code coverage report https://138.corosio.prtest3.cppalliance.org/gcovr/index.html
LCOV code coverage report https://138.corosio.prtest3.cppalliance.org/genhtml/index.html
Coverage Diff Report https://138.corosio.prtest3.cppalliance.org/diff-report/index.html

Build time: 2026-02-12 23:39:15 UTC

@mvandeberg mvandeberg merged commit 4777a92 into cppalliance:develop Feb 12, 2026
17 checks passed
@mvandeberg mvandeberg deleted the feature/kqueue-pump branch February 12, 2026 23:42
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.

2 participants