1use std::future::Future;
39use std::pin::Pin;
40use std::sync::Arc;
41use std::sync::atomic::{AtomicBool, Ordering};
42use std::task::{Context, Poll, Wake, Waker};
43use std::thread::JoinHandle;
44use std::time::Duration;
45
46use parking_lot::Mutex;
47
48use crate::cross_process_waker::CrossProcessWaker;
49use crate::shared_ring::RingError;
50use crate::spsc_ring::{SpscRingCore, SPSC_PAYLOAD_BYTES};
51
52const REACTOR_HEAL_INTERVAL: Duration = Duration::from_millis(50);
59
60pub(crate) fn wake_trace() -> bool {
65 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
66 *ON.get_or_init(|| std::env::var_os("SUBETHA_WAKE_TRACE").is_some_and(|v| v == "1"))
67}
68
69pub(crate) static POLLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
73pub(crate) static POPS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
74pub(crate) static REGISTERS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
75
76struct ThreadWaker {
79 thread: std::thread::Thread,
80}
81
82impl Wake for ThreadWaker {
83 fn wake(self: Arc<Self>) {
84 self.thread.unpark();
85 }
86 fn wake_by_ref(self: &Arc<Self>) {
87 self.thread.unpark();
88 }
89}
90
91pub fn block_on<F: Future>(future: F) -> F::Output {
95 let mut future = Box::pin(future);
96 let waker = Waker::from(Arc::new(ThreadWaker {
97 thread: std::thread::current(),
98 }));
99 let mut cx = Context::from_waker(&waker);
100 loop {
101 match future.as_mut().poll(&mut cx) {
102 Poll::Ready(v) => return v,
103 Poll::Pending => std::thread::park(),
106 }
107 }
108}
109
110enum SenderSignal {
112 Local(Arc<Mutex<Option<Waker>>>),
114 Cross(Arc<CrossProcessWaker>),
116}
117
118pub struct ReactiveSender {
122 ring: Arc<SpscRingCore>,
123 signal: SenderSignal,
124}
125
126impl ReactiveSender {
127 pub fn try_send(&self, payload: &[u8]) -> Result<(), RingError> {
130 self.ring.try_push(payload)?;
131 match &self.signal {
132 SenderSignal::Local(slot) => {
133 if let Some(w) = slot.lock().take() {
134 w.wake();
135 }
136 }
137 SenderSignal::Cross(xwaker) => {
138 xwaker.wake_up_to(self.ring.head());
139 }
140 }
141 Ok(())
142 }
143
144 pub fn published(&self) -> u64 {
146 self.ring.head()
147 }
148}
149
150pub struct ReactiveReceiver {
154 ring: Arc<SpscRingCore>,
155 slot: Arc<Mutex<Option<Waker>>>,
156 _reactor: Option<ReactorHandle>,
159}
160
161impl ReactiveReceiver {
162 pub fn recv(&self) -> ReactiveRecv {
165 ReactiveRecv {
166 ring: Arc::clone(&self.ring),
167 slot: Arc::clone(&self.slot),
168 }
169 }
170
171 pub fn try_recv(&self, out: &mut [u8]) -> Result<usize, RingError> {
173 self.ring.try_pop(out)
174 }
175}
176
177pub struct ReactiveRecv {
179 ring: Arc<SpscRingCore>,
180 slot: Arc<Mutex<Option<Waker>>>,
181}
182
183impl Future for ReactiveRecv {
184 type Output = [u8; SPSC_PAYLOAD_BYTES];
185
186 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
187 POLLS.fetch_add(1, Ordering::Relaxed);
188 let mut out = [0u8; SPSC_PAYLOAD_BYTES];
189 if self.ring.try_pop(&mut out).is_ok() {
190 POPS.fetch_add(1, Ordering::Relaxed);
191 return Poll::Ready(out);
192 }
193 *self.slot.lock() = Some(cx.waker().clone());
196 REGISTERS.fetch_add(1, Ordering::Relaxed);
197 if self.ring.try_pop(&mut out).is_ok() {
198 POPS.fetch_add(1, Ordering::Relaxed);
199 return Poll::Ready(out);
200 }
201 Poll::Pending
202 }
203}
204
205struct ReactorHandle {
207 shutdown: Arc<AtomicBool>,
208 xwaker: Arc<CrossProcessWaker>,
209 join: Option<JoinHandle<()>>,
210}
211
212impl Drop for ReactorHandle {
213 fn drop(&mut self) {
214 self.shutdown.store(true, Ordering::Release);
215 self.xwaker.wake_all();
217 if let Some(j) = self.join.take() {
218 j.join().ok();
219 }
220 }
221}
222
223pub fn anon_pair(
227 capacity: usize,
228) -> Result<(ReactiveSender, ReactiveReceiver), RingError> {
229 let ring = Arc::new(SpscRingCore::create_anon(capacity)?);
230 let slot = Arc::new(Mutex::new(None));
231 Ok((
232 ReactiveSender {
233 ring: Arc::clone(&ring),
234 signal: SenderSignal::Local(Arc::clone(&slot)),
235 },
236 ReactiveReceiver { ring, slot, _reactor: None },
237 ))
238}
239
240pub fn sender_cross(
243 ring: Arc<SpscRingCore>,
244 xwaker: Arc<CrossProcessWaker>,
245) -> ReactiveSender {
246 ReactiveSender { ring, signal: SenderSignal::Cross(xwaker) }
247}
248
249pub fn receiver_cross(
253 ring: Arc<SpscRingCore>,
254 xwaker: Arc<CrossProcessWaker>,
255) -> ReactiveReceiver {
256 let slot: Arc<Mutex<Option<Waker>>> = Arc::new(Mutex::new(None));
257 let shutdown = Arc::new(AtomicBool::new(false));
258
259 let join = {
260 let ring = Arc::clone(&ring);
261 let xwaker = Arc::clone(&xwaker);
262 let slot = Arc::clone(&slot);
263 let shutdown = Arc::clone(&shutdown);
264 std::thread::spawn(move || reactor_loop(ring, xwaker, slot, shutdown))
265 };
266
267 ReactiveReceiver {
268 ring,
269 slot,
270 _reactor: Some(ReactorHandle {
271 shutdown,
272 xwaker,
273 join: Some(join),
274 }),
275 }
276}
277
278fn reactor_loop(
282 ring: Arc<SpscRingCore>,
283 xwaker: Arc<CrossProcessWaker>,
284 slot: Arc<Mutex<Option<Waker>>>,
285 shutdown: Arc<AtomicBool>,
286) {
287 let trace = wake_trace();
288 let (mut iters, mut advances, mut fires, mut fires_empty) = (0u64, 0u64, 0u64, 0u64);
289 let (mut parks, mut park_full, mut waits) = (0u64, 0u64, 0u64);
290 let mut last_snap = std::time::Instant::now();
291 let mut last = ring.head();
292 loop {
293 if trace {
294 iters += 1;
295 if last_snap.elapsed() >= Duration::from_secs(1) {
296 last_snap = std::time::Instant::now();
297 eprintln!(
298 "subetha: reactor ring={:p} head={} iters={iters} adv={advances} \
299 fires={fires} fires_empty={fires_empty} parks={parks} \
300 park_full={park_full} waits={waits} | futures polls={} pops={} regs={}",
301 Arc::as_ptr(&ring),
302 ring.head(),
303 POLLS.load(Ordering::Relaxed),
304 POPS.load(Ordering::Relaxed),
305 REGISTERS.load(Ordering::Relaxed),
306 );
307 }
308 }
309 if shutdown.load(Ordering::Acquire) {
310 break;
311 }
312 let head = ring.head();
313 if head != last {
314 last = head;
315 advances += 1;
316 if let Some(w) = slot.lock().take() {
317 fires += 1;
318 w.wake();
319 } else {
320 fires_empty += 1;
321 }
322 continue;
323 }
324 if ring.head() != ring.tail()
331 && let Some(w) = slot.lock().take()
332 {
333 fires += 1;
334 w.wake();
335 continue;
336 }
337 match xwaker.try_park(head + 1) {
339 Ok(token) => {
340 parks += 1;
341 if shutdown.load(Ordering::Acquire) || ring.head() != head {
345 xwaker.release(token);
346 continue;
347 }
348 xwaker.wait(token, Some(REACTOR_HEAL_INTERVAL)).ok();
354 waits += 1;
355 }
356 Err(crate::cross_process_waker::WakerError::Full) => {
357 park_full += 1;
358 std::hint::spin_loop();
360 }
361 Err(_) => break,
362 }
363 }
364}
365
366pub(crate) struct SeqReactor {
372 shutdown: Arc<AtomicBool>,
373 xwaker: Arc<CrossProcessWaker>,
374 join: Option<JoinHandle<()>>,
375}
376
377impl Drop for SeqReactor {
378 fn drop(&mut self) {
379 self.shutdown.store(true, Ordering::Release);
380 self.xwaker.wake_all();
381 if let Some(j) = self.join.take() {
382 j.join().ok();
383 }
384 }
385}
386
387pub(crate) fn spawn_seq_reactor(
390 published: Arc<dyn Fn() -> u64 + Send + Sync>,
391 xwaker: Arc<CrossProcessWaker>,
392 slot: Arc<Mutex<Option<Waker>>>,
393) -> SeqReactor {
394 let shutdown = Arc::new(AtomicBool::new(false));
395 let join = {
396 let xwaker = Arc::clone(&xwaker);
397 let shutdown = Arc::clone(&shutdown);
398 std::thread::spawn(move || seq_reactor_loop(published, &xwaker, &slot, &shutdown))
399 };
400 SeqReactor { shutdown, xwaker, join: Some(join) }
401}
402
403fn seq_reactor_loop(
404 published: Arc<dyn Fn() -> u64 + Send + Sync>,
405 xwaker: &CrossProcessWaker,
406 slot: &Mutex<Option<Waker>>,
407 shutdown: &AtomicBool,
408) {
409 let mut last = u64::MAX;
413 loop {
414 if shutdown.load(Ordering::Acquire) {
415 break;
416 }
417 let cur = published();
418 if cur != last {
419 last = cur;
420 if let Some(w) = slot.lock().take() {
421 w.wake();
422 }
423 continue;
424 }
425 match xwaker.try_park(cur + 1) {
426 Ok(token) => {
427 if shutdown.load(Ordering::Acquire) || published() != cur {
428 xwaker.release(token);
429 continue;
430 }
431 xwaker.wait(token, Some(REACTOR_HEAL_INTERVAL)).ok();
432 }
433 Err(crate::cross_process_waker::WakerError::Full) => {
434 std::hint::spin_loop();
435 }
436 Err(_) => break,
437 }
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use super::*;
444 use std::sync::atomic::{AtomicU64, Ordering};
445 use std::thread;
446
447 #[test]
448 fn intra_process_block_on_parks_and_wakes_on_send() {
449 let (tx, rx) = anon_pair(8).unwrap();
453 const N: u64 = 1000;
454
455 let producer = thread::spawn(move || {
456 for i in 0..N {
457 let mut buf = [0u8; SPSC_PAYLOAD_BYTES];
458 buf[..8].copy_from_slice(&i.to_le_bytes());
459 while tx.try_send(&buf).is_err() {
460 std::hint::spin_loop();
461 }
462 }
463 });
464
465 let sum = block_on(async move {
466 let mut s = 0u64;
467 for _ in 0..N {
468 let item = rx.recv().await;
469 s += u64::from_le_bytes(item[..8].try_into().unwrap());
470 }
471 s
472 });
473
474 producer.join().unwrap();
475 assert_eq!(sum, (0..N).sum());
476 }
477
478 #[test]
479 fn many_futures_one_local_signal() {
480 let (tx, rx) = anon_pair(4).unwrap();
484 let got = Arc::new(AtomicU64::new(0));
485 let got2 = Arc::clone(&got);
486
487 let consumer = thread::spawn(move || {
488 block_on(async move {
489 for _ in 0..500u64 {
490 let item = rx.recv().await;
491 got2.fetch_add(
492 u64::from_le_bytes(item[..8].try_into().unwrap()),
493 Ordering::AcqRel,
494 );
495 }
496 });
497 });
498
499 for i in 0..500u64 {
500 let mut buf = [0u8; SPSC_PAYLOAD_BYTES];
501 buf[..8].copy_from_slice(&i.to_le_bytes());
502 while tx.try_send(&buf).is_err() {
503 std::hint::spin_loop();
504 }
505 }
506 consumer.join().unwrap();
507 assert_eq!(got.load(Ordering::Acquire), (0..500u64).sum());
508 }
509}