use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant};
#[cfg(feature = "internal")]
use cranpose_core::internal::FrameClock;
use cranpose_core::{Clock, Runtime, RuntimeHandle, RuntimeScheduler};
#[cfg(target_arch = "wasm32")]
struct SyncWaker<F>(F);
#[cfg(target_arch = "wasm32")]
unsafe impl<F> Sync for SyncWaker<F> {}
#[cfg(target_arch = "wasm32")]
impl<F: Fn()> SyncWaker<F> {
fn call(&self) {
(self.0)();
}
}
pub struct StdScheduler {
frame_requested: AtomicBool,
#[cfg(not(target_arch = "wasm32"))]
frame_waker: RwLock<Option<Arc<dyn Fn() + Send + Sync + 'static>>>,
#[cfg(target_arch = "wasm32")]
frame_waker: RwLock<Option<Arc<SyncWaker<Box<dyn Fn() + Send + 'static>>>>>,
}
impl StdScheduler {
pub fn new() -> Self {
Self {
frame_requested: AtomicBool::new(false),
frame_waker: RwLock::new(None),
}
}
pub fn take_frame_request(&self) -> bool {
self.frame_requested.swap(false, Ordering::SeqCst)
}
pub fn has_frame_request(&self) -> bool {
self.frame_requested.load(Ordering::SeqCst)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_frame_waker(&self, waker: impl Fn() + Send + Sync + 'static) {
*self.frame_waker.write().unwrap() = Some(Arc::new(waker));
}
#[cfg(target_arch = "wasm32")]
pub fn set_frame_waker(&self, waker: impl Fn() + Send + 'static) {
*self.frame_waker.write().unwrap() = Some(Arc::new(SyncWaker(Box::new(waker))));
}
pub fn clear_frame_waker(&self) {
*self.frame_waker.write().unwrap() = None;
}
#[cfg(not(target_arch = "wasm32"))]
fn wake(&self) {
let waker = self.frame_waker.read().unwrap().clone();
if let Some(waker) = waker {
waker();
}
}
#[cfg(target_arch = "wasm32")]
fn wake(&self) {
let waker = self.frame_waker.read().unwrap().clone();
if let Some(waker) = waker {
waker.call();
}
}
}
impl Default for StdScheduler {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for StdScheduler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("StdScheduler")
.field(
"frame_requested",
&self.frame_requested.load(Ordering::SeqCst),
)
.finish()
}
}
impl RuntimeScheduler for StdScheduler {
fn schedule_frame(&self) {
self.frame_requested.store(true, Ordering::SeqCst);
self.wake();
}
}
#[derive(Debug, Default, Clone)]
pub struct StdClock;
impl Clock for StdClock {
type Instant = Instant;
fn now(&self) -> Self::Instant {
Instant::now()
}
fn elapsed_millis(&self, since: Self::Instant) -> u64 {
since.elapsed().as_millis() as u64
}
}
impl StdClock {
pub fn elapsed(&self, since: Instant) -> Duration {
since.elapsed()
}
}
#[derive(Clone)]
pub struct StdRuntime {
scheduler: Arc<StdScheduler>,
clock: Arc<StdClock>,
runtime: Runtime,
}
impl StdRuntime {
pub fn new() -> Self {
let scheduler = Arc::new(StdScheduler::default());
let runtime = Runtime::new(scheduler.clone());
Self {
scheduler,
clock: Arc::new(StdClock),
runtime,
}
}
pub fn runtime(&self) -> Runtime {
self.runtime.clone()
}
pub fn runtime_handle(&self) -> RuntimeHandle {
self.runtime.handle()
}
#[cfg(feature = "internal")]
pub fn frame_clock(&self) -> FrameClock {
self.runtime.frame_clock()
}
pub fn scheduler(&self) -> Arc<StdScheduler> {
Arc::clone(&self.scheduler)
}
pub fn clock(&self) -> Arc<StdClock> {
Arc::clone(&self.clock)
}
pub fn take_frame_request(&self) -> bool {
self.scheduler.take_frame_request()
}
pub fn has_frame_request(&self) -> bool {
self.scheduler.has_frame_request()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_frame_waker(&self, waker: impl Fn() + Send + Sync + 'static) {
self.scheduler.set_frame_waker(waker);
}
#[cfg(target_arch = "wasm32")]
pub fn set_frame_waker(&self, waker: impl Fn() + Send + 'static) {
self.scheduler.set_frame_waker(waker);
}
pub fn clear_frame_waker(&self) {
self.scheduler.clear_frame_waker();
}
pub fn drain_frame_callbacks(&self, frame_time_nanos: u64) {
self.runtime_handle()
.drain_frame_callbacks(frame_time_nanos);
}
}
impl fmt::Debug for StdRuntime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("StdRuntime")
.field("scheduler", &self.scheduler)
.field("clock", &self.clock)
.finish()
}
}
impl Default for StdRuntime {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "tests/std_runtime_tests.rs"]
mod tests;