Skip to content

Tear down suspended coroutines through their chain's owner - #395

Merged
sgerbino merged 8 commits into
cppalliance:developfrom
sgerbino:pr/lifetime-executor
Oct 16, 2026
Merged

sgerbino merged 8 commits into
cppalliance:developfrom
sgerbino:pr/lifetime-executor

Conversation

@sgerbino

@sgerbino sgerbino commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • Destroying an io_context leaves the coroutines suspended in it suspended forever. That covers operations, timers, signal waits, blocking-pool work and queued work. Their frames, and the I/O objects in them, leak.
  • Opening, closing and running I/O objects allocates on every operation.
  • Several backends have lifetime and cancellation races.

Teardown

  • Every holder gives up a coroutine it will never resume with capy::abandon. The chain then unwinds from its owner, on the owner's executor. Destructors run, and no code after a suspension point runs.
  • The holders are reactor, io_uring and IOCP operations; timers; signal waits; resolver and file work on the blocking pool; the schedulers' queues; and tcp_server connections.
  • Schedulers wait for cancellations still posting, drain their queues, and destroy work posted after the drain.
  • An operation whose executor throws as it completes calls std::terminate. Nothing else could resume the coroutine or unwind it.
  • Work that crosses contexts must finish before either context is destroyed.

Pools

  • I/O object implementations come from per-type pools, held through counted references. An operation in flight keeps its object alive.
  • Accept nodes, file operations and connection launches are recycled. Steady-state I/O allocates nothing.

Fixes

  • All backends: awaiters resume on their own context; timer teardown races; signal delivery; TLS streams moved or destroyed mid-operation; accepted peers that are never claimed.
  • io_uring: cancellation, wakeups and run_for.
  • IOCP: cancellation, accept, adoption and wait-reactor races.
  • tcp_server: move, destroy and stop while running; workers returned at teardown.

Documentation

  • A new Lifetime and Teardown guide page, which ends with a checklist for operation authors.
  • A design note, doc/design/lifetime-contract.md, sets out when an implementation may be touched and when its memory may be reused.

Testing

Teardown tests run under ASan and LSan, and the full CI matrix is green. Commits before the teardown commit build, but their tests need the matching capy.

Resolves #380
Resolves #381
Resolves #384
Resolves #385
Resolves #390
Resolves #391
Resolves #392

run_task is entered without the scheduler lock when the caller has
just handed queued work to a peer thread, and the snapshot then walked
registered_descs_ while deregister_descriptor() erased from it under
that lock. Take the lock for the snapshot when the caller released it.
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Benchmark report

0a387cbd0493 (base) vs dbb0bed21a1a (head)

  • ⚠️ linux — 24 flagged (epoll, uring)
  • ⚠️ windows — 5 flagged (iocp)
  • ⚠️ macos — 3 flagged (kqueue)

⚠️ Flagged

Platform Backend Benchmark Metric Base Head Δ Noise
linux epoll accept_churn/sequential ops_per_sec 14.85K ops/s 15.40K ops/s 🟢▲ +3.96% 1.23%
linux uring accept_churn/burst/10 ops_per_sec 1.71K ops/s 1.82K ops/s 🟢▲ +6.23% 1.11%
linux uring accept_churn/burst/100 ops_per_sec 174.1 ops/s 187.0 ops/s 🟢▲ +7.30% 0.67%
linux uring accept_churn/burst_lockless/10 ops_per_sec 1.82K ops/s 1.95K ops/s 🟢▲ +6.64% 1.16%
linux uring accept_churn/burst_lockless/100 ops_per_sec 186.0 ops/s 200.6 ops/s 🟢▲ +7.97% 0.73%
linux uring accept_churn/concurrent/1 ops_per_sec 12.71K ops/s 13.71K ops/s 🟢▲ +8.17% 1.27%
linux uring accept_churn/concurrent/16 ops_per_sec 12.60K ops/s 13.63K ops/s 🟢▲ +8.38% 1.15%
linux uring accept_churn/concurrent/4 ops_per_sec 12.67K ops/s 13.64K ops/s 🟢▲ +7.23% 1.06%
linux uring accept_churn/sequential ops_per_sec 12.75K ops/s 13.74K ops/s 🟢▲ +7.86% 1.08%
linux uring accept_churn/sequential_lockless ops_per_sec 13.63K ops/s 14.68K ops/s 🟢▲ +7.49% 1.16%
linux uring fan_out/concurrent_parents_lockless/1 ops_per_sec 3.65K ops/s 3.50K ops/s 🔴▼ -4.32% 1.40%
linux uring fan_out/concurrent_parents_lockless/16 ops_per_sec 3.52K ops/s 3.36K ops/s 🔴▼ -4.76% 1.05%
linux uring fan_out/concurrent_parents_lockless/4 ops_per_sec 3.55K ops/s 3.40K ops/s 🔴▼ -4.71% 1.21%
linux uring fan_out/fork_join_lockless/16 ops_per_sec 3.65K ops/s 3.47K ops/s 🔴▼ -5.08% 0.26%
linux uring fan_out/fork_join_lockless/4 ops_per_sec 14.51K ops/s 13.87K ops/s 🔴▼ -4.85% 0.90%
linux uring fan_out/fork_join_lockless/64 ops_per_sec 865.4 ops/s 824.8 ops/s 🔴▼ -4.69% 0.30%
linux uring fan_out/nested_lockless/16 ops_per_sec 854.6 ops/s 819.2 ops/s 🔴▼ -4.54% 0.98%
linux uring fan_out/nested_lockless/4 ops_per_sec 3.61K ops/s 3.45K ops/s 🔴▼ -4.68% 0.93%
linux uring http_server/multithread/16 ops_per_sec 81.44K ops/s 83.21K ops/s 🟢▲ +2.58% 0.52%
linux uring local_socket_throughput/bidirectional_lockless/16384 bytes_per_sec 2.61 GB/s 2.41 GB/s 🔴▼ -7.57% 1.48%
linux uring local_socket_throughput/bidirectional_lockless/65536 bytes_per_sec 3.18 GB/s 3.00 GB/s 🔴▼ -6.33% 1.73%
linux uring local_socket_throughput/unidirectional_lockless/262144 bytes_per_sec 3.35 GB/s 3.51 GB/s 🟢▲ +5.95% 1.88%
linux uring socket_throughput/bidirectional_lockless/1048576 bytes_per_sec 3.32 GB/s 3.20 GB/s 🔴▼ -3.44% 0.84%
linux uring socket_throughput/bidirectional_lockless/262144 bytes_per_sec 3.24 GB/s 3.13 GB/s 🔴▼ -3.17% 0.66%
windows iocp accept_churn/burst/10 ops_per_sec 1.53K ops/s 1.56K ops/s 🟢▲ +2.26% 0.34%
windows iocp accept_churn/burst/100 ops_per_sec 149.6 ops/s 157.2 ops/s 🟢▲ +5.02% 0.94%
windows iocp accept_churn/burst_lockless/10 ops_per_sec 1.53K ops/s 1.57K ops/s 🟢▲ +2.38% 0.37%
windows iocp accept_churn/burst_lockless/100 ops_per_sec 151.9 ops/s 160.0 ops/s 🟢▲ +5.42% 0.48%
windows iocp accept_churn/sequential_lockless ops_per_sec 12.28K ops/s 12.55K ops/s 🟢▲ +2.19% 0.32%
macos kqueue accept_churn/burst/10 ops_per_sec 4.21K ops/s 4.33K ops/s 🟢▲ +2.75% 0.88%
macos kqueue accept_churn/burst_lockless/10 ops_per_sec 4.27K ops/s 4.37K ops/s 🟢▲ +2.50% 0.57%
macos kqueue accept_churn/concurrent/16 ops_per_sec 35.19K ops/s 36.38K ops/s 🟢▲ +3.44% 0.95%
linux — full results (226 benchmarks, 5 iterations, 1.5s each)

accept_churn

