1use std::{
2 sync::Arc,
3 thread::{self, ThreadId},
4};
5
6use async_task::{Runnable, Task};
7use crossbeam::deque::{Injector, Stealer, Worker};
8use dashmap::DashMap;
9
10use pin_project_lite::pin_project;
11
12thread_local! {
13 static WORK_QUEUE: Worker<Runnable> = Worker::new_fifo();
14}
15
16pub struct Runtime {
17 injector: Arc<Injector<Runnable>>,
18 stealers: Arc<DashMap<ThreadId, Stealer<Runnable>>>,
19}
20
21#[derive(Clone)]
22pub struct Handle {
23 injector: Arc<Injector<Runnable>>,
24 stealers: Arc<DashMap<ThreadId, Stealer<Runnable>>>,
25}
26
27pin_project! {
28 pub struct JoinHandle<T> {
29 #[pin]
30 task: Task<T>,
31 }
32}
33
34impl<T> Future for JoinHandle<T> {
35 type Output = T;
36
37 fn poll(
38 self: std::pin::Pin<&mut Self>,
39 cx: &mut std::task::Context<'_>,
40 ) -> std::task::Poll<Self::Output> {
41 self.project().task.poll(cx)
42 }
43}
44
45impl Runtime {
46 pub fn new(background_threads: usize) -> Self {
50 let injector: Arc<Injector<Runnable>> = Arc::new(Injector::new());
51 for _ in 0..background_threads {
52 std::thread::spawn({
53 let injector = injector.clone();
54 move || {
55 loop {
56 if let Some(task) = injector.steal().success() {
57 task.run();
58 }
59 }
60 }
61 });
62 }
63 Self {
64 injector,
65 stealers: Arc::new(DashMap::new()),
66 }
67 }
68
69 pub fn handle(&self) -> Handle {
70 Handle {
71 injector: Arc::clone(&self.injector),
72 stealers: Arc::clone(&self.stealers),
73 }
74 }
75
76 pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
77 where
78 F: Future + Send + 'static,
79 F::Output: Send + Sync,
80 {
81 let injector = self.injector.clone();
82 let schedule = move |runnable| injector.push(runnable);
83 let (runnable, task) = async_task::spawn(future, schedule);
84 runnable.schedule();
85 JoinHandle { task }
86 }
87
88 pub fn block_on<F>(&self, future: F) -> F::Output
89 where
90 F: Future + Send + 'static,
91 F::Output: Send + Sync,
92 {
93 self.handle().block_on(future)
94 }
95}
96
97impl Handle {
98 pub fn block_on<F>(&self, future: F) -> F::Output
99 where
100 F: Future + Send + 'static,
101 F::Output: Send + Sync,
102 {
103 let schedule = {
104 let stealers = self.stealers.clone();
105 move |runnable| {
106 WORK_QUEUE.with(|q| {
107 stealers
109 .entry(thread::current().id())
110 .or_insert(q.stealer());
111 q.push(runnable);
112 })
113 }
114 };
115 let (runnable, task) = async_task::spawn(future, schedule);
116 runnable.schedule();
117
118 while !task.is_finished() {
119 if let Some(stolen_task) = self.find_task() {
120 stolen_task.run();
121 }
122 }
123
124 futures::executor::block_on(task)
125 }
126
127 pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
128 where
129 F: Future + Send + 'static,
130 F::Output: Send + Sync,
131 {
132 let injector = self.injector.clone();
133 let schedule = move |runnable| injector.push(runnable);
134 let (runnable, task) = async_task::spawn(future, schedule);
135 runnable.schedule();
136 JoinHandle { task }
137 }
138
139 fn find_task(&self) -> Option<Runnable> {
140 WORK_QUEUE.with(|local| {
141 local.pop().or_else(|| {
143 std::iter::repeat_with(|| {
144 self.injector
145 .steal_batch_with_limit_and_pop(local, 1)
146 .or_else(|| self.stealers.iter().map(|s| s.steal()).collect())
147 })
148 .find(|s| !s.is_retry())
149 .and_then(|s| s.success())
150 })
151 })
152 }
153}
154
155#[cfg(test)]
156mod tests {
157
158 use rstest::rstest;
159
160 use super::*;
161
162 #[rstest]
163 #[case(0)]
164 #[case(1)]
165 fn test_basic(#[case] thread_count: usize) {
166 let rt = Runtime::new(thread_count);
167 let jh: JoinHandle<()> = rt.spawn(async move { println!("spawned task!") });
168 let handle = rt.handle();
169 let h2 = handle.clone();
170
171 handle.block_on(async move {
172 _ = h2.spawn(async move {
173 if thread_count == 0 {
174 panic!("This should never run");
175 }
176 });
177 println!("blocking here!");
178 println!("Awaited the second future");
179 });
180 println!("Finished block_on block");
181 rt.block_on(jh);
182 }
183}