1use std::{
14 sync::Arc,
15 thread::{self, ThreadId},
16};
17
18use async_task::Runnable;
19use crossbeam::deque::{Injector, Stealer, Worker};
20use dashmap::DashMap;
21
22pub mod futures;
23mod join_handle;
24
25pub use join_handle::*;
26
27thread_local! {
28 static WORK_QUEUE: Worker<Runnable> = Worker::new_fifo();
29}
30
31pub struct Runtime {
33 injector: Arc<Injector<Runnable>>,
34 stealers: Arc<DashMap<ThreadId, Stealer<Runnable>>>,
35}
36
37#[derive(Clone)]
41pub struct Handle {
42 injector: Arc<Injector<Runnable>>,
43 stealers: Arc<DashMap<ThreadId, Stealer<Runnable>>>,
44}
45
46impl Runtime {
47 pub fn new(background_threads: usize) -> Self {
51 let injector: Arc<Injector<Runnable>> = Arc::new(Injector::new());
52 for idx in 0..background_threads {
53 thread::Builder::new()
54 .name(format!("cuckoo-background-thread-{idx}"))
55 .spawn({
56 let injector = Arc::clone(&injector);
57 move || {
58 loop {
59 if let Some(task) = injector.steal().success() {
60 task.run();
61 }
62 }
63 }
64 })
65 .expect("Failed to spawn background thread");
66 }
67 Self {
68 injector,
69 stealers: Arc::new(DashMap::new()),
70 }
71 }
72
73 pub fn handle(&self) -> Handle {
74 Handle {
75 injector: Arc::clone(&self.injector),
76 stealers: Arc::clone(&self.stealers),
77 }
78 }
79
80 pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
84 where
85 F: Future + Send + 'static,
86 F::Output: Send + Sync,
87 {
88 let injector = Arc::clone(&self.injector);
89 let schedule = move |runnable| injector.push(runnable);
90 let (runnable, task) = async_task::spawn(future, schedule);
91 runnable.schedule();
92 JoinHandle { task: Some(task) }
93 }
94
95 pub fn block_on<F>(&self, future: F) -> F::Output
99 where
100 F: Future + Send + 'static,
101 F::Output: Send + Sync,
102 {
103 self.handle().block_on(future)
104 }
105}
106
107impl Handle {
108 pub fn block_on<F>(&self, future: F) -> F::Output
112 where
113 F: Future + Send + 'static,
114 F::Output: Send + Sync,
115 {
116 let schedule = {
117 let stealers = Arc::clone(&self.stealers);
118 move |runnable| {
119 WORK_QUEUE.with(|q| {
120 let current_thread_id = thread::current().id();
121 if !stealers.contains_key(¤t_thread_id) {
122 stealers.entry(current_thread_id).or_insert(q.stealer());
124 }
125
126 q.push(runnable);
127 })
128 }
129 };
130 let (runnable, task) = async_task::spawn(future, schedule);
131 runnable.run();
133
134 while !task.is_finished() {
135 if let Some(stolen_task) = self.find_task() {
136 stolen_task.run();
137 }
138 }
139
140 ::futures::executor::block_on(task)
141 }
142
143 pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
147 where
148 F: Future + Send + 'static,
149 F::Output: Send + Sync,
150 {
151 let injector = Arc::clone(&self.injector);
152 let schedule = move |runnable| injector.push(runnable);
153 let (runnable, task) = async_task::spawn(future, schedule);
154 runnable.schedule();
155 JoinHandle { task: Some(task) }
156 }
157
158 fn find_task(&self) -> Option<Runnable> {
159 WORK_QUEUE.with(|local| {
160 local.pop().or_else(|| {
162 std::iter::repeat_with(|| {
163 self.injector
164 .steal_batch_with_limit_and_pop(local, 1)
165 .or_else(|| self.stealers.iter().map(|s| s.steal()).collect())
166 })
167 .find(|s| !s.is_retry())
168 .and_then(|s| s.success())
169 })
170 })
171 }
172}
173
174#[cfg(test)]
175mod tests {
176
177 use rstest::rstest;
178
179 use super::*;
180
181 #[rstest]
182 #[case(0)]
183 #[case(1)]
184 fn test_basic(#[case] thread_count: usize) {
185 let rt = Runtime::new(thread_count);
186 let jh: JoinHandle<()> = rt.spawn(async move { println!("spawned task!") });
187 let handle = rt.handle();
188 let h2 = handle.clone();
189
190 handle.block_on(async move {
191 _ = h2.spawn(async move {
192 if thread_count == 0 {
193 panic!("This should never run");
194 }
195 });
196 println!("blocking here!");
197 println!("Awaited the second future");
198 });
199 println!("Finished block_on block");
200 rt.block_on(jh);
201 }
202}