1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! TPC (thread-per-core) runtime scaffolding for the wombatkv daemon.
//!
//! # Why this module exists
//!
//! Per-shard compio runtime with SO_REUSEPORT for connection distribution.
//! "shard = OS thread + dedicated compio runtime + CPU pin" shape so
//! the per-prefix worker loop runs on a completion-based reactor with
//! no cross-thread future migration and (on Linux) a hard CPU pin.
//! Today the daemon spawns one plain `std::thread` per prefix and
//! drains the SHM ring synchronously. That works but leaves the
//! Linux `io_uring` / pinned-shard wins on the table. The shape we
//! want is the same per-shard completion-based reactor pattern that
//! high-concurrency request paths benefit from.
//!
//! This module is the SHAPE commit, not the perf commit. It gives the
//! daemon binary an opt-in (`--tpc` CLI flag) path that:
//!
//! 1. spawns N OS threads (one per shard),
//! 2. calls `pin_to_cpu(shard_id)` on each thread (no-op on macOS,
//! `sched_setaffinity` on Linux),
//! 3. builds a fresh `compio::runtime::Runtime` per thread,
//! 4. runs the shard's async closure to completion inside that
//! runtime via `rt.block_on(...)`.
//!
//! Cross-thread future migration is impossible by construction: each
//! shard's future is owned by exactly one runtime, never woken by
//! another. That isolation invariant is the whole reason for the
//! per-shard runtime split.
//!
//! # macOS vs Linux
//!
//! `compio` on macOS uses kqueue, which delivers correctness but NOT
//! the `io_uring` perf wins for high-concurrency request paths.
//! Production target is Linux + `sched_setaffinity`. Mac dev box
//! sees the SHAPE land cleanly; perf bench must be on Linux to
//! validate the high-concurrency numbers.
//!
//! On macOS this module still gives us:
//! - per-shard isolation (no future migration),
//! - per-shard reactor (each thread parks on its own kqueue fd),
//! - the wiring needed for a thread-per-core bench cell on a Linux runner
//! to drop in `sched_setaffinity` and immediately get the
//! high-concurrency perf shape.
//!
//! # NOT implemented here (deferred)
//!
//! * Per-shard SHM mailbox routing, the existing per-prefix
//! `disruptor_mp` rings still hold, the TPC scaffold only owns
//! the thread+runtime. Cross-shard routing is a future enhancement.
//! * Async ring-pair attach / drain: `serve_prefix_async` below
//! is a thin async wrapper around the existing sync `serve_prefix`
//! body; the actual `compio::io`-backed SHM driver is a future enhancement.
//! * rkyv on the runtime crossing.
use Future;
use Pin;
use Arc;
use JoinHandle;
/// Future returned by a shard closure. Boxed + `Send` so the same
/// closure can be invoked on every shard thread and the shape doesn't
/// need to thread a generic future type through the runtime.
pub type ShardFuture = ;
/// Builder closure for a per-shard future. Receives the shard id
/// (`0..num_shards`) and returns the future that runs to completion
/// inside the per-shard compio runtime.
pub type ShardFn = dyn Fn + Send + Sync + 'static;
/// Spawn `num_shards` OS threads, each pinned to a CPU (Linux only),
/// each running its own `compio::runtime::Runtime`. The runtime drives
/// the future produced by `shard_fn(shard_id)` to completion via
/// `rt.block_on(...)`.
///
/// Returns one `JoinHandle` per shard. The caller is responsible for
/// joining them. Each shard runs independently, a panic on shard N
/// does NOT affect shards M ≠ N.
///
/// Naming: threads are named `wkvd-shard-{N}` so they show up in
/// `top -H` / `tracy` / `Instruments` clearly.
/// Pin the calling thread to a specific CPU.
///
/// On Linux, calls `sched_setaffinity(0, ...)` with a cpu_set
/// containing only `cpu_id`. On other platforms (notably macOS),
/// this is a no-op. Darwin doesn't expose hard CPU pinning to
/// user space. The shape commit still lands; the perf win does not.
///
/// Best-effort: errors are swallowed (logged via stderr only on
/// Linux). A pin failure is not fatal, the shard still runs, just
/// without the cache-line locality benefit.
/// macOS / FreeBSD / etc, no hard CPU pinning available.
///
/// We could call `pthread_set_qos_class_self_np` on macOS to nudge
/// the scheduler (similar to the existing `set_background_qos_self`
/// in the daemon binary), but that is `QoS`, not pinning, and is
/// orthogonal to this module's scope. Left as a no-op.