Benchmark Backend Metric Base Head Δ Noise
burst/10 epoll ops_per_sec 1.88K ops/s 1.91K ops/s 🟢▲ +1.92% 0.90%
burst/10 uring ops_per_sec 1.71K ops/s 1.82K ops/s 🟢▲ +6.23% 1.11%
burst/100 epoll ops_per_sec 189.9 ops/s 193.6 ops/s 🟢▲ +2.13% 0.82%
burst/100 uring ops_per_sec 174.1 ops/s 187.0 ops/s 🟢▲ +7.30% 0.67%
burst_lockless/10 epoll ops_per_sec 1.90K ops/s 1.94K ops/s 🟢▲ +2.16% 1.27%
burst_lockless/10 uring ops_per_sec 1.82K ops/s 1.95K ops/s 🟢▲ +6.64% 1.16%
burst_lockless/100 epoll ops_per_sec 192.0 ops/s 195.9 ops/s 🟢▲ +2.45% 1.21%
burst_lockless/100 uring ops_per_sec 186.0 ops/s 200.6 ops/s 🟢▲ +7.97% 0.73%
concurrent/1 epoll ops_per_sec 14.85K ops/s 15.28K ops/s 🟢▲ +2.99% 1.16%
concurrent/1 uring ops_per_sec 12.71K ops/s 13.71K ops/s 🟢▲ +8.17% 1.27%
concurrent/16 epoll ops_per_sec 14.97K ops/s 15.31K ops/s 🟢▲ +2.19% 0.97%
concurrent/16 uring ops_per_sec 12.60K ops/s 13.63K ops/s 🟢▲ +8.38% 1.15%
concurrent/4 epoll ops_per_sec 14.92K ops/s 15.31K ops/s 🟢▲ +2.29% 1.25%
concurrent/4 uring ops_per_sec 12.67K ops/s 13.64K ops/s 🟢▲ +7.23% 1.06%
sequential epoll ops_per_sec 14.85K ops/s 15.40K ops/s 🟢▲ +3.96% 1.23%
sequential uring ops_per_sec 12.75K ops/s 13.74K ops/s 🟢▲ +7.86% 1.08%
sequential_lockless epoll ops_per_sec 14.94K ops/s 15.54K ops/s 🟢▲ +3.29% 1.13%
sequential_lockless uring ops_per_sec 13.63K ops/s 14.68K ops/s 🟢▲ +7.49% 1.16%

fan_out

Benchmark Backend Metric Base Head Δ Noise
concurrent_parents/1 epoll ops_per_sec 3.70K ops/s 3.69K ops/s 🔴▼ -0.38% 1.08%
concurrent_parents/1 uring ops_per_sec 3.42K ops/s 3.45K ops/s 🟢▲ +0.28% 1.05%
concurrent_parents/16 epoll ops_per_sec 3.59K ops/s 3.58K ops/s ⚪ 0.00% 0.59%
concurrent_parents/16 uring ops_per_sec 3.30K ops/s 3.34K ops/s 🟢▲ +0.30% 1.30%
concurrent_parents/4 epoll ops_per_sec 3.60K ops/s 3.61K ops/s 🟢▲ +0.09% 0.92%
concurrent_parents/4 uring ops_per_sec 3.33K ops/s 3.37K ops/s 🟢▲ +0.15% 1.27%
concurrent_parents_lockless/1 epoll ops_per_sec 3.72K ops/s 3.71K ops/s 🔴▼ -0.30% 0.26%
concurrent_parents_lockless/1 uring ops_per_sec 3.65K ops/s 3.50K ops/s 🔴▼ -4.32% 1.40%
concurrent_parents_lockless/16 epoll ops_per_sec 3.61K ops/s 3.61K ops/s 🟢▲ +0.09% 0.22%
concurrent_parents_lockless/16 uring ops_per_sec 3.52K ops/s 3.36K ops/s 🔴▼ -4.76% 1.05%
concurrent_parents_lockless/4 epoll ops_per_sec 3.63K ops/s 3.63K ops/s 🔴▼ -0.20% 0.20%
concurrent_parents_lockless/4 uring ops_per_sec 3.55K ops/s 3.40K ops/s 🔴▼ -4.71% 1.21%
fork_join/1 epoll ops_per_sec 54.94K ops/s 54.68K ops/s 🔴▼ -0.18% 1.23%
fork_join/1 uring ops_per_sec 53.08K ops/s 53.56K ops/s 🟢▲ +0.46% 1.32%
fork_join/16 epoll ops_per_sec 3.70K ops/s 3.70K ops/s 🔴▼ -0.04% 1.27%
fork_join/16 uring ops_per_sec 3.43K ops/s 3.46K ops/s 🟢▲ +0.14% 1.42%
fork_join/4 epoll ops_per_sec 14.67K ops/s 14.65K ops/s 🔴▼ -0.18% 1.07%
fork_join/4 uring ops_per_sec 13.69K ops/s 13.80K ops/s 🟢▲ +0.25% 1.29%
fork_join/64 epoll ops_per_sec 902.3 ops/s 905.4 ops/s 🔴▼ -0.05% 1.30%
fork_join/64 uring ops_per_sec 811.7 ops/s 818.3 ops/s 🟢▲ +0.57% 0.94%
fork_join_lockless/1 epoll ops_per_sec 55.62K ops/s 55.75K ops/s 🟢▲ +0.08% 1.25%
fork_join_lockless/1 uring ops_per_sec 55.67K ops/s 54.00K ops/s 🔴▼ -3.45% 1.25%
fork_join_lockless/16 epoll ops_per_sec 3.74K ops/s 3.75K ops/s 🔴▼ -0.25% 1.29%
fork_join_lockless/16 uring ops_per_sec 3.65K ops/s 3.47K ops/s 🔴▼ -5.08% 0.26%
fork_join_lockless/4 epoll ops_per_sec 14.88K ops/s 14.89K ops/s 🔴▼ -0.40% 1.18%
fork_join_lockless/4 uring ops_per_sec 14.51K ops/s 13.87K ops/s 🔴▼ -4.85% 0.90%
fork_join_lockless/64 epoll ops_per_sec 913.0 ops/s 917.8 ops/s 🔴▼ -0.14% 1.10%
fork_join_lockless/64 uring ops_per_sec 865.4 ops/s 824.8 ops/s 🔴▼ -4.69% 0.30%
nested/16 epoll ops_per_sec 899.3 ops/s 898.3 ops/s 🔴▼ -0.11% 1.21%
nested/16 uring ops_per_sec 802.3 ops/s 803.1 ops/s 🔴▼ -0.02% 0.26%
nested/4 epoll ops_per_sec 3.69K ops/s 3.70K ops/s 🔴▼ -0.24% 1.39%
nested/4 uring ops_per_sec 3.39K ops/s 3.39K ops/s 🟢▲ +0.11% 0.14%
nested_lockless/16 epoll ops_per_sec 908.1 ops/s 903.9 ops/s 🔴▼ -0.28% 0.97%
nested_lockless/16 uring ops_per_sec 854.6 ops/s 819.2 ops/s 🔴▼ -4.54% 0.98%
nested_lockless/4 epoll ops_per_sec 3.74K ops/s 3.73K ops/s 🔴▼ -0.22% 1.35%
nested_lockless/4 uring ops_per_sec 3.61K ops/s 3.45K ops/s 🔴▼ -4.68% 0.93%

http_server

Benchmark Backend Metric Base Head Δ Noise
concurrent/1 epoll ops_per_sec 54.88K ops/s 54.86K ops/s 🔴▼ -0.23% 0.77%
concurrent/1 uring ops_per_sec 53.34K ops/s 53.85K ops/s 🟢▲ +0.45% 1.21%
concurrent/16 epoll ops_per_sec 58.07K ops/s 58.05K ops/s 🔴▼ -0.45% 1.37%
concurrent/16 uring ops_per_sec 52.78K ops/s 52.91K ops/s 🟢▲ +0.22% 0.26%
concurrent/32 epoll ops_per_sec 57.66K ops/s 57.71K ops/s 🔴▼ -0.67% 1.48%
concurrent/32 uring ops_per_sec 52.46K ops/s 52.49K ops/s 🔴▼ -0.08% 0.21%
concurrent/4 epoll ops_per_sec 57.35K ops/s 57.16K ops/s 🔴▼ -0.62% 1.16%
concurrent/4 uring ops_per_sec 52.82K ops/s 53.33K ops/s 🟢▲ +0.73% 0.90%
multithread/1 epoll ops_per_sec 57.54K ops/s 57.64K ops/s 🔴▼ -0.25% 1.29%
multithread/1 uring ops_per_sec 52.46K ops/s 52.53K ops/s 🟢▲ +0.22% 0.27%
multithread/16 epoll ops_per_sec 161.2K ops/s 161.3K ops/s 🟢▲ +0.37% 0.58%
multithread/16 uring ops_per_sec 81.44K ops/s 83.21K ops/s 🟢▲ +2.58% 0.52%
multithread/2 epoll ops_per_sec 86.59K ops/s 86.48K ops/s 🔴▼ -0.31% 0.39%
multithread/2 uring ops_per_sec 90.29K ops/s 91.00K ops/s 🟢▲ +0.47% 1.28%
multithread/4 epoll ops_per_sec 157.9K ops/s 158.0K ops/s 🟢▲ +0.01% 1.04%
multithread/4 uring ops_per_sec 79.60K ops/s 80.18K ops/s 🟢▲ +0.87% 0.84%
multithread/8 epoll ops_per_sec 160.6K ops/s 160.6K ops/s ⚪ 0.00% 0.42%
multithread/8 uring ops_per_sec 77.24K ops/s 72.05K ops/s 🟢▲ +1.13% 19.47%
single_conn epoll ops_per_sec 54.92K ops/s 54.71K ops/s 🔴▼ -0.18% 0.26%
single_conn uring ops_per_sec 53.32K ops/s 53.84K ops/s 🟢▲ +0.74% 1.11%
single_conn_lockless epoll ops_per_sec 55.61K ops/s 55.46K ops/s 🔴▼ -0.24% 0.20%
single_conn_lockless uring ops_per_sec 55.92K ops/s 54.41K ops/s 🔴▼ -3.26% 1.28%

