1use std::cell::{Cell, UnsafeCell};
2use std::collections::VecDeque;
3use std::sync::{Arc, atomic::AtomicBool, atomic::Ordering};
4use std::{future::Future, io, thread};
5
6use async_task::Runnable;
7use crossbeam_queue::SegQueue;
8use swap_buffer_queue::error::{TryDequeueError, TryEnqueueError};
9use swap_buffer_queue::{Queue, buffer::ArrayBuffer};
10
11use crate::{driver::Driver, driver::Notify, driver::PollResult, handle::JoinHandle};
12
13scoped_tls::scoped_thread_local!(static CURRENT_RUNTIME: Runtime);
14
15thread_local!(static THREAD_ID: thread::ThreadId = thread::current().id());
16
17fn current_thread_id() -> thread::ThreadId {
19 THREAD_ID.with(|id| *id)
20}
21
22#[derive(Debug)]
23pub struct Runtime {
27 stop: Cell<bool>,
28 queue: Arc<RunnableQueue>,
29}
30
31impl Runtime {
32 pub fn new(handle: Box<dyn Notify>) -> Self {
34 Self::builder().build(handle)
35 }
36
37 pub fn builder() -> RuntimeBuilder {
39 RuntimeBuilder::new()
40 }
41
42 #[allow(clippy::arc_with_non_send_sync)]
43 fn with_builder(builder: &RuntimeBuilder, handle: Box<dyn Notify>) -> Self {
44 Self {
45 stop: Cell::new(false),
46 queue: Arc::new(RunnableQueue::new(builder.event_interval, handle)),
47 }
48 }
49
50 pub fn with_current<T, F: FnOnce(&Self) -> T>(f: F) -> T {
56 #[cold]
57 fn not_in_neon_runtime() -> ! {
58 panic!("not in a neon runtime")
59 }
60
61 if CURRENT_RUNTIME.is_set() {
62 CURRENT_RUNTIME.with(f)
63 } else {
64 not_in_neon_runtime()
65 }
66 }
67
68 #[inline]
69 pub fn handle(&self) -> Handle {
71 Handle {
72 queue: self.queue.clone(),
73 }
74 }
75
76 pub fn spawn<F: Future + 'static>(&self, future: F) -> JoinHandle<F::Output> {
81 unsafe { self.spawn_unchecked(future) }
82 }
83
84 pub unsafe fn spawn_unchecked<F: Future>(&self, future: F) -> JoinHandle<F::Output> {
90 let queue = self.queue.clone();
91 let (runnable, task) = unsafe {
92 async_task::spawn_unchecked(future, move |runnable| {
93 queue.schedule(runnable);
94 })
95 };
96 runnable.schedule();
97 JoinHandle::new(task)
98 }
99
100 #[inline]
107 pub fn awake(&self) {
108 self.queue.idle.set(false);
109 }
110
111 pub fn poll(&self) -> PollResult {
113 if self.stop.get() {
114 PollResult::Ready
115 } else if self.queue.run() {
116 PollResult::PollAgain
117 } else {
118 PollResult::Pending
119 }
120 }
121
122 pub fn block_on<F: Future>(&self, future: F, driver: &dyn Driver) -> F::Output {
130 self.stop.set(false);
131
132 CURRENT_RUNTIME.set(self, || {
133 let mut result = None;
134 unsafe {
135 self.spawn_unchecked(async {
136 result = Some(future.await);
137 self.stop.set(true);
138 let _ = self.queue.handle.notify();
139 });
140 }
141
142 ntex_error::set_backtrace_start_alt("src/raw.rs", 0);
143 driver.run(self).expect("Driver failed");
144 result.expect("Driver failed to poll")
145 })
146 }
147}
148
149impl Drop for Runtime {
150 fn drop(&mut self) {
151 CURRENT_RUNTIME.set(self, || {
152 self.queue.clear();
153 });
154 }
155}
156
157#[derive(Debug)]
158pub struct Handle {
160 queue: Arc<RunnableQueue>,
161}
162
163impl Handle {
164 pub fn current() -> Handle {
170 Runtime::with_current(Runtime::handle)
171 }
172
173 pub fn notify(&self) -> io::Result<()> {
175 self.queue.handle.notify()
176 }
177
178 pub fn spawn<F: Future + Send + 'static>(&self, future: F) -> JoinHandle<F::Output> {
183 let queue = self.queue.clone();
184 let schedule = move |runnable| {
185 queue.schedule(runnable);
186 };
187 let (runnable, task) = unsafe { async_task::spawn_unchecked(future, schedule) };
188 runnable.schedule();
189 JoinHandle::new(task)
190 }
191}
192
193impl Clone for Handle {
194 fn clone(&self) -> Self {
195 Self {
196 queue: self.queue.clone(),
197 }
198 }
199}
200
201#[derive(Debug)]
202struct RunnableQueue {
203 id: thread::ThreadId,
204 idle: Cell<bool>,
205 notified: AtomicBool,
207 handle: Box<dyn Notify>,
208 event_interval: usize,
209 local_queue: UnsafeCell<VecDeque<Runnable>>,
210 sync_fixed_queue: Queue<ArrayBuffer<Runnable, 128>>,
211 sync_queue: SegQueue<Runnable>,
212}
213
214unsafe impl Send for RunnableQueue {}
215unsafe impl Sync for RunnableQueue {}
216
217impl RunnableQueue {
218 fn new(event_interval: usize, handle: Box<dyn Notify>) -> Self {
219 Self {
220 handle,
221 event_interval,
222 id: current_thread_id(),
223 idle: Cell::new(true),
224 notified: AtomicBool::new(false),
225 local_queue: UnsafeCell::new(VecDeque::new()),
226 sync_fixed_queue: Queue::default(),
227 sync_queue: SegQueue::new(),
228 }
229 }
230
231 fn schedule(&self, runnable: Runnable) {
232 if self.id == current_thread_id() {
233 unsafe { (*self.local_queue.get()).push_back(runnable) };
234 if self.idle.get() {
235 self.idle.set(false);
236 self.handle.notify().ok();
237 }
238 } else {
239 let result = self.sync_fixed_queue.try_enqueue([runnable]);
240 if let Err(TryEnqueueError::InsufficientCapacity([runnable])) = result {
241 self.sync_queue.push(runnable);
242 }
243 if !self.notified.swap(true, Ordering::Release) {
246 self.handle.notify().ok();
247 }
248 }
249 }
250
251 fn run(&self) -> bool {
252 if self.notified.load(Ordering::Relaxed) {
255 self.notified.swap(false, Ordering::Acquire);
256 }
257
258 for _ in 0..self.event_interval {
261 if let Some(task) = self.pop_local() {
262 task.run();
263 } else {
264 break;
265 }
266 }
267
268 let sync_queue_fixed = match self.sync_fixed_queue.try_dequeue() {
269 Ok(buf) => {
270 for task in buf {
271 task.run();
272 }
273 false
274 }
275 Err(TryDequeueError::Empty | TryDequeueError::Closed) => false,
276 Err(_) => true,
277 };
278
279 let sync_queue = {
280 for _ in 0..self.event_interval {
281 if let Some(task) = self.sync_queue.pop() {
282 task.run();
283 } else {
284 break;
285 }
286 }
287 !self.sync_queue.is_empty()
288 };
289
290 let local_queue = unsafe { !(*self.local_queue.get()).is_empty() };
292 let more_tasks = local_queue || sync_queue_fixed || sync_queue;
293 if !more_tasks {
294 self.idle.set(true);
295 }
296 more_tasks
297 }
298
299 fn clear(&self) {
300 while self.sync_queue.pop().is_some() {}
301 while self.sync_fixed_queue.try_dequeue().is_ok() {}
302 while let Some(task) = self.pop_local() {
304 drop(task);
305 }
306 }
307
308 fn pop_local(&self) -> Option<Runnable> {
309 unsafe { (*self.local_queue.get()).pop_front() }
310 }
311}
312
313#[derive(Debug, Clone)]
315pub struct RuntimeBuilder {
316 event_interval: usize,
317}
318
319impl Default for RuntimeBuilder {
320 fn default() -> Self {
321 Self::new()
322 }
323}
324
325impl RuntimeBuilder {
326 pub fn new() -> Self {
328 Self { event_interval: 61 }
329 }
330
331 pub fn event_interval(&mut self, val: usize) -> &mut Self {
337 self.event_interval = val.max(1);
338 self
339 }
340
341 pub fn build(&self, handle: Box<dyn Notify>) -> Runtime {
343 Runtime::with_builder(self, handle)
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use std::task::{Poll, Waker};
350 use std::{cell::RefCell, future::poll_fn, rc::Rc};
351
352 use super::*;
353
354 #[derive(Debug)]
355 struct NoopNotify;
356
357 impl Notify for NoopNotify {
358 fn notify(&self) -> io::Result<()> {
359 Ok(())
360 }
361 }
362
363 struct WakeOnDrop(Rc<RefCell<Option<Waker>>>);
364
365 impl Drop for WakeOnDrop {
366 fn drop(&mut self) {
367 if let Some(w) = self.0.borrow_mut().take() {
368 w.wake();
369 }
370 }
371 }
372
373 #[derive(Debug, Default)]
374 struct CountNotify(Arc<std::sync::atomic::AtomicUsize>);
375
376 impl Notify for CountNotify {
377 fn notify(&self) -> io::Result<()> {
378 self.0.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
379 Ok(())
380 }
381 }
382
383 #[test]
384 fn awake_skips_local_notify() {
385 use std::sync::atomic::Ordering;
386
387 let cnt = Arc::new(std::sync::atomic::AtomicUsize::new(0));
388 let rt = Runtime::new(Box::new(CountNotify(cnt.clone())));
389
390 rt.awake();
392 rt.spawn(async {}).detach();
393 assert_eq!(cnt.load(Ordering::Relaxed), 0);
394 assert_eq!(rt.poll(), PollResult::Pending);
395
396 rt.spawn(async {}).detach();
398 rt.spawn(async {}).detach();
399 assert_eq!(cnt.load(Ordering::Relaxed), 1);
400 assert_eq!(rt.poll(), PollResult::Pending);
401 }
402
403 #[test]
404 fn remote_schedule_notifies_once() {
405 let cnt = Arc::new(std::sync::atomic::AtomicUsize::new(0));
406 let rt = Runtime::new(Box::new(CountNotify(cnt.clone())));
407 let spawn = |n| {
408 let hnd = rt.handle();
409 std::thread::spawn(move || {
410 for _ in 0..n {
411 hnd.spawn(async {}).detach();
412 }
413 })
414 .join()
415 .unwrap();
416 };
417
418 spawn(100);
419 assert_eq!(cnt.load(Ordering::Relaxed), 1);
420 assert_eq!(rt.poll(), PollResult::Pending);
421
422 spawn(100);
424 assert_eq!(cnt.load(Ordering::Relaxed), 2);
425 assert_eq!(rt.poll(), PollResult::Pending);
426 }
427
428 #[test]
429 fn remote_schedule_no_lost_wakeup() {
430 use std::sync::{Condvar, Mutex, atomic::AtomicUsize};
431 use std::time::Duration;
432
433 #[derive(Debug, Default)]
434 struct Event(Mutex<bool>, Condvar);
435
436 #[derive(Debug)]
437 struct EventNotify(Arc<Event>);
438
439 impl Notify for EventNotify {
440 fn notify(&self) -> io::Result<()> {
441 *self.0.0.lock().unwrap() = true;
442 self.0.1.notify_one();
443 Ok(())
444 }
445 }
446
447 const THREADS: usize = 4;
448 const TASKS: usize = 10_000;
449
450 let ev = Arc::new(Event::default());
451 let rt = Runtime::new(Box::new(EventNotify(ev.clone())));
452 let cnt = Arc::new(AtomicUsize::new(0));
453 let threads: Vec<_> = (0..THREADS)
454 .map(|_| {
455 let (hnd, cnt) = (rt.handle(), cnt.clone());
456 std::thread::spawn(move || {
457 for _ in 0..TASKS {
458 let cnt = cnt.clone();
459 hnd.spawn(async move {
460 cnt.fetch_add(1, Ordering::Relaxed);
461 })
462 .detach();
463 }
464 })
465 })
466 .collect();
467
468 while cnt.load(Ordering::Relaxed) < THREADS * TASKS {
470 if rt.poll() == PollResult::Pending {
471 let guard = ev.0.lock().unwrap();
472 let (mut guard, res) =
473 ev.1.wait_timeout_while(guard, Duration::from_secs(5), |n| !*n)
474 .unwrap();
475 assert!(!res.timed_out(), "lost wakeup");
476 *guard = false;
477 }
478 }
479 for t in threads {
480 t.join().unwrap();
481 }
482 }
483
484 #[test]
485 fn local_task_woken_by_remote_task() {
486 use std::sync::{Mutex, atomic::AtomicBool, atomic::Ordering};
487
488 let rt = Runtime::new(Box::new(NoopNotify));
489 let waker = Arc::new(Mutex::new(None::<Waker>));
490 let done = Rc::new(AtomicBool::new(false));
491 let (waker2, done2) = (waker.clone(), done.clone());
492 rt.spawn(poll_fn(move |cx| {
493 if waker2.lock().unwrap().replace(cx.waker().clone()).is_some() {
494 done2.store(true, Ordering::Relaxed);
495 Poll::Ready(())
496 } else {
497 Poll::Pending
498 }
499 }))
500 .detach();
501 assert_eq!(rt.poll(), PollResult::Pending);
502
503 let hnd = rt.handle();
504 std::thread::spawn(move || {
505 hnd.spawn(async move {
506 waker.lock().unwrap().clone().unwrap().wake();
507 })
508 .detach();
509 })
510 .join()
511 .unwrap();
512
513 assert_eq!(rt.poll(), PollResult::PollAgain);
515 assert_eq!(rt.poll(), PollResult::Pending);
516 assert!(done.load(Ordering::Relaxed));
517 }
518
519 #[test]
520 fn schedule_while_running() {
521 let rt = Runtime::new(Box::new(NoopNotify));
522 let done = Rc::new(RefCell::new(0));
523 let done2 = done.clone();
524 rt.spawn(async move {
525 let h = Runtime::with_current(|rt| rt.spawn(async { 1 }));
527 *done2.borrow_mut() = h.await.unwrap();
528 })
529 .detach();
530 CURRENT_RUNTIME.set(&rt, || while rt.poll() == PollResult::PollAgain {});
531 assert_eq!(*done.borrow(), 1);
532 }
533
534 #[test]
535 fn event_interval() {
536 use std::sync::atomic::{AtomicUsize, Ordering};
537
538 for val in [0, 1] {
539 let rt = Runtime::builder()
540 .event_interval(val)
541 .build(Box::new(NoopNotify));
542 assert_eq!(rt.poll(), PollResult::Pending);
543
544 rt.spawn(async {}).detach();
545 rt.spawn(async {}).detach();
546 assert_eq!(rt.poll(), PollResult::PollAgain);
547 assert_eq!(rt.poll(), PollResult::Pending);
548 }
549
550 let rt = Runtime::builder()
552 .event_interval(1)
553 .build(Box::new(NoopNotify));
554 let cnt = Arc::new(AtomicUsize::new(0));
555 let hnd = rt.handle();
556 let cnt2 = cnt.clone();
557 std::thread::spawn(move || {
558 for _ in 0..130 {
559 let cnt = cnt2.clone();
560 hnd.spawn(async move {
561 cnt.fetch_add(1, Ordering::Relaxed);
562 })
563 .detach();
564 }
565 })
566 .join()
567 .unwrap();
568 assert_eq!(rt.poll(), PollResult::PollAgain);
569 assert_eq!(cnt.load(Ordering::Relaxed), 129);
570 assert_eq!(rt.poll(), PollResult::Pending);
571 assert_eq!(cnt.load(Ordering::Relaxed), 130);
572 }
573
574 #[test]
575 fn schedule_while_clearing() {
576 let rt = Runtime::new(Box::new(NoopNotify));
577 let waker = Rc::new(RefCell::new(None));
578 let waker2 = waker.clone();
579 rt.spawn(poll_fn(move |cx| {
580 *waker2.borrow_mut() = Some(cx.waker().clone());
581 Poll::<()>::Pending
582 }))
583 .detach();
584 assert_eq!(rt.poll(), PollResult::Pending);
585 assert!(waker.borrow().is_some());
586
587 let guard = WakeOnDrop(waker.clone());
589 rt.spawn(async move {
590 let _g = guard;
591 })
592 .detach();
593 rt.spawn(async {}).detach();
594 drop(rt);
595 assert!(waker.borrow().is_none());
596 assert_eq!(Rc::strong_count(&waker), 1);
598 }
599}