#![deny(unsafe_code)]
#[cfg(target_arch = "wasm32")]
use std::cell::RefCell;
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
#[cfg(not(target_arch = "wasm32"))]
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::time::{Duration, Instant};
#[cfg(feature = "internal")]
use cranpose_core::internal::FrameClock;
use cranpose_core::{Clock, Runtime, RuntimeHandle, RuntimeScheduler};
#[cfg(not(target_arch = "wasm32"))]
type NativeFrameWaker = Arc<dyn Fn() + Send + Sync + 'static>;
pub struct StdScheduler {
frame_requested: AtomicBool,
#[cfg(not(target_arch = "wasm32"))]
frame_waker: RwLock<Option<NativeFrameWaker>>,
#[cfg(target_arch = "wasm32")]
frame_waker: RefCell<Option<Box<dyn Fn() + 'static>>>,
}
impl StdScheduler {
pub fn new() -> Self {
Self {
frame_requested: AtomicBool::new(false),
frame_waker: Default::default(),
}
}
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) {
let old_waker = {
let mut frame_waker = self.frame_waker_write();
frame_waker.replace(Arc::new(waker))
};
drop(old_waker);
}
#[cfg(target_arch = "wasm32")]
pub fn set_frame_waker(&self, waker: impl Fn() + 'static) {
*self.frame_waker.borrow_mut() = Some(Box::new(waker));
}
#[cfg(not(target_arch = "wasm32"))]
pub fn clear_frame_waker(&self) {
let old_waker = {
let mut frame_waker = self.frame_waker_write();
frame_waker.take()
};
drop(old_waker);
}
#[cfg(target_arch = "wasm32")]
pub fn clear_frame_waker(&self) {
*self.frame_waker.borrow_mut() = None;
}
#[cfg(not(target_arch = "wasm32"))]
fn wake(&self) {
let waker = self.frame_waker_read().clone();
if let Some(waker) = waker {
waker();
}
}
#[cfg(target_arch = "wasm32")]
fn wake(&self) {
if let Some(waker) = self.frame_waker.borrow().as_ref() {
waker();
}
}
#[cfg(not(target_arch = "wasm32"))]
fn frame_waker_read(&self) -> RwLockReadGuard<'_, Option<NativeFrameWaker>> {
match self.frame_waker.read() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
}
}
#[cfg(not(target_arch = "wasm32"))]
fn frame_waker_write(&self) -> RwLockWriteGuard<'_, Option<NativeFrameWaker>> {
match self.frame_waker.write() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
}
}
}
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() + '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;