local_socket_latency

Benchmark Backend Metric Base Head Δ Noise
concurrent/1 epoll latency_mean_ns 7.29 µs 7.26 µs 🔴▼ -0.24% 4.89%
concurrent/1 uring latency_mean_ns 6.59 µs 6.76 µs 🔴▼ -0.59% 4.44%
concurrent/16 epoll latency_mean_ns 109.76 µs 110.34 µs 🔴▼ -0.48% 0.90%
concurrent/16 uring latency_mean_ns 108.44 µs 108.90 µs 🔴▼ -0.43% 0.36%
concurrent/4 epoll latency_mean_ns 27.49 µs 27.97 µs 🔴▼ -2.09% 2.19%
concurrent/4 uring latency_mean_ns 27.08 µs 27.18 µs 🔴▼ -0.64% 1.22%
concurrent_lockless/1 epoll latency_mean_ns 6.98 µs 7.15 µs 🔴▼ -0.09% 4.14%
concurrent_lockless/1 uring latency_mean_ns 6.83 µs 6.70 µs 🔴▼ -0.34% 5.44%
concurrent_lockless/16 epoll latency_mean_ns 108.93 µs 109.01 µs 🟢▲ +0.07% 0.53%
concurrent_lockless/16 uring latency_mean_ns 107.99 µs 107.81 µs 🔴▼ -0.15% 0.81%
concurrent_lockless/4 epoll latency_mean_ns 27.58 µs 27.43 µs 🟢▲ +0.53% 1.07%
concurrent_lockless/4 uring latency_mean_ns 27.11 µs 27.02 µs 🔴▼ -0.56% 1.37%
pingpong/1 epoll latency_mean_ns 7.33 µs 7.21 µs 🟢▲ +0.20% 5.08%
pingpong/1 uring latency_mean_ns 6.83 µs 6.52 µs 🟢▲ +4.48% 4.07%
pingpong/1024 epoll latency_mean_ns 8.04 µs 7.89 µs 🟢▲ +0.25% 3.86%
pingpong/1024 uring latency_mean_ns 7.23 µs 7.23 µs 🔴▼ -0.64% 5.14%
pingpong/64 epoll latency_mean_ns 7.53 µs 7.26 µs 🟢▲ +1.11% 5.18%
pingpong/64 uring latency_mean_ns 6.65 µs 6.57 µs 🔴▼ -0.08% 2.06%
pingpong_lockless/1 epoll latency_mean_ns 7.11 µs 7.10 µs 🟢▲ +0.37% 4.40%
pingpong_lockless/1 uring latency_mean_ns 6.80 µs 6.53 µs 🟢▲ +0.10% 5.30%
pingpong_lockless/1024 epoll latency_mean_ns 7.56 µs 7.77 µs 🔴▼ -2.03% 3.88%
pingpong_lockless/1024 uring latency_mean_ns 7.33 µs 6.97 µs 🟢▲ +8.04% 4.39%
pingpong_lockless/64 epoll latency_mean_ns 6.99 µs 7.03 µs 🟢▲ +0.26% 1.11%
pingpong_lockless/64 uring latency_mean_ns 6.96 µs 6.72 µs 🟢▲ +0.30% 5.15%

local_socket_throughput

Benchmark Backend Metric Base Head Δ Noise
bidirectional/1024 epoll bytes_per_sec 302.8 MB/s 304.2 MB/s 🟢▲ +0.77% 1.05%
bidirectional/1024 uring bytes_per_sec 301.9 MB/s 301.9 MB/s 🔴▼ -0.06% 0.87%
bidirectional/1048576 epoll bytes_per_sec 3.21 GB/s 3.17 GB/s 🟢▲ +0.48% 2.79%
bidirectional/1048576 uring bytes_per_sec 3.33 GB/s 3.28 GB/s 🔴▼ -0.17% 2.25%
bidirectional/16384 epoll bytes_per_sec 3.08 GB/s 3.01 GB/s 🟢▲ +0.53% 4.73%
bidirectional/16384 uring bytes_per_sec 2.42 GB/s 2.41 GB/s 🔴▼ -0.33% 0.65%
bidirectional/262144 epoll bytes_per_sec 3.22 GB/s 3.20 GB/s 🟢▲ +0.57% 2.56%
bidirectional/262144 uring bytes_per_sec 3.29 GB/s 3.32 GB/s 🟢▲ +0.82% 2.36%
bidirectional/4096 epoll bytes_per_sec 999.9 MB/s 1.00 GB/s 🟢▲ +0.02% 0.58%
bidirectional/4096 uring bytes_per_sec 989.1 MB/s 988.4 MB/s 🔴▼ -0.66% 1.09%
bidirectional/65536 epoll bytes_per_sec 2.95 GB/s 2.95 GB/s 🟢▲ +0.68% 1.28%
bidirectional/65536 uring bytes_per_sec 3.01 GB/s 3.00 GB/s 🔴▼ -0.40% 1.61%
bidirectional_lockless/1024 epoll bytes_per_sec 312.1 MB/s 305.2 MB/s 🔴▼ -2.10% 1.89%
bidirectional_lockless/1024 uring bytes_per_sec 303.5 MB/s 301.3 MB/s 🔴▼ -0.64% 0.48%
bidirectional_lockless/1048576 epoll bytes_per_sec 3.18 GB/s 3.18 GB/s 🟢▲ +0.60% 2.98%
bidirectional_lockless/1048576 uring bytes_per_sec 3.36 GB/s 3.27 GB/s 🔴▼ -1.44% 2.59%
bidirectional_lockless/16384 epoll bytes_per_sec 3.07 GB/s 3.05 GB/s 🟢▲ +0.72% 3.88%
bidirectional_lockless/16384 uring bytes_per_sec 2.61 GB/s 2.41 GB/s 🔴▼ -7.57% 1.48%
bidirectional_lockless/262144 epoll bytes_per_sec 3.19 GB/s 3.20 GB/s 🟢▲ +0.12% 2.66%
bidirectional_lockless/262144 uring bytes_per_sec 3.33 GB/s 3.30 GB/s 🔴▼ -1.52% 2.16%
bidirectional_lockless/4096 epoll bytes_per_sec 1.02 GB/s 1.01 GB/s 🟢▲ +0.07% 1.20%
bidirectional_lockless/4096 uring bytes_per_sec 992.5 MB/s 990.3 MB/s 🔴▼ -0.31% 1.10%
bidirectional_lockless/65536 epoll bytes_per_sec 2.96 GB/s 2.95 GB/s 🔴▼ -0.35% 1.48%
bidirectional_lockless/65536 uring bytes_per_sec 3.18 GB/s 3.00 GB/s 🔴▼ -6.33% 1.73%
unidirectional/1024 epoll bytes_per_sec 296.0 MB/s 297.4 MB/s 🟢▲ +0.12% 0.88%
unidirectional/1024 uring bytes_per_sec 303.1 MB/s 301.7 MB/s 🔴▼ -0.22% 0.82%
unidirectional/1048576 epoll bytes_per_sec 3.34 GB/s 3.30 GB/s 🔴▼ -0.14% 2.90%
unidirectional/1048576 uring bytes_per_sec 3.45 GB/s 3.43 GB/s 🔴▼ -0.39% 1.98%
unidirectional/16384 epoll bytes_per_sec 3.09 GB/s 3.04 GB/s 🔴▼ -0.70% 2.87%
unidirectional/16384 uring bytes_per_sec 2.58 GB/s 2.65 GB/s 🟢▲ +3.69% 1.58%
unidirectional/262144 epoll bytes_per_sec 3.35 GB/s 3.30 GB/s 🔴▼ -0.61% 3.16%
unidirectional/262144 uring bytes_per_sec 3.39 GB/s 3.47 GB/s 🟢▲ +3.05% 2.21%
unidirectional/4096 epoll bytes_per_sec 982.4 MB/s 978.9 MB/s 🔴▼ -0.01% 1.01%
unidirectional/4096 uring bytes_per_sec 995.6 MB/s 984.5 MB/s 🔴▼ -0.64% 0.87%
unidirectional/65536 epoll bytes_per_sec 3.22 GB/s 3.27 GB/s 🟢▲ +2.32% 2.34%
unidirectional/65536 uring bytes_per_sec 3.31 GB/s 3.24 GB/s 🔴▼ -2.51% 2.65%
unidirectional_lockless/1024 epoll bytes_per_sec 302.3 MB/s 300.0 MB/s 🔴▼ -0.93% 1.57%
unidirectional_lockless/1024 uring bytes_per_sec 303.5 MB/s 303.4 MB/s 🔴▼ -0.21% 1.05%
unidirectional_lockless/1048576 epoll bytes_per_sec 3.35 GB/s 3.32 GB/s 🔴▼ -1.10% 3.14%
unidirectional_lockless/1048576 uring bytes_per_sec 3.42 GB/s 3.45 GB/s 🟢▲ +1.55% 2.35%
unidirectional_lockless/16384 epoll bytes_per_sec 3.14 GB/s 3.12 GB/s 🔴▼ -0.55% 1.67%
unidirectional_lockless/16384 uring bytes_per_sec 2.65 GB/s 2.66 GB/s 🔴▼ -0.09% 2.37%
unidirectional_lockless/262144 epoll bytes_per_sec 3.35 GB/s 3.31 GB/s 🔴▼ -0.08% 3.16%
unidirectional_lockless/262144 uring bytes_per_sec 3.35 GB/s 3.51 GB/s 🟢▲ +5.95% 1.88%
unidirectional_lockless/4096 epoll bytes_per_sec 992.8 MB/s 995.1 MB/s 🟢▲ +0.24% 1.63%
unidirectional_lockless/4096 uring bytes_per_sec 990.1 MB/s 998.1 MB/s 🟢▲ +0.15% 1.04%
unidirectional_lockless/65536 epoll bytes_per_sec 3.25 GB/s 3.30 GB/s 🔴▼ -0.20% 2.15%
unidirectional_lockless/65536 uring bytes_per_sec 3.27 GB/s 3.25 GB/s 🔴▼ -0.18% 2.63%

