1use std::cell::{Cell, UnsafeCell};
2use std::collections::VecDeque;
3use std::{future::Future, io, sync::Arc, thread};
4
5use async_task::Runnable;
6use crossbeam_queue::SegQueue;
7use swap_buffer_queue::error::{TryDequeueError, TryEnqueueError};
8use swap_buffer_queue::{Queue, buffer::ArrayBuffer};
9
10use crate::{driver::Driver, driver::Notify, driver::PollResult, handle::JoinHandle};
11
12scoped_tls::scoped_thread_local!(static CURRENT_RUNTIME: Runtime);
13
14thread_local!(static THREAD_ID: thread::ThreadId = thread::current().id());
15
16fn current_thread_id() -> thread::ThreadId {
18 THREAD_ID.with(|id| *id)
19}
20
21#[derive(Debug)]
22pub struct Runtime {
26 stop: Cell<bool>,
27 queue: Arc<RunnableQueue>,
28}
29
30impl Runtime {
31 pub fn new(handle: Box<dyn Notify>) -> Self {
33 Self::builder().build(handle)
34 }
35
36 pub fn builder() -> RuntimeBuilder {
38 RuntimeBuilder::new()
39 }
40
41 #[allow(clippy::arc_with_non_send_sync)]
42 fn with_builder(builder: &RuntimeBuilder, handle: Box<dyn Notify>) -> Self {
43 Self {
44 stop: Cell::new(false),
45 queue: Arc::new(RunnableQueue::new(builder.event_interval, handle)),
46 }
47 }
48
49 pub fn with_current<T, F: FnOnce(&Self) -> T>(f: F) -> T {
55 #[cold]
56 fn not_in_neon_runtime() -> ! {
57 panic!("not in a neon runtime")
58 }
59
60 if CURRENT_RUNTIME.is_set() {
61 CURRENT_RUNTIME.with(f)
62 } else {
63 not_in_neon_runtime()
64 }
65 }
66
67 #[inline]
68 pub fn handle(&self) -> Handle {
70 Handle {
71 queue: self.queue.clone(),
72 }
73 }
74
75 pub fn spawn<F: Future + 'static>(&self, future: F) -> JoinHandle<F::Output> {
80 unsafe { self.spawn_unchecked(future) }
81 }
82
83 pub unsafe fn spawn_unchecked<F: Future>(&self, future: F) -> JoinHandle<F::Output> {
89 let queue = self.queue.clone();
90 let (runnable, task) = unsafe {
91 async_task::spawn_unchecked(future, move |runnable| {
92 queue.schedule(runnable);
93 })
94 };
95 runnable.schedule();
96 JoinHandle::new(task)
97 }
98
99 #[inline]
106 pub fn awake(&self) {
107 self.queue.idle.set(false);
108 }
109
110 pub fn poll(&self) -> PollResult {
112 if self.stop.get() {
113 PollResult::Ready
114 } else if self.queue.run() {
115 PollResult::PollAgain
116 } else {
117 PollResult::Pending
118 }
119 }
120
121 pub fn block_on<F: Future>(&self, future: F, driver: &dyn Driver) -> F::Output {
129 self.stop.set(false);
130
131 CURRENT_RUNTIME.set(self, || {
132 let mut result = None;
133 unsafe {
134 self.spawn_unchecked(async {
135 result = Some(future.await);
136 self.stop.set(true);
137 let _ = self.queue.handle.notify();
138 });
139 }
140
141 ntex_error::set_backtrace_start_alt("src/raw.rs", 0);
142 driver.run(self).expect("Driver failed");
143 result.expect("Driver failed to poll")
144 })
145 }
146}
147
148impl Drop for Runtime {
149 fn drop(&mut self) {
150 CURRENT_RUNTIME.set(self, || {
151 self.queue.clear();
152 });
153 }
154}
155
156#[derive(Debug)]
157pub struct Handle {
159 queue: Arc<RunnableQueue>,
160}
161
162impl Handle {
163 pub fn current() -> Handle {
169 Runtime::with_current(Runtime::handle)
170 }
171
172 pub fn notify(&self) -> io::Result<()> {
174 self.queue.handle.notify()
175 }
176
177 pub fn spawn<F: Future + Send + 'static>(&self, future: F) -> JoinHandle<F::Output> {
182 let queue = self.queue.clone();
183 let schedule = move |runnable| {
184 queue.schedule(runnable);
185 };
186 let (runnable, task) = unsafe { async_task::spawn_unchecked(future, schedule) };
187 runnable.schedule();
188 JoinHandle::new(task)
189 }
190}
191
192impl Clone for Handle {
193 fn clone(&self) -> Self {
194 Self {
195 queue: self.queue.clone(),
196 }
197 }
198}
199
200#[derive(Debug)]
201struct RunnableQueue {
202 id: thread::ThreadId,
203 idle: Cell<bool>,
204 handle: Box<dyn Notify>,
205 event_interval: usize,
206 local_queue: UnsafeCell<VecDeque<Runnable>>,
207 sync_fixed_queue: Queue<ArrayBuffer<Runnable, 128>>,
208 sync_queue: SegQueue<Runnable>,
209}
210
211unsafe impl Send for RunnableQueue {}
212unsafe impl Sync for RunnableQueue {}
213
214impl RunnableQueue {
215 fn new(event_interval: usize, handle: Box<dyn Notify>) -> Self {
216 Self {
217 handle,
218 event_interval,
219 id: current_thread_id(),
220 idle: Cell::new(true),
221 local_queue: UnsafeCell::new(VecDeque::new()),
222 sync_fixed_queue: Queue::default(),
223 sync_queue: SegQueue::new(),
224 }
225 }
226
227 fn schedule(&self, runnable: Runnable) {
228 if self.id == current_thread_id() {
229 unsafe { (*self.local_queue.get()).push_back(runnable) };
230 if self.idle.get() {
231 self.idle.set(false);
232 self.handle.notify().ok();
233 }
234 } else {
235 let result = self.sync_fixed_queue.try_enqueue([runnable]);
236 if let Err(TryEnqueueError::InsufficientCapacity([runnable])) = result {
237 self.sync_queue.push(runnable);
238 }
239 self.handle.notify().ok();
240 }
241 }
242
243 fn run(&self) -> bool {
244 for _ in 0..self.event_interval {
247 if let Some(task) = self.pop_local() {
248 task.run();
249 } else {
250 break;
251 }
252 }
253
254 let sync_queue_fixed = match self.sync_fixed_queue.try_dequeue() {
255 Ok(buf) => {
256 for task in buf {
257 task.run();
258 }
259 false
260 }
261 Err(TryDequeueError::Empty | TryDequeueError::Closed) => false,
262 Err(_) => true,
263 };
264
265 let sync_queue = {
266 for _ in 0..self.event_interval {
267 if let Some(task) = self.sync_queue.pop() {
268 task.run();
269 } else {
270 break;
271 }
272 }
273 !self.sync_queue.is_empty()
274 };
275
276 let local_queue = unsafe { !(*self.local_queue.get()).is_empty() };
278 let more_tasks = local_queue || sync_queue_fixed || sync_queue;
279 if !more_tasks {
280 self.idle.set(true);
281 }
282 more_tasks
283 }
284
285 fn clear(&self) {
286 while self.sync_queue.pop().is_some() {}
287 while self.sync_fixed_queue.try_dequeue().is_ok() {}
288 while let Some(task) = self.pop_local() {
290 drop(task);
291 }
292 }
293
294 fn pop_local(&self) -> Option<Runnable> {
295 unsafe { (*self.local_queue.get()).pop_front() }
296 }
297}
298
299#[derive(Debug, Clone)]
301pub struct RuntimeBuilder {
302 event_interval: usize,
303}
304
305impl Default for RuntimeBuilder {
306 fn default() -> Self {
307 Self::new()
308 }
309}
310
311impl RuntimeBuilder {
312 pub fn new() -> Self {
314 Self { event_interval: 61 }
315 }
316
317 pub fn event_interval(&mut self, val: usize) -> &mut Self {
323 self.event_interval = val.max(1);
324 self
325 }
326
327 pub fn build(&self, handle: Box<dyn Notify>) -> Runtime {
329 Runtime::with_builder(self, handle)
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use std::task::{Poll, Waker};
336 use std::{cell::RefCell, future::poll_fn, rc::Rc};
337
338 use super::*;
339
340 #[derive(Debug)]
341 struct NoopNotify;
342
343 impl Notify for NoopNotify {
344 fn notify(&self) -> io::Result<()> {
345 Ok(())
346 }
347 }
348
349 struct WakeOnDrop(Rc<RefCell<Option<Waker>>>);
350
351 impl Drop for WakeOnDrop {
352 fn drop(&mut self) {
353 if let Some(w) = self.0.borrow_mut().take() {
354 w.wake();
355 }
356 }
357 }
358
359 #[derive(Debug, Default)]
360 struct CountNotify(Arc<std::sync::atomic::AtomicUsize>);
361
362 impl Notify for CountNotify {
363 fn notify(&self) -> io::Result<()> {
364 self.0.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
365 Ok(())
366 }
367 }
368
369 #[test]
370 fn awake_skips_local_notify() {
371 use std::sync::atomic::Ordering;
372
373 let cnt = Arc::new(std::sync::atomic::AtomicUsize::new(0));
374 let rt = Runtime::new(Box::new(CountNotify(cnt.clone())));
375
376 rt.awake();
378 rt.spawn(async {}).detach();
379 assert_eq!(cnt.load(Ordering::Relaxed), 0);
380 assert_eq!(rt.poll(), PollResult::Pending);
381
382 rt.spawn(async {}).detach();
384 rt.spawn(async {}).detach();
385 assert_eq!(cnt.load(Ordering::Relaxed), 1);
386 assert_eq!(rt.poll(), PollResult::Pending);
387 }
388
389 #[test]
390 fn local_task_woken_by_remote_task() {
391 use std::sync::{Mutex, atomic::AtomicBool, atomic::Ordering};
392
393 let rt = Runtime::new(Box::new(NoopNotify));
394 let waker = Arc::new(Mutex::new(None::<Waker>));
395 let done = Rc::new(AtomicBool::new(false));
396 let (waker2, done2) = (waker.clone(), done.clone());
397 rt.spawn(poll_fn(move |cx| {
398 if waker2.lock().unwrap().replace(cx.waker().clone()).is_some() {
399 done2.store(true, Ordering::Relaxed);
400 Poll::Ready(())
401 } else {
402 Poll::Pending
403 }
404 }))
405 .detach();
406 assert_eq!(rt.poll(), PollResult::Pending);
407
408 let hnd = rt.handle();
409 std::thread::spawn(move || {
410 hnd.spawn(async move {
411 waker.lock().unwrap().clone().unwrap().wake();
412 })
413 .detach();
414 })
415 .join()
416 .unwrap();
417
418 assert_eq!(rt.poll(), PollResult::PollAgain);
420 assert_eq!(rt.poll(), PollResult::Pending);
421 assert!(done.load(Ordering::Relaxed));
422 }
423
424 #[test]
425 fn schedule_while_running() {
426 let rt = Runtime::new(Box::new(NoopNotify));
427 let done = Rc::new(RefCell::new(0));
428 let done2 = done.clone();
429 rt.spawn(async move {
430 let h = Runtime::with_current(|rt| rt.spawn(async { 1 }));
432 *done2.borrow_mut() = h.await.unwrap();
433 })
434 .detach();
435 CURRENT_RUNTIME.set(&rt, || while rt.poll() == PollResult::PollAgain {});
436 assert_eq!(*done.borrow(), 1);
437 }
438
439 #[test]
440 fn event_interval() {
441 use std::sync::atomic::{AtomicUsize, Ordering};
442
443 for val in [0, 1] {
444 let rt = Runtime::builder()
445 .event_interval(val)
446 .build(Box::new(NoopNotify));
447 assert_eq!(rt.poll(), PollResult::Pending);
448
449 rt.spawn(async {}).detach();
450 rt.spawn(async {}).detach();
451 assert_eq!(rt.poll(), PollResult::PollAgain);
452 assert_eq!(rt.poll(), PollResult::Pending);
453 }
454
455 let rt = Runtime::builder()
457 .event_interval(1)
458 .build(Box::new(NoopNotify));
459 let cnt = Arc::new(AtomicUsize::new(0));
460 let hnd = rt.handle();
461 let cnt2 = cnt.clone();
462 std::thread::spawn(move || {
463 for _ in 0..130 {
464 let cnt = cnt2.clone();
465 hnd.spawn(async move {
466 cnt.fetch_add(1, Ordering::Relaxed);
467 })
468 .detach();
469 }
470 })
471 .join()
472 .unwrap();
473 assert_eq!(rt.poll(), PollResult::PollAgain);
474 assert_eq!(cnt.load(Ordering::Relaxed), 129);
475 assert_eq!(rt.poll(), PollResult::Pending);
476 assert_eq!(cnt.load(Ordering::Relaxed), 130);
477 }
478
479 #[test]
480 fn schedule_while_clearing() {
481 let rt = Runtime::new(Box::new(NoopNotify));
482 let waker = Rc::new(RefCell::new(None));
483 let waker2 = waker.clone();
484 rt.spawn(poll_fn(move |cx| {
485 *waker2.borrow_mut() = Some(cx.waker().clone());
486 Poll::<()>::Pending
487 }))
488 .detach();
489 assert_eq!(rt.poll(), PollResult::Pending);
490 assert!(waker.borrow().is_some());
491
492 let guard = WakeOnDrop(waker.clone());
494 rt.spawn(async move {
495 let _g = guard;
496 })
497 .detach();
498 rt.spawn(async {}).detach();
499 drop(rt);
500 assert!(waker.borrow().is_none());
501 assert_eq!(Rc::strong_count(&waker), 1);
503 }
504}