cranpose_runtime_std/
lib.rs1#![doc = include_str!("../README.md")]
2
3#[cfg(target_arch = "wasm32")]
4use std::cell::RefCell;
5#[cfg(not(target_arch = "wasm32"))]
6use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
7use std::{
8 fmt,
9 sync::{
10 Arc,
11 atomic::{AtomicBool, Ordering},
12 },
13 time::Duration,
14};
15
16#[cfg(feature = "internal")]
17use cranpose_core::internal::FrameClock;
18use cranpose_core::{Clock, Runtime, RuntimeHandle, RuntimeScheduler};
19use web_time::Instant;
20
21#[cfg(not(target_arch = "wasm32"))]
22type NativeFrameWaker = Arc<dyn Fn() + Send + Sync + 'static>;
23
24pub struct StdScheduler {
26 frame_requested: AtomicBool,
27 #[cfg(not(target_arch = "wasm32"))]
28 frame_waker: RwLock<Option<NativeFrameWaker>>,
29 #[cfg(target_arch = "wasm32")]
30 frame_waker: RefCell<Option<Box<dyn Fn() + 'static>>>,
31}
32
33impl StdScheduler {
34 pub fn new() -> Self {
35 Self {
36 frame_requested: AtomicBool::new(false),
37 frame_waker: Default::default(),
38 }
39 }
40
41 pub fn take_frame_request(&self) -> bool {
43 self.frame_requested.swap(false, Ordering::SeqCst)
44 }
45
46 pub fn has_frame_request(&self) -> bool {
48 self.frame_requested.load(Ordering::SeqCst)
49 }
50
51 #[cfg(not(target_arch = "wasm32"))]
53 pub fn set_frame_waker(&self, waker: impl Fn() + Send + Sync + 'static) {
54 let old_waker = {
55 let mut frame_waker = self.frame_waker_write();
56 frame_waker.replace(Arc::new(waker))
57 };
58 drop(old_waker);
59 }
60
61 #[cfg(target_arch = "wasm32")]
62 pub fn set_frame_waker(&self, waker: impl Fn() + 'static) {
63 *self.frame_waker.borrow_mut() = Some(Box::new(waker));
64 }
65
66 #[cfg(not(target_arch = "wasm32"))]
68 pub fn clear_frame_waker(&self) {
69 let old_waker = {
70 let mut frame_waker = self.frame_waker_write();
71 frame_waker.take()
72 };
73 drop(old_waker);
74 }
75
76 #[cfg(target_arch = "wasm32")]
78 pub fn clear_frame_waker(&self) {
79 *self.frame_waker.borrow_mut() = None;
80 }
81
82 #[cfg(not(target_arch = "wasm32"))]
83 fn wake(&self) {
84 let waker = self.frame_waker_read().clone();
85 if let Some(waker) = waker {
86 waker();
87 }
88 }
89
90 #[cfg(target_arch = "wasm32")]
91 fn wake(&self) {
92 if let Some(waker) = self.frame_waker.borrow().as_ref() {
93 waker();
94 }
95 }
96
97 #[cfg(not(target_arch = "wasm32"))]
98 fn frame_waker_read(&self) -> RwLockReadGuard<'_, Option<NativeFrameWaker>> {
99 match self.frame_waker.read() {
100 Ok(guard) => guard,
101 Err(poisoned) => poisoned.into_inner(),
102 }
103 }
104
105 #[cfg(not(target_arch = "wasm32"))]
106 fn frame_waker_write(&self) -> RwLockWriteGuard<'_, Option<NativeFrameWaker>> {
107 match self.frame_waker.write() {
108 Ok(guard) => guard,
109 Err(poisoned) => poisoned.into_inner(),
110 }
111 }
112}
113
114impl Default for StdScheduler {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120impl fmt::Debug for StdScheduler {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.debug_struct("StdScheduler")
123 .field(
124 "frame_requested",
125 &self.frame_requested.load(Ordering::SeqCst),
126 )
127 .finish()
128 }
129}
130
131impl RuntimeScheduler for StdScheduler {
132 fn schedule_frame(&self) {
133 self.frame_requested.store(true, Ordering::SeqCst);
134 self.wake();
135 }
136}
137
138#[cfg(not(target_arch = "wasm32"))]
147pub type StdSchedulerRef = Arc<StdScheduler>;
148
149#[cfg(target_arch = "wasm32")]
151pub type StdSchedulerRef = std::rc::Rc<StdScheduler>;
152
153#[derive(Debug, Default, Clone)]
155pub struct StdClock;
156
157impl Clock for StdClock {
158 type Instant = Instant;
159
160 fn now(&self) -> Self::Instant {
161 Instant::now()
162 }
163
164 fn elapsed_millis(&self, since: Self::Instant) -> u64 {
165 since.elapsed().as_millis() as u64
166 }
167}
168
169impl StdClock {
170 pub fn elapsed(&self, since: Instant) -> Duration {
172 since.elapsed()
173 }
174}
175
176#[derive(Clone)]
178pub struct StdRuntime {
179 scheduler: StdSchedulerRef,
180 clock: Arc<StdClock>,
181 runtime: Runtime,
182}
183
184impl StdRuntime {
185 pub fn new() -> Self {
187 let scheduler = StdSchedulerRef::new(StdScheduler::default());
188 let runtime = Runtime::new(scheduler.clone());
189 Self {
190 scheduler,
191 clock: Arc::new(StdClock),
192 runtime,
193 }
194 }
195
196 pub fn runtime(&self) -> Runtime {
198 self.runtime.clone()
199 }
200
201 pub fn runtime_handle(&self) -> RuntimeHandle {
203 self.runtime.handle()
204 }
205
206 #[cfg(feature = "internal")]
208 pub fn frame_clock(&self) -> FrameClock {
209 self.runtime.frame_clock()
210 }
211
212 pub fn scheduler(&self) -> StdSchedulerRef {
214 StdSchedulerRef::clone(&self.scheduler)
215 }
216
217 pub fn clock(&self) -> Arc<StdClock> {
219 Arc::clone(&self.clock)
220 }
221
222 pub fn take_frame_request(&self) -> bool {
224 self.scheduler.take_frame_request()
225 }
226
227 pub fn has_frame_request(&self) -> bool {
228 self.scheduler.has_frame_request()
229 }
230
231 #[cfg(not(target_arch = "wasm32"))]
233 pub fn set_frame_waker(&self, waker: impl Fn() + Send + Sync + 'static) {
234 self.scheduler.set_frame_waker(waker);
235 }
236
237 #[cfg(target_arch = "wasm32")]
238 pub fn set_frame_waker(&self, waker: impl Fn() + 'static) {
239 self.scheduler.set_frame_waker(waker);
240 }
241
242 pub fn clear_frame_waker(&self) {
244 self.scheduler.clear_frame_waker();
245 }
246
247 pub fn drain_frame_callbacks(&self, frame_time_nanos: u64) {
249 self.runtime_handle()
250 .drain_frame_callbacks(frame_time_nanos);
251 }
252}
253
254impl fmt::Debug for StdRuntime {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 f.debug_struct("StdRuntime")
257 .field("scheduler", &self.scheduler)
258 .field("clock", &self.clock)
259 .finish()
260 }
261}
262
263impl Default for StdRuntime {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269#[cfg(test)]
270#[path = "tests/std_runtime_tests.rs"]
271mod tests;