socket_latency

Benchmark Backend Metric Base Head Δ Noise
concurrent/1 epoll latency_mean_ns 14.73 µs 14.72 µs 🟢▲ +0.26% 1.36%
concurrent/1 uring latency_mean_ns 14.53 µs 14.54 µs 🔴▼ -0.13% 0.28%
concurrent/16 epoll latency_mean_ns 232.66 µs 233.14 µs 🔴▼ -0.08% 0.44%
concurrent/16 uring latency_mean_ns 235.36 µs 235.32 µs 🔴▼ -0.03% 0.12%
concurrent/4 epoll latency_mean_ns 57.90 µs 58.01 µs 🟢▲ +0.04% 1.22%
concurrent/4 uring latency_mean_ns 58.37 µs 58.45 µs 🔴▼ -0.31% 0.17%
concurrent_lockless/1 epoll latency_mean_ns 14.69 µs 14.68 µs 🟢▲ +0.19% 0.16%
concurrent_lockless/1 uring latency_mean_ns 14.51 µs 14.48 µs 🔴▼ -0.10% 0.97%
concurrent_lockless/16 epoll latency_mean_ns 231.45 µs 231.85 µs 🔴▼ -0.21% 0.12%
concurrent_lockless/16 uring latency_mean_ns 235.25 µs 234.07 µs 🟢▲ +0.03% 1.27%
concurrent_lockless/4 epoll latency_mean_ns 57.80 µs 57.81 µs ⚪ 0.00% 0.10%
concurrent_lockless/4 uring latency_mean_ns 58.26 µs 58.12 µs 🔴▼ -0.31% 1.29%
pingpong/1 epoll latency_mean_ns 14.69 µs 14.73 µs 🟢▲ +0.30% 1.30%
pingpong/1 uring latency_mean_ns 14.47 µs 14.43 µs 🔴▼ -0.10% 1.20%
pingpong/1024 epoll latency_mean_ns 15.02 µs 15.00 µs 🟢▲ +0.27% 1.37%
pingpong/1024 uring latency_mean_ns 14.81 µs 14.81 µs 🔴▼ -0.22% 0.65%
pingpong/64 epoll latency_mean_ns 14.73 µs 14.72 µs 🟢▲ +0.30% 1.16%
pingpong/64 uring latency_mean_ns 14.55 µs 14.50 µs 🔴▼ -0.07% 1.27%
pingpong_lockless/1 epoll latency_mean_ns 14.57 µs 14.55 µs 🟢▲ +0.10% 1.35%
pingpong_lockless/1 uring latency_mean_ns 14.44 µs 14.47 µs 🔴▼ -0.19% 0.30%
pingpong_lockless/1024 epoll latency_mean_ns 14.90 µs 14.87 µs 🟢▲ +0.33% 1.50%
pingpong_lockless/1024 uring latency_mean_ns 14.76 µs 14.82 µs 🔴▼ -0.38% 0.22%
pingpong_lockless/64 epoll latency_mean_ns 14.63 µs 14.62 µs 🟢▲ +0.20% 1.38%
pingpong_lockless/64 uring latency_mean_ns 14.51 µs 14.56 µs 🔴▼ -0.39% 0.25%

socket_throughput

Benchmark Backend Metric Base Head Δ Noise
bidirectional/1024 epoll bytes_per_sec 133.6 MB/s 134.3 MB/s 🟢▲ +0.51% 1.29%
bidirectional/1024 uring bytes_per_sec 140.6 MB/s 140.3 MB/s 🔴▼ -0.38% 0.35%
bidirectional/1048576 epoll bytes_per_sec 3.27 GB/s 3.27 GB/s 🔴▼ -0.30% 2.03%
bidirectional/1048576 uring bytes_per_sec 3.22 GB/s 3.21 GB/s 🔴▼ -0.44% 0.90%
bidirectional/16384 epoll bytes_per_sec 1.63 GB/s 1.64 GB/s 🟢▲ +0.84% 0.85%
bidirectional/16384 uring bytes_per_sec 1.52 GB/s 1.52 GB/s 🟢▲ +1.08% 1.82%
bidirectional/262144 epoll bytes_per_sec 3.19 GB/s 3.22 GB/s 🟢▲ +1.41% 1.06%
bidirectional/262144 uring bytes_per_sec 3.18 GB/s 3.15 GB/s 🔴▼ -1.26% 0.92%
bidirectional/4096 epoll bytes_per_sec 504.3 MB/s 506.3 MB/s 🟢▲ +0.23% 1.21%
bidirectional/4096 uring bytes_per_sec 490.8 MB/s 490.6 MB/s 🔴▼ -0.22% 0.68%
bidirectional/65536 epoll bytes_per_sec 2.55 GB/s 2.61 GB/s 🟢▲ +0.21% 4.18%
bidirectional/65536 uring bytes_per_sec 2.82 GB/s 2.78 GB/s 🔴▼ -1.31% 3.04%
bidirectional_lockless/1024 epoll bytes_per_sec 134.0 MB/s 133.9 MB/s 🔴▼ -0.07% 0.35%
bidirectional_lockless/1024 uring bytes_per_sec 140.8 MB/s 140.8 MB/s 🔴▼ -0.15% 0.39%
bidirectional_lockless/1048576 epoll bytes_per_sec 3.26 GB/s 3.32 GB/s 🟢▲ +1.90% 2.65%
bidirectional_lockless/1048576 uring bytes_per_sec 3.32 GB/s 3.20 GB/s 🔴▼ -3.44% 0.84%
bidirectional_lockless/16384 epoll bytes_per_sec 1.63 GB/s 1.63 GB/s 🟢▲ +0.03% 1.03%
bidirectional_lockless/16384 uring bytes_per_sec 1.54 GB/s 1.53 GB/s 🔴▼ -0.48% 0.71%
bidirectional_lockless/262144 epoll bytes_per_sec 3.19 GB/s 3.23 GB/s 🟢▲ +1.11% 1.02%
bidirectional_lockless/262144 uring bytes_per_sec 3.24 GB/s 3.13 GB/s 🔴▼ -3.17% 0.66%
bidirectional_lockless/4096 epoll bytes_per_sec 505.5 MB/s 503.9 MB/s 🔴▼ -0.56% 0.68%
bidirectional_lockless/4096 uring bytes_per_sec 494.3 MB/s 493.9 MB/s 🔴▼ -0.06% 0.39%
bidirectional_lockless/65536 epoll bytes_per_sec 2.53 GB/s 2.53 GB/s 🟢▲ +0.22% 2.77%
bidirectional_lockless/65536 uring bytes_per_sec 2.75 GB/s 2.75 GB/s 🔴▼ -0.73% 2.33%
multithread/2 epoll bytes_per_sec 3.41 GB/s 3.41 GB/s 🔴▼ -0.08% 0.64%
multithread/2 uring bytes_per_sec 3.66 GB/s 3.67 GB/s 🟢▲ +0.03% 0.71%
multithread/4 epoll bytes_per_sec 4.86 GB/s 4.87 GB/s 🟢▲ +0.37% 0.77%
multithread/4 uring bytes_per_sec 5.08 GB/s 5.06 GB/s 🔴▼ -0.30% 0.26%
multithread/8 epoll bytes_per_sec 4.96 GB/s 4.95 GB/s 🔴▼ -0.10% 0.33%
multithread/8 uring bytes_per_sec 5.13 GB/s 5.12 GB/s 🟢▲ +0.07% 0.23%
unidirectional/1024 epoll bytes_per_sec 125.2 MB/s 126.2 MB/s 🟢▲ +0.38% 1.20%
unidirectional/1024 uring bytes_per_sec 139.1 MB/s 139.3 MB/s 🟢▲ +0.05% 0.93%
unidirectional/1048576 epoll bytes_per_sec 3.34 GB/s 3.34 GB/s 🟢▲ +0.10% 0.92%
unidirectional/1048576 uring bytes_per_sec 3.40 GB/s 3.41 GB/s 🟢▲ +0.55% 0.90%
unidirectional/16384 epoll bytes_per_sec 1.53 GB/s 1.54 GB/s 🟢▲ +1.38% 1.29%
unidirectional/16384 uring bytes_per_sec 1.57 GB/s 1.57 GB/s 🔴▼ -0.22% 3.14%
unidirectional/262144 epoll bytes_per_sec 3.27 GB/s 3.28 GB/s 🟢▲ +0.41% 1.33%
unidirectional/262144 uring bytes_per_sec 3.19 GB/s 3.18 GB/s 🔴▼ -0.02% 1.42%
unidirectional/4096 epoll bytes_per_sec 470.0 MB/s 472.5 MB/s 🟢▲ +0.69% 0.56%
unidirectional/4096 uring bytes_per_sec 490.8 MB/s 489.3 MB/s 🔴▼ -0.08% 0.77%
unidirectional/65536 epoll bytes_per_sec 2.65 GB/s 2.73 GB/s 🟢▲ +1.09% 5.19%
unidirectional/65536 uring bytes_per_sec 2.72 GB/s 2.70 GB/s 🔴▼ -0.90% 0.53%
unidirectional_lockless/1024 epoll bytes_per_sec 125.8 MB/s 125.8 MB/s 🔴▼ -0.08% 0.18%
unidirectional_lockless/1024 uring bytes_per_sec 139.4 MB/s 139.0 MB/s 🔴▼ -0.25% 0.21%
unidirectional_lockless/1048576 epoll bytes_per_sec 3.32 GB/s 3.37 GB/s 🟢▲ +1.91% 1.17%
unidirectional_lockless/1048576 uring bytes_per_sec 3.30 GB/s 3.36 GB/s 🟢▲ +1.73% 1.50%
unidirectional_lockless/16384 epoll bytes_per_sec 1.56 GB/s 1.55 GB/s 🔴▼ -0.12% 0.77%
unidirectional_lockless/16384 uring bytes_per_sec 1.58 GB/s 1.57 GB/s 🟢▲ +0.13% 2.85%
unidirectional_lockless/262144 epoll bytes_per_sec 3.32 GB/s 3.37 GB/s 🟢▲ +1.79% 1.31%
unidirectional_lockless/262144 uring bytes_per_sec 3.22 GB/s 3.23 GB/s 🟢▲ +0.21% 1.33%
unidirectional_lockless/4096 epoll bytes_per_sec 472.4 MB/s 471.0 MB/s 🔴▼ -0.09% 0.45%
unidirectional_lockless/4096 uring bytes_per_sec 491.3 MB/s 488.3 MB/s 🔴▼ -0.54% 0.35%
unidirectional_lockless/65536 epoll bytes_per_sec 2.67 GB/s 2.77 GB/s 🟢▲ +0.41% 4.74%
unidirectional_lockless/65536 uring bytes_per_sec 2.70 GB/s 2.73 GB/s 🔴▼ -0.14% 2.95%
windows — full results (77 benchmarks, 5 iterations, 1.5s each)

