1use std::future::Future;
15use std::pin::Pin;
16use std::sync::Arc;
17use std::time::Duration;
18
19use bevy_ecs::prelude::Resource;
20
21pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
24
25pub trait Spawn: Send + Sync + 'static {
33 fn spawn(&self, fut: BoxFuture<()>);
40
41 fn spawn_blocking(&self, task: Box<dyn FnOnce() + Send + 'static>);
47}
48
49pub trait Timer: Send + Sync + 'static {
55 fn sleep(&self, duration: Duration) -> BoxFuture<()>;
60}
61
62#[derive(Resource, Clone)]
69pub struct SpawnService(Arc<dyn Spawn>);
70
71impl SpawnService {
72 pub fn new<S: Spawn>(spawn: S) -> Self {
74 Self(Arc::new(spawn))
75 }
76
77 pub fn as_spawn(&self) -> &dyn Spawn {
79 self.0.as_ref()
80 }
81
82 pub fn handle(&self) -> Arc<dyn Spawn> {
85 Arc::clone(&self.0)
86 }
87}
88
89impl<S: Spawn> From<S> for SpawnService {
90 fn from(spawn: S) -> Self {
91 Self::new(spawn)
92 }
93}
94
95impl std::ops::Deref for SpawnService {
96 type Target = dyn Spawn;
97
98 fn deref(&self) -> &Self::Target {
99 self.0.as_ref()
100 }
101}
102
103#[derive(Resource, Clone)]
106pub struct TimerService(Arc<dyn Timer>);
107
108impl TimerService {
109 pub fn new<T: Timer>(timer: T) -> Self {
111 Self(Arc::new(timer))
112 }
113
114 pub fn as_timer(&self) -> &dyn Timer {
116 self.0.as_ref()
117 }
118
119 pub fn handle(&self) -> Arc<dyn Timer> {
121 Arc::clone(&self.0)
122 }
123}
124
125impl<T: Timer> From<T> for TimerService {
126 fn from(timer: T) -> Self {
127 Self::new(timer)
128 }
129}
130
131impl std::ops::Deref for TimerService {
132 type Target = dyn Timer;
133
134 fn deref(&self) -> &Self::Target {
135 self.0.as_ref()
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use std::sync::atomic::{AtomicUsize, Ordering};
143
144 #[derive(Default)]
147 struct ThreadSpawn {
148 spawned: Arc<AtomicUsize>,
149 }
150
151 impl Spawn for ThreadSpawn {
152 fn spawn(&self, fut: BoxFuture<()>) {
153 self.spawned.fetch_add(1, Ordering::SeqCst);
154 let handle = std::thread::spawn(move || block_on_unit(fut));
155 handle.join().expect("task thread joins");
156 }
157
158 fn spawn_blocking(&self, task: Box<dyn FnOnce() + Send + 'static>) {
159 self.spawned.fetch_add(1, Ordering::SeqCst);
160 std::thread::spawn(task).join().expect("task thread joins");
161 }
162 }
163
164 struct InstantTimer;
165
166 impl Timer for InstantTimer {
167 fn sleep(&self, _duration: Duration) -> BoxFuture<()> {
168 Box::pin(std::future::ready(()))
169 }
170 }
171
172 fn block_on_unit(mut fut: BoxFuture<()>) {
176 let waker = std::task::Waker::noop();
177 let mut cx = std::task::Context::from_waker(waker);
178 loop {
179 if fut.as_mut().poll(&mut cx).is_ready() {
180 return;
181 }
182 std::thread::yield_now();
183 }
184 }
185
186 #[test]
187 fn spawn_service_dispatches_to_the_installed_executor() {
188 let counter = Arc::new(AtomicUsize::new(0));
189 let service = SpawnService::new(ThreadSpawn {
190 spawned: Arc::clone(&counter),
191 });
192 let ran = Arc::new(AtomicUsize::new(0));
193 let ran_in_task = Arc::clone(&ran);
194 service.spawn(Box::pin(async move {
195 ran_in_task.fetch_add(1, Ordering::SeqCst);
196 }));
197 assert_eq!(counter.load(Ordering::SeqCst), 1);
198 assert_eq!(ran.load(Ordering::SeqCst), 1);
199 }
200
201 #[test]
202 fn spawn_service_dispatches_blocking_work() {
203 let service = SpawnService::from(ThreadSpawn::default());
204 let ran = Arc::new(AtomicUsize::new(0));
205 let ran_in_task = Arc::clone(&ran);
206 service.spawn_blocking(Box::new(move || {
207 ran_in_task.fetch_add(1, Ordering::SeqCst);
208 }));
209 assert_eq!(ran.load(Ordering::SeqCst), 1);
210 }
211
212 #[test]
213 fn timer_service_returns_a_future_from_the_installed_timer() {
214 let service = TimerService::from(InstantTimer);
215 block_on_unit(service.sleep(Duration::from_secs(3600)));
216 }
217
218 #[test]
219 fn services_are_cloneable_handles_on_one_backend() {
220 let counter = Arc::new(AtomicUsize::new(0));
221 let service = SpawnService::new(ThreadSpawn {
222 spawned: Arc::clone(&counter),
223 });
224 let clone = service.clone();
225 service.spawn(Box::pin(async {}));
226 clone.spawn(Box::pin(async {}));
227 clone.handle().spawn(Box::pin(async {}));
228 assert_eq!(counter.load(Ordering::SeqCst), 3);
229 }
230}