diff --git a/Cargo.lock b/Cargo.lock index bbd9d565..1fceef16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -639,6 +639,15 @@ dependencies = [ "libloading", ] +[[package]] +name = "jay-async-engine" +version = "0.1.0" +dependencies = [ + "jay-time", + "jay-tracy", + "jay-utils", +] + [[package]] name = "jay-cmm" version = "0.1.0" @@ -672,6 +681,7 @@ dependencies = [ "isnt 0.2.0", "jay-algorithms", "jay-ash", + "jay-async-engine", "jay-cmm", "jay-config", "jay-criteria", diff --git a/Cargo.toml b/Cargo.toml index 71b7640f..310e76df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ members = [ "cmm", "time", "tracy", + "async-engine", "toml-config", "algorithms", "toml-spec", @@ -55,6 +56,7 @@ jay-criteria = { version = "0.1.0", path = "criteria" } jay-cmm = { version = "0.1.0", path = "cmm" } jay-time = { version = "0.1.0", path = "time" } jay-tracy = { version = "0.1.0", path = "tracy" } +jay-async-engine = { version = "0.1.0", path = "async-engine" } uapi = "0.2.13" thiserror = "2.0.11" @@ -117,5 +119,5 @@ opt-level = 3 [features] rc_tracking = [] -it = [] -tracy = ["jay-tracy/tracy"] +it = ["jay-async-engine/it"] +tracy = ["jay-tracy/tracy", "jay-async-engine/tracy"] diff --git a/async-engine/Cargo.toml b/async-engine/Cargo.toml new file mode 100644 index 00000000..7d7ca019 --- /dev/null +++ b/async-engine/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "jay-async-engine" +version = "0.1.0" +edition = "2024" +license = "GPL-3.0-only" + +[dependencies] +jay-time = { version = "0.1.0", path = "../time" } +jay-tracy = { version = "0.1.0", path = "../tracy" } +jay-utils = { version = "0.1.0", path = "../utils" } + +[features] +it = [] +tracy = ["jay-tracy/tracy"] diff --git a/src/async_engine/ae_task.rs b/async-engine/src/ae_task.rs similarity index 96% rename from src/async_engine/ae_task.rs rename to async-engine/src/ae_task.rs index e980c89c..50647187 100644 --- a/src/async_engine/ae_task.rs +++ b/async-engine/src/ae_task.rs @@ -1,11 +1,9 @@ use { - crate::{ - async_engine::{AsyncEngine, Phase}, - tracy::ZoneName, - utils::{ - numcell::NumCell, - ptr_ext::{MutPtrExt, PtrExt}, - }, + crate::{AsyncEngine, Phase}, + jay_tracy::ZoneName, + jay_utils::{ + numcell::NumCell, + ptr_ext::{MutPtrExt, PtrExt}, }, std::{ cell::{Cell, UnsafeCell}, @@ -142,7 +140,7 @@ impl AsyncEngine { }), waker: Cell::new(None), queue: self.clone(), - zone: create_zone_name!("task:{}", name), + zone: jay_tracy::create_zone_name!("task:{}", name), }); unsafe { f.schedule_run(); @@ -254,7 +252,7 @@ impl> Task { let mut ctx = Context::from_waker(&waker); let poll = { - dynamic_zone!(self.zone); + jay_tracy::dynamic_zone!(self.zone); Pin::new_unchecked(&mut *data.future).poll(&mut ctx) }; if let Poll::Ready(d) = poll { diff --git a/src/async_engine/ae_yield.rs b/async-engine/src/ae_yield.rs similarity index 93% rename from src/async_engine/ae_yield.rs rename to async-engine/src/ae_yield.rs index 7ada5c81..41c561c5 100644 --- a/src/async_engine/ae_yield.rs +++ b/async-engine/src/ae_yield.rs @@ -1,5 +1,5 @@ use { - crate::async_engine::AsyncEngine, + crate::AsyncEngine, std::{ future::Future, pin::Pin, diff --git a/async-engine/src/lib.rs b/async-engine/src/lib.rs new file mode 100644 index 00000000..a67963b0 --- /dev/null +++ b/async-engine/src/lib.rs @@ -0,0 +1,168 @@ +mod ae_task; +mod ae_yield; + +pub use {ae_task::SpawnedFuture, ae_yield::Yield}; +use { + crate::ae_task::Runnable, + jay_time::Time, + jay_utils::{array, numcell::NumCell, syncqueue::SyncQueue}, + std::{ + cell::{Cell, RefCell}, + collections::VecDeque, + future::Future, + rc::Rc, + task::Waker, + }, +}; + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum Phase { + EventHandling, + Layout, + PostLayout, + Present, +} +const NUM_PHASES: usize = 4; + +pub struct AsyncEngine { + num_queued: NumCell, + queues: [SyncQueue; NUM_PHASES], + iteration: NumCell, + yields: SyncQueue, + stash: RefCell>, + yield_stash: RefCell>, + stopped: Cell, + now: Cell>, + #[cfg(feature = "it")] + idle: Cell>, +} + +impl AsyncEngine { + pub fn new() -> Rc { + Rc::new(Self { + num_queued: Default::default(), + queues: array::from_fn(|_| Default::default()), + iteration: Default::default(), + yields: Default::default(), + stash: Default::default(), + yield_stash: Default::default(), + stopped: Cell::new(false), + now: Default::default(), + #[cfg(feature = "it")] + idle: Default::default(), + }) + } + + pub fn stop(&self) { + self.stopped.set(true); + } + + pub fn clear(&self) { + self.stash.borrow_mut().clear(); + self.yield_stash.borrow_mut().clear(); + self.yields.take(); + for queue in &self.queues { + queue.take(); + } + } + + pub fn spawn + 'static>( + self: &Rc, + name: &str, + f: F, + ) -> SpawnedFuture { + self.spawn_(name, Phase::EventHandling, f) + } + + pub fn spawn2 + 'static>( + self: &Rc, + name: &str, + phase: Phase, + f: F, + ) -> SpawnedFuture { + self.spawn_(name, phase, f) + } + + pub fn yield_now(self: &Rc) -> Yield { + Yield { + iteration: self.iteration(), + queue: self.clone(), + } + } + + pub fn dispatch(&self) { + let mut stash = self.stash.borrow_mut(); + let mut yield_stash = self.yield_stash.borrow_mut(); + loop { + if self.num_queued.get() == 0 { + #[cfg(feature = "it")] + if let Some(idle) = self.idle.take() { + idle.wake(); + continue; + } + break; + } + self.now.take(); + let mut phase = 0; + while phase < NUM_PHASES { + self.queues[phase].swap(&mut *stash); + if stash.is_empty() { + phase += 1; + continue; + } + self.num_queued.fetch_sub(stash.len()); + while let Some(runnable) = stash.pop_front() { + runnable.run(); + if self.stopped.get() { + return; + } + } + } + self.iteration.fetch_add(1); + self.yields.swap(&mut *yield_stash); + while let Some(waker) = yield_stash.pop_front() { + waker.wake(); + } + } + } + + #[cfg(feature = "it")] + pub async fn idle(&self) { + use std::{future::poll_fn, task::Poll}; + let mut register = true; + poll_fn(|ctx| { + if register { + self.idle.set(Some(ctx.waker().clone())); + register = false; + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await + } + + fn push(&self, runnable: Runnable, phase: Phase) { + self.queues[phase as usize].push(runnable); + self.num_queued.fetch_add(1); + } + + fn push_yield(&self, waker: Waker) { + self.yields.push(waker); + } + + pub fn iteration(&self) -> u64 { + self.iteration.get() + } + + pub fn now(&self) -> Time { + match self.now.get() { + Some(t) => t, + None => { + let now = Time::now_unchecked(); + self.now.set(Some(now)); + now + } + } + } +} diff --git a/src/async_engine.rs b/src/async_engine.rs index 80e38c52..740b31b3 100644 --- a/src/async_engine.rs +++ b/src/async_engine.rs @@ -1,170 +1 @@ -mod ae_task; -mod ae_yield; - -pub use {crate::async_engine::ae_yield::Yield, ae_task::SpawnedFuture}; -use { - crate::{ - async_engine::ae_task::Runnable, - time::Time, - utils::{array, numcell::NumCell, syncqueue::SyncQueue}, - }, - std::{ - cell::{Cell, RefCell}, - collections::VecDeque, - future::Future, - rc::Rc, - task::Waker, - }, -}; - -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum Phase { - EventHandling, - Layout, - PostLayout, - Present, -} -const NUM_PHASES: usize = 4; - -pub struct AsyncEngine { - num_queued: NumCell, - queues: [SyncQueue; NUM_PHASES], - iteration: NumCell, - yields: SyncQueue, - stash: RefCell>, - yield_stash: RefCell>, - stopped: Cell, - now: Cell>, - #[cfg(feature = "it")] - idle: Cell>, -} - -impl AsyncEngine { - pub fn new() -> Rc { - Rc::new(Self { - num_queued: Default::default(), - queues: array::from_fn(|_| Default::default()), - iteration: Default::default(), - yields: Default::default(), - stash: Default::default(), - yield_stash: Default::default(), - stopped: Cell::new(false), - now: Default::default(), - #[cfg(feature = "it")] - idle: Default::default(), - }) - } - - pub fn stop(&self) { - self.stopped.set(true); - } - - pub fn clear(&self) { - self.stash.borrow_mut().clear(); - self.yield_stash.borrow_mut().clear(); - self.yields.take(); - for queue in &self.queues { - queue.take(); - } - } - - pub fn spawn + 'static>( - self: &Rc, - name: &str, - f: F, - ) -> SpawnedFuture { - self.spawn_(name, Phase::EventHandling, f) - } - - pub fn spawn2 + 'static>( - self: &Rc, - name: &str, - phase: Phase, - f: F, - ) -> SpawnedFuture { - self.spawn_(name, phase, f) - } - - pub fn yield_now(self: &Rc) -> Yield { - Yield { - iteration: self.iteration(), - queue: self.clone(), - } - } - - pub fn dispatch(&self) { - let mut stash = self.stash.borrow_mut(); - let mut yield_stash = self.yield_stash.borrow_mut(); - loop { - if self.num_queued.get() == 0 { - #[cfg(feature = "it")] - if let Some(idle) = self.idle.take() { - idle.wake(); - continue; - } - break; - } - self.now.take(); - let mut phase = 0; - while phase < NUM_PHASES { - self.queues[phase].swap(&mut *stash); - if stash.is_empty() { - phase += 1; - continue; - } - self.num_queued.fetch_sub(stash.len()); - while let Some(runnable) = stash.pop_front() { - runnable.run(); - if self.stopped.get() { - return; - } - } - } - self.iteration.fetch_add(1); - self.yields.swap(&mut *yield_stash); - while let Some(waker) = yield_stash.pop_front() { - waker.wake(); - } - } - } - - #[cfg(feature = "it")] - pub async fn idle(&self) { - use std::{future::poll_fn, task::Poll}; - let mut register = true; - poll_fn(|ctx| { - if register { - self.idle.set(Some(ctx.waker().clone())); - register = false; - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await - } - - fn push(&self, runnable: Runnable, phase: Phase) { - self.queues[phase as usize].push(runnable); - self.num_queued.fetch_add(1); - } - - fn push_yield(&self, waker: Waker) { - self.yields.push(waker); - } - - pub fn iteration(&self) -> u64 { - self.iteration.get() - } - - pub fn now(&self) -> Time { - match self.now.get() { - Some(t) => t, - None => { - let now = Time::now_unchecked(); - self.now.set(Some(now)); - now - } - } - } -} +pub use jay_async_engine::*;