accept_churn

Benchmark Backend Metric Base Head Δ Noise
burst/10 iocp ops_per_sec 1.53K ops/s 1.56K ops/s 🟢▲ +2.26% 0.34%
burst/100 iocp ops_per_sec 149.6 ops/s 157.2 ops/s 🟢▲ +5.02% 0.94%
burst_lockless/10 iocp ops_per_sec 1.53K ops/s 1.57K ops/s 🟢▲ +2.38% 0.37%
burst_lockless/100 iocp ops_per_sec 151.9 ops/s 160.0 ops/s 🟢▲ +5.42% 0.48%
concurrent/1 iocp ops_per_sec 12.22K ops/s 12.57K ops/s 🟢▲ +2.71% 1.41%
concurrent/16 iocp ops_per_sec 13.22K ops/s 13.47K ops/s 🟢▲ +1.83% 0.27%
concurrent/4 iocp ops_per_sec 12.94K ops/s 13.18K ops/s 🟢▲ +1.84% 0.28%
sequential iocp ops_per_sec 12.25K ops/s 12.47K ops/s 🟢▲ +1.95% 0.38%
sequential_lockless iocp ops_per_sec 12.28K ops/s 12.55K ops/s 🟢▲ +2.19% 0.32%

fan_out

Benchmark Backend Metric Base Head Δ Noise
concurrent_parents/1 iocp ops_per_sec 7.30K ops/s 7.35K ops/s 🟢▲ +0.64% 0.31%
concurrent_parents/16 iocp ops_per_sec 6.32K ops/s 6.21K ops/s 🔴▼ -1.05% 1.69%
concurrent_parents/4 iocp ops_per_sec 6.87K ops/s 6.91K ops/s 🟢▲ +0.28% 1.05%
concurrent_parents_lockless/1 iocp ops_per_sec 7.32K ops/s 7.35K ops/s 🟢▲ +0.47% 0.51%
concurrent_parents_lockless/16 iocp ops_per_sec 6.40K ops/s 6.36K ops/s 🔴▼ -1.18% 1.36%
concurrent_parents_lockless/4 iocp ops_per_sec 6.90K ops/s 6.95K ops/s 🟢▲ +0.55% 0.73%
fork_join/1 iocp ops_per_sec 112.5K ops/s 112.6K ops/s 🟢▲ +0.05% 0.68%
fork_join/16 iocp ops_per_sec 7.31K ops/s 7.34K ops/s 🟢▲ +0.42% 0.39%
fork_join/4 iocp ops_per_sec 29.10K ops/s 29.08K ops/s 🟢▲ +0.02% 0.41%
fork_join/64 iocp ops_per_sec 1.72K ops/s 1.74K ops/s 🟢▲ +0.83% 1.88%
fork_join_lockless/1 iocp ops_per_sec 112.3K ops/s 112.7K ops/s 🟢▲ +0.19% 0.65%
fork_join_lockless/16 iocp ops_per_sec 7.31K ops/s 7.34K ops/s 🟢▲ +0.45% 0.27%
fork_join_lockless/4 iocp ops_per_sec 29.15K ops/s 29.13K ops/s 🟢▲ +0.12% 0.47%
fork_join_lockless/64 iocp ops_per_sec 1.73K ops/s 1.74K ops/s 🟢▲ +0.11% 1.10%
nested/16 iocp ops_per_sec 1.69K ops/s 1.69K ops/s 🔴▼ -0.33% 1.45%
nested/4 iocp ops_per_sec 7.12K ops/s 7.14K ops/s 🟢▲ +0.29% 0.49%
nested_lockless/16 iocp ops_per_sec 1.68K ops/s 1.69K ops/s 🔴▼ -0.64% 1.38%
nested_lockless/4 iocp ops_per_sec 7.11K ops/s 7.15K ops/s 🟢▲ +0.55% 0.40%

http_server

Benchmark Backend Metric Base Head Δ Noise
concurrent/1 iocp ops_per_sec 111.3K ops/s 111.5K ops/s 🟢▲ +0.15% 0.44%
concurrent/16 iocp ops_per_sec 108.4K ops/s 108.6K ops/s 🟢▲ +0.04% 0.29%
concurrent/32 iocp ops_per_sec 107.7K ops/s 107.8K ops/s 🟢▲ +0.15% 0.30%
concurrent/4 iocp ops_per_sec 109.3K ops/s 109.1K ops/s 🔴▼ -0.32% 0.48%
multithread/1 iocp ops_per_sec 107.5K ops/s 107.7K ops/s 🟢▲ +0.23% 0.30%
multithread/16 iocp ops_per_sec 267.0K ops/s 265.6K ops/s 🔴▼ -0.18% 0.53%
multithread/2 iocp ops_per_sec 181.5K ops/s 181.2K ops/s 🔴▼ -0.74% 0.93%
multithread/4 iocp ops_per_sec 225.6K ops/s 224.4K ops/s 🔴▼ -0.84% 0.74%
multithread/8 iocp ops_per_sec 267.4K ops/s 266.0K ops/s 🔴▼ -0.61% 0.22%
single_conn iocp ops_per_sec 111.6K ops/s 111.5K ops/s 🔴▼ -0.16% 0.43%
single_conn_lockless iocp ops_per_sec 111.8K ops/s 111.7K ops/s 🔴▼ -0.27% 0.37%

socket_latency

Benchmark Backend Metric Base Head Δ Noise
concurrent/1 iocp latency_mean_ns 7.60 µs 7.55 µs 🟢▲ +0.43% 0.41%
concurrent/16 iocp latency_mean_ns 130.91 µs 130.62 µs 🟢▲ +0.05% 3.06%
concurrent/4 iocp latency_mean_ns 30.54 µs 30.43 µs 🟢▲ +0.44% 0.46%
concurrent_lockless/1 iocp latency_mean_ns 7.60 µs 7.57 µs 🟢▲ +0.43% 0.55%
concurrent_lockless/16 iocp latency_mean_ns 123.01 µs 121.85 µs 🟢▲ +0.37% 1.98%
concurrent_lockless/4 iocp latency_mean_ns 30.48 µs 30.48 µs 🔴▼ -0.14% 0.38%
pingpong/1 iocp latency_mean_ns 7.58 µs 7.55 µs 🟢▲ +0.41% 0.42%
pingpong/1024 iocp latency_mean_ns 7.68 µs 7.67 µs 🟢▲ +0.20% 0.54%
pingpong/64 iocp latency_mean_ns 7.59 µs 7.56 µs 🟢▲ +0.41% 0.26%
pingpong_lockless/1 iocp latency_mean_ns 7.56 µs 7.54 µs 🟢▲ +0.09% 0.45%
pingpong_lockless/1024 iocp latency_mean_ns 7.69 µs 7.68 µs 🟢▲ +0.19% 0.71%
pingpong_lockless/64 iocp latency_mean_ns 7.59 µs 7.56 µs 🟢▲ +0.36% 0.22%

socket_throughput

Benchmark Backend Metric Base Head Δ Noise
bidirectional/1024 iocp bytes_per_sec 282.1 MB/s 280.8 MB/s 🔴▼ -0.66% 0.63%
bidirectional/1048576 iocp bytes_per_sec 5.27 GB/s 5.47 GB/s 🟢▲ +0.32% 8.92%
bidirectional/16384 iocp bytes_per_sec 3.70 GB/s 3.75 GB/s 🟢▲ +0.59% 3.20%
bidirectional/262144 iocp bytes_per_sec 9.26 GB/s 9.59 GB/s 🟢▲ +0.41% 7.50%
bidirectional/4096 iocp bytes_per_sec 1.08 GB/s 1.08 GB/s 🔴▼ -0.47% 0.33%
bidirectional/65536 iocp bytes_per_sec 8.78 GB/s 9.09 GB/s 🟢▲ +0.23% 8.02%
bidirectional_lockless/1024 iocp bytes_per_sec 281.7 MB/s 280.4 MB/s 🔴▼ -0.87% 0.45%
bidirectional_lockless/1048576 iocp bytes_per_sec 5.32 GB/s 5.90 GB/s 🟢▲ +10.15% 9.06%
bidirectional_lockless/16384 iocp bytes_per_sec 3.72 GB/s 3.72 GB/s 🔴▼ -0.20% 0.91%
bidirectional_lockless/262144 iocp bytes_per_sec 9.76 GB/s 9.79 GB/s 🟢▲ +0.45% 2.63%
bidirectional_lockless/4096 iocp bytes_per_sec 1.08 GB/s 1.08 GB/s 🔴▼ -0.33% 0.28%
bidirectional_lockless/65536 iocp bytes_per_sec 8.92 GB/s 9.00 GB/s 🟢▲ +1.16% 1.96%
multithread/2 iocp bytes_per_sec 12.77 GB/s 12.81 GB/s 🟢▲ +0.04% 0.88%
multithread/4 iocp bytes_per_sec 17.05 GB/s 17.08 GB/s 🔴▼ -0.17% 1.38%
multithread/8 iocp bytes_per_sec 18.35 GB/s 18.39 GB/s 🟢▲ +0.13% 1.31%
unidirectional/1024 iocp bytes_per_sec 285.3 MB/s 285.5 MB/s 🟢▲ +0.19% 0.49%
unidirectional/1048576 iocp bytes_per_sec 4.25 GB/s 4.20 GB/s 🔴▼ -4.16% 7.56%
unidirectional/16384 iocp bytes_per_sec 3.78 GB/s 3.80 GB/s 🟢▲ +0.96% 1.18%
unidirectional/262144 iocp bytes_per_sec 8.37 GB/s 8.48 GB/s 🔴▼ -1.55% 12.91%
unidirectional/4096 iocp bytes_per_sec 1.09 GB/s 1.10 GB/s 🟢▲ +0.64% 0.52%
unidirectional/65536 iocp bytes_per_sec 9.24 GB/s 9.15 GB/s 🟢▲ +1.09% 4.13%
unidirectional_lockless/1024 iocp bytes_per_sec 286.1 MB/s 285.5 MB/s 🔴▼ -0.04% 0.47%
unidirectional_lockless/1048576 iocp bytes_per_sec 4.78 GB/s 4.82 GB/s 🟢▲ +2.65% 6.05%
unidirectional_lockless/16384 iocp bytes_per_sec 3.80 GB/s 3.80 GB/s 🔴▼ -0.62% 0.89%
unidirectional_lockless/262144 iocp bytes_per_sec 9.79 GB/s 9.81 GB/s 🟢▲ +0.17% 0.35%
unidirectional_lockless/4096 iocp bytes_per_sec 1.10 GB/s 1.10 GB/s 🔴▼ -0.11% 0.41%
unidirectional_lockless/65536 iocp bytes_per_sec 9.26 GB/s 9.32 GB/s 🟢▲ +0.69% 1.80%
macos — full results (113 benchmarks, 5 iterations, 1.5s each)

accept_churn

Benchmark Backend Metric Base Head Δ Noise
burst/10 kqueue ops_per_sec 4.21K ops/s 4.33K ops/s 🟢▲ +2.75% 0.88%
burst/100 kqueue ops_per_sec 433.7 ops/s 443.4 ops/s 🟢▲ +3.34% 1.14%
burst_lockless/10 kqueue ops_per_sec 4.27K ops/s 4.37K ops/s 🟢▲ +2.50% 0.57%
burst_lockless/100 kqueue ops_per_sec 434.8 ops/s 446.9 ops/s 🟢▲ +2.53% 1.09%
concurrent/1 kqueue ops_per_sec 19.66K ops/s 20.00K ops/s 🟢▲ +1.35% 0.86%
concurrent/16 kqueue ops_per_sec 35.19K ops/s 36.38K ops/s 🟢▲ +3.44% 0.95%
concurrent/4 kqueue ops_per_sec 29.41K ops/s 29.38K ops/s 🔴▼ -0.64% 1.09%
sequential kqueue ops_per_sec 19.96K ops/s 20.27K ops/s 🟢▲ +2.06% 0.75%
sequential_lockless kqueue ops_per_sec 20.02K ops/s 20.26K ops/s 🟢▲ +0.50% 1.43%

fan_out

Benchmark Backend Metric Base Head Δ Noise
concurrent_parents/1 kqueue ops_per_sec 8.04K ops/s 8.08K ops/s 🟢▲ +0.31% 0.46%
concurrent_parents/16 kqueue ops_per_sec 8.05K ops/s 6.48K ops/s 🔴▼ -27.75% 17.85%
concurrent_parents/4 kqueue ops_per_sec 8.15K ops/s 8.29K ops/s 🟢▲ +2.78% 1.25%
concurrent_parents_lockless/1 kqueue ops_per_sec 8.11K ops/s 8.18K ops/s 🟢▲ +0.93% 0.76%
concurrent_parents_lockless/16 kqueue ops_per_sec 8.04K ops/s 7.09K ops/s 🔴▼ -0.41% 20.12%
concurrent_parents_lockless/4 kqueue ops_per_sec 8.21K ops/s 8.36K ops/s 🟢▲ +2.90% 1.33%
fork_join/1 kqueue ops_per_sec 47.79K ops/s 48.09K ops/s 🟢▲ +0.96% 0.90%
fork_join/16 kqueue ops_per_sec 8.04K ops/s 8.04K ops/s 🔴▼ -0.02% 0.32%
fork_join/4 kqueue ops_per_sec 23.54K ops/s 23.65K ops/s 🟢▲ +0.60% 0.42%
fork_join/64 kqueue ops_per_sec 2.07K ops/s 2.05K ops/s 🔴▼ -0.34% 1.91%
fork_join_lockless/1 kqueue ops_per_sec 49.51K ops/s 49.50K ops/s 🔴▼ -0.06% 0.38%
fork_join_lockless/16 kqueue ops_per_sec 8.17K ops/s 8.13K ops/s 🔴▼ -0.40% 0.79%
fork_join_lockless/4 kqueue ops_per_sec 23.48K ops/s 23.63K ops/s 🟢▲ +0.88% 0.71%
fork_join_lockless/64 kqueue ops_per_sec 2.09K ops/s 2.08K ops/s 🟢▲ +0.13% 1.99%
nested/16 kqueue ops_per_sec 2.03K ops/s 2.04K ops/s 🟢▲ +0.22% 1.87%
nested/4 kqueue ops_per_sec 7.93K ops/s 7.96K ops/s 🟢▲ +0.09% 0.34%
nested_lockless/16 kqueue ops_per_sec 2.02K ops/s 2.07K ops/s 🟢▲ +3.78% 1.86%
nested_lockless/4 kqueue ops_per_sec 8.03K ops/s 8.06K ops/s 🟢▲ +0.26% 0.52%

http_server

Benchmark Backend Metric Base Head Δ Noise
concurrent/1 kqueue ops_per_sec 49.36K ops/s 49.14K ops/s 🔴▼ -0.43% 0.69%
concurrent/16 kqueue ops_per_sec 129.8K ops/s 129.0K ops/s 🔴▼ -0.57% 0.41%
concurrent/32 kqueue ops_per_sec 129.9K ops/s 128.2K ops/s 🔴▼ -1.72% 0.83%
concurrent/4 kqueue ops_per_sec 88.76K ops/s 89.93K ops/s 🟢▲ +1.40% 0.28%
multithread/1 kqueue ops_per_sec 130.1K ops/s 128.0K ops/s 🔴▼ -1.74% 1.23%
multithread/16 kqueue ops_per_sec 135.7K ops/s 133.4K ops/s 🟢▲ +0.30% 4.64%
multithread/2 kqueue ops_per_sec 182.8K ops/s 185.0K ops/s 🟢▲ +1.32% 0.42%
multithread/4 kqueue ops_per_sec 165.0K ops/s 162.2K ops/s 🔴▼ -0.53% 3.38%
multithread/8 kqueue ops_per_sec 141.9K ops/s 136.9K ops/s 🔴▼ -1.91% 3.68%
single_conn kqueue ops_per_sec 49.43K ops/s 49.03K ops/s 🔴▼ -0.67% 0.47%
single_conn_lockless kqueue ops_per_sec 49.54K ops/s 49.22K ops/s 🔴▼ -0.59% 0.56%

local_socket_latency

Benchmark Backend Metric Base Head Δ Noise
concurrent/1 kqueue latency_mean_ns 1.84 µs 1.85 µs 🔴▼ -0.63% 0.43%
concurrent/16 kqueue latency_mean_ns 27.16 µs 27.32 µs 🔴▼ -0.58% 0.37%
concurrent/4 kqueue latency_mean_ns 6.83 µs 6.87 µs 🔴▼ -0.66% 0.45%
concurrent_lockless/1 kqueue latency_mean_ns 1.82 µs 1.84 µs 🔴▼ -0.48% 0.42%
concurrent_lockless/16 kqueue latency_mean_ns 26.96 µs 27.10 µs 🔴▼ -0.60% 0.32%
concurrent_lockless/4 kqueue latency_mean_ns 6.78 µs 6.82 µs 🔴▼ -0.56% 0.38%
pingpong/1 kqueue latency_mean_ns 1.83 µs 1.85 µs 🔴▼ -0.91% 0.32%
pingpong/1024 kqueue latency_mean_ns 1.93 µs 1.94 µs 🔴▼ -0.62% 0.35%
pingpong/64 kqueue latency_mean_ns 1.83 µs 1.85 µs 🔴▼ -0.82% 0.38%
pingpong_lockless/1 kqueue latency_mean_ns 1.82 µs 1.84 µs 🔴▼ -0.84% 0.51%
pingpong_lockless/1024 kqueue latency_mean_ns 1.92 µs 1.93 µs 🔴▼ -0.68% 0.40%
pingpong_lockless/64 kqueue latency_mean_ns 1.82 µs 1.84 µs 🔴▼ -0.96% 0.38%

local_socket_throughput

Benchmark Backend Metric Base Head Δ Noise
bidirectional/1024 kqueue bytes_per_sec 1.30 GB/s 1.30 GB/s 🔴▼ -0.51% 0.29%
bidirectional/1048576 kqueue bytes_per_sec 3.83 GB/s 3.84 GB/s 🟢▲ +0.36% 0.58%
bidirectional/16384 kqueue bytes_per_sec 3.80 GB/s 3.82 GB/s 🟢▲ +0.63% 0.69%
bidirectional/262144 kqueue bytes_per_sec 3.83 GB/s 3.82 GB/s 🔴▼ -0.13% 0.71%
bidirectional/4096 kqueue bytes_per_sec 2.89 GB/s 2.90 GB/s 🟢▲ +0.32% 0.58%
bidirectional/65536 kqueue bytes_per_sec 3.82 GB/s 3.82 GB/s 🔴▼ -0.25% 0.33%
bidirectional_lockless/1024 kqueue bytes_per_sec 1.31 GB/s 1.30 GB/s 🔴▼ -0.39% 0.28%
bidirectional_lockless/1048576 kqueue bytes_per_sec 3.87 GB/s 3.90 GB/s 🟢▲ +0.89% 0.63%
bidirectional_lockless/16384 kqueue bytes_per_sec 3.87 GB/s 3.90 GB/s 🟢▲ +0.80% 0.49%
bidirectional_lockless/262144 kqueue bytes_per_sec 3.89 GB/s 3.89 GB/s 🟢▲ +0.12% 0.60%
bidirectional_lockless/4096 kqueue bytes_per_sec 2.94 GB/s 2.94 GB/s 🟢▲ +0.55% 0.47%
bidirectional_lockless/65536 kqueue bytes_per_sec 3.90 GB/s 3.90 GB/s 🔴▼ -0.03% 1.03%
unidirectional/1024 kqueue bytes_per_sec 1.28 GB/s 1.27 GB/s 🔴▼ -0.03% 0.56%
unidirectional/1048576 kqueue bytes_per_sec 3.46 GB/s 3.45 GB/s 🔴▼ -0.13% 0.42%
unidirectional/16384 kqueue bytes_per_sec 3.46 GB/s 3.46 GB/s 🟢▲ +0.25% 0.53%
unidirectional/262144 kqueue bytes_per_sec 3.45 GB/s 3.45 GB/s 🔴▼ -0.04% 0.31%
unidirectional/4096 kqueue bytes_per_sec 2.68 GB/s 2.68 GB/s 🔴▼ -0.21% 0.55%
unidirectional/65536 kqueue bytes_per_sec 3.45 GB/s 3.46 GB/s 🟢▲ +0.33% 0.48%
unidirectional_lockless/1024 kqueue bytes_per_sec 1.28 GB/s 1.28 GB/s 🔴▼ -0.40% 0.41%
unidirectional_lockless/1048576 kqueue bytes_per_sec 3.51 GB/s 3.50 GB/s 🔴▼ -0.09% 0.40%
unidirectional_lockless/16384 kqueue bytes_per_sec 3.52 GB/s 3.51 GB/s 🔴▼ -0.43% 0.36%
unidirectional_lockless/262144 kqueue bytes_per_sec 3.50 GB/s 3.52 GB/s 🟢▲ +0.52% 0.47%
unidirectional_lockless/4096 kqueue bytes_per_sec 2.71 GB/s 2.71 GB/s 🔴▼ -0.10% 0.28%
unidirectional_lockless/65536 kqueue bytes_per_sec 3.51 GB/s 3.51 GB/s 🔴▼ -0.28% 0.50%

socket_latency

Benchmark Backend Metric Base Head Δ Noise
concurrent/1 kqueue latency_mean_ns 18.85 µs 18.93 µs 🔴▼ -0.51% 0.37%
concurrent/16 kqueue latency_mean_ns 119.90 µs 120.50 µs 🔴▼ -0.64% 0.47%
concurrent/4 kqueue latency_mean_ns 43.61 µs 43.46 µs 🔴▼ -0.07% 1.23%
concurrent_lockless/1 kqueue latency_mean_ns 18.78 µs 18.86 µs 🔴▼ -0.29% 0.29%
concurrent_lockless/16 kqueue latency_mean_ns 120.30 µs 119.63 µs 🟢▲ +1.15% 0.88%
concurrent_lockless/4 kqueue latency_mean_ns 43.08 µs 42.75 µs 🟢▲ +1.04% 0.80%
pingpong/1 kqueue latency_mean_ns 15.51 µs 15.50 µs 🔴▼ -0.07% 1.59%
pingpong/1024 kqueue latency_mean_ns 18.98 µs 19.10 µs 🔴▼ -0.02% 1.45%
pingpong/64 kqueue latency_mean_ns 18.85 µs 18.94 µs 🔴▼ -0.19% 0.56%
pingpong_lockless/1 kqueue latency_mean_ns 15.40 µs 15.40 µs 🟢▲ +0.09% 0.16%
pingpong_lockless/1024 kqueue latency_mean_ns 19.08 µs 19.10 µs 🔴▼ -0.06% 0.36%
pingpong_lockless/64 kqueue latency_mean_ns 18.80 µs 18.84 µs 🔴▼ -0.30% 0.28%

socket_throughput

Benchmark Backend Metric Base Head Δ Noise
bidirectional/1024 kqueue bytes_per_sec 470.2 MB/s 469.8 MB/s 🔴▼ -0.05% 1.03%
bidirectional/1048576 kqueue bytes_per_sec 7.86 GB/s 7.67 GB/s 🔴▼ -1.45% 4.35%
bidirectional/16384 kqueue bytes_per_sec 4.51 GB/s 4.54 GB/s 🟢▲ +0.81% 0.64%
bidirectional/262144 kqueue bytes_per_sec 8.82 GB/s 8.69 GB/s 🔴▼ -1.58% 2.25%
bidirectional/4096 kqueue bytes_per_sec 1.80 GB/s 1.81 GB/s 🟢▲ +0.70% 1.08%
bidirectional/65536 kqueue bytes_per_sec 7.49 GB/s 7.59 GB/s 🟢▲ +0.01% 1.78%
bidirectional_lockless/1024 kqueue bytes_per_sec 471.6 MB/s 473.8 MB/s 🟢▲ +0.54% 0.92%
bidirectional_lockless/1048576 kqueue bytes_per_sec 7.87 GB/s 7.63 GB/s 🔴▼ -1.71% 3.92%
bidirectional_lockless/16384 kqueue bytes_per_sec 4.57 GB/s 4.58 GB/s 🟢▲ +0.89% 1.67%
bidirectional_lockless/262144 kqueue bytes_per_sec 8.86 GB/s 8.77 GB/s 🔴▼ -0.21% 1.85%
bidirectional_lockless/4096 kqueue bytes_per_sec 1.82 GB/s 1.81 GB/s 🔴▼ -0.48% 0.63%
bidirectional_lockless/65536 kqueue bytes_per_sec 7.54 GB/s 7.61 GB/s 🟢▲ +0.48% 1.69%
multithread/2 kqueue bytes_per_sec 7.60 GB/s 7.61 GB/s 🟢▲ +1.85% 4.11%
multithread/4 kqueue bytes_per_sec 3.67 GB/s 3.78 GB/s 🟢▲ +5.71% 3.11%
multithread/8 kqueue bytes_per_sec 3.22 GB/s 3.43 GB/s 🟢▲ +10.20% 11.96%
unidirectional/1024 kqueue bytes_per_sec 455.3 MB/s 453.4 MB/s 🟢▲ +0.39% 1.71%
unidirectional/1048576 kqueue bytes_per_sec 9.03 GB/s 8.78 GB/s 🔴▼ -1.02% 5.39%
unidirectional/16384 kqueue bytes_per_sec 4.46 GB/s 4.46 GB/s 🔴▼ -0.04% 1.85%
unidirectional/262144 kqueue bytes_per_sec 8.92 GB/s 8.90 GB/s 🟢▲ +0.47% 1.70%
unidirectional/4096 kqueue bytes_per_sec 1.79 GB/s 1.78 GB/s 🟢▲ +0.09% 1.49%
unidirectional/65536 kqueue bytes_per_sec 7.37 GB/s 7.38 GB/s 🟢▲ +0.15% 2.48%
unidirectional_lockless/1024 kqueue bytes_per_sec 472.8 MB/s 472.0 MB/s 🔴▼ -0.04% 1.98%
unidirectional_lockless/1048576 kqueue bytes_per_sec 9.02 GB/s 8.87 GB/s 🔴▼ -0.57% 5.13%
unidirectional_lockless/16384 kqueue bytes_per_sec 4.48 GB/s 4.47 GB/s 🔴▼ -0.40% 2.17%
unidirectional_lockless/262144 kqueue bytes_per_sec 8.89 GB/s 8.93 GB/s 🟢▲ +0.46% 2.30%
unidirectional_lockless/4096 kqueue bytes_per_sec 1.79 GB/s 1.80 GB/s 🟢▲ +0.53% 2.31%
unidirectional_lockless/65536 kqueue bytes_per_sec 7.34 GB/s 7.42 GB/s 🟢▲ +2.18% 1.50%

Run & raw JSON artifacts · flag rule: |median Δ| > max(2%, 3×CV of the noisier side) · advisory only

Opening and closing sockets, acceptors, timers, files, resolvers and
signal sets allocated and freed an implementation each time, and
accepting, random-access file I/O and launching a tcp_server
connection allocated per operation. Implementations now come from
per-type object pools and are held through counted object_ref
handles, so an operation in flight keeps its object alive. Accept
nodes, random-access file operations and connection launches are
recycled, multishot accept hands its nodes to the kernel, and the
IOCP wait reactor reuses its drain buffers, so steady-state I/O no
longer allocates.

Tests poison recycled implementations, churn them, and check that
steady-state operations do not allocate. The fault harness chains its
shadows into the sanitizer interceptors.
@sgerbino
sgerbino force-pushed the pr/lifetime-executor branch from 2669b45 to b173ee3 Compare October 16, 2026 16:54
@cppalliance-bot

cppalliance-bot commented Oct 16, 2026 •

Copy link
Copy Markdown

An automated preview of the documentation is available at https://395.corosio.prtest3.cppalliance.org/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-10-16 17:39:29 UTC

An awaiter is resumed through the continuation its awaitable holds,
on its own context rather than the one that completed it. A file
keeps its descriptor number allocated while a pool worker uses it,
and adopting a descriptor is refused in the unsafe locking tier.

Timers shut down before the scheduler drains, a drain's writes are
ordered before a lock-free cancel trusts them, a stop request on
another thread can race timer teardown, and timeout()'s detached
timer coroutine is allocated outside the awaiting chain. Signals are
delivered outside the signal pipe's lock.

A TLS stream can be destroyed or moved with an operation in flight,
and a wolfSSL shutdown finishes when its close_notify arrives behind
another record. An accepted peer whose awaiter never resumes is
closed, including one a drained reactor accept already took. A socket
or mocket pair whose connect is refused fails.
A pending connect is cancelled when its socket is released, and an
acceptor keeps listening after cancel(). Cancel requests target only
the object's own operations and are never dropped, an operation whose
stop lands before its SQE is linked is cancelled, and a wait cancelled
by close skips the SO_ERROR probe. A post from another thread under
unsafe_io wakes the run thread, and run_for is bounded by its
duration when a later timer is armed.
Socket cancellation is re-checked before an operation can complete,
and connects and lookups stopped before they are issued are
cancelled. An object handle's wait is reserved atomically and its
stop callback engaged before it is published. An accept's peer is
constructed before the accept is armed, the local acceptor's close is
defined in every local socket translation unit, and sockets in
skip-on-success mode are refused for adoption.

The wait reactor skips the SO_ERROR probe for a closed socket, ends a
wait whose stop lands while it registers, and no longer has cancels
queued before it runs. Shutdown sweeps the port past the stop signal,
and signal-wait completions are posted instead of resumed inline.
A tcp_server can be moved or destroyed while it runs, a connection
launched after stop() is stopped, and set_workers is refused while a
worker is in use. A worker stays out of the pool until its run()
returns, and a connection is allocated outside the chain that
launches it. The documentation says what an exception from a
worker's run() does and that the server binds only while no session
is active.
Destroying an io_context left coroutines suspended in its operations,
timers, signal waits, blocking-pool work and queues suspended forever,
with their frames and everything they held leaked. Each holder now
gives a continuation it will never resume to capy::abandon, which
unwinds the coroutine's chain from its owner on the owner's executor:
reactor, io_uring and IOCP operations, timers, signal waits, resolver
and file work on the blocking pool, posted work, and tcp_server
connections and workers.

Schedulers finish with themselves before work posted to them can run,
wait for cancellations still posting before they drain, destroy work
posted after the drain, and stop under their lock. A lockless io_uring
context can be torn down from any thread. Select is woken before the
lock an operation parked under is released, and posting a ready
reactor operation is its initiator's last touch. Blocking-pool
operations count as work on the context that owns them, and a run
call restores the thread's frame allocator. A completed operation
whose executor throws terminates.

tcp_server holds work on a connection's context until it ends,
returns workers when that context is torn down, gives back a launch
whose post throws, and transfers from its launch coroutine as a task
does. Socket and mocket pairs finish their setup before returning or
throwing. Teardown tests run under AddressSanitizer, and the
documentation states what io_context teardown destroys and the order
between contexts.
One page now says how long I/O objects, buffers and contexts must
live, how to stop work, what destroying a context does to coroutines
still suspended in it, why work must not cross contexts at teardown,
and what happens when an executor throws as an operation completes.
It ends with a checklist for code that suspends and resumes
coroutines, covering the ways such code can break teardown.
@sgerbino
sgerbino force-pushed the pr/lifetime-executor branch from b173ee3 to dbb0bed Compare October 16, 2026 17:33
@sgerbino
sgerbino merged commit c013eac into cppalliance:develop Oct 16, 2026
48 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Beast2 Oct 16, 2026
@sgerbino
sgerbino deleted the pr/lifetime-executor branch October 16, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment