From 54e908530db0fbd7f1b4aec5bd50b19d5632ddcf Mon Sep 17 00:00:00 2001 From: TudbuT Date: Sun, 14 Jul 2024 01:56:36 +0000 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 16 ++++++++++++ Cargo.toml | 7 +++++ examples/basic.rs | 13 ++++++++++ src/lib.rs | 27 +++++++++++++++++++ src/rt/mod.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++ src/rt/waker.rs | 37 ++++++++++++++++++++++++++ 7 files changed, 167 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 examples/basic.rs create mode 100644 src/lib.rs create mode 100644 src/rt/mod.rs create mode 100644 src/rt/waker.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..71cbd31 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "microasync" +version = "0.1.0" +dependencies = [ + "microlock", +] + +[[package]] +name = "microlock" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "849d9daa132b37b3082c2ef186343573aedf6ebc7e1b57343e9278e81a2b5b20" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..48b8f75 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "microasync" +version = "0.1.0" +edition = "2021" + +[dependencies] +microlock = "0.2.0" diff --git a/examples/basic.rs b/examples/basic.rs new file mode 100644 index 0000000..6da307f --- /dev/null +++ b/examples/basic.rs @@ -0,0 +1,13 @@ +use std::time::Duration; + +use microasync::rt::{MiniAsync, TimerDuration}; + +fn main() { + let rt = MiniAsync::new(TimerDuration::Real(Duration::from_millis(20))); + rt.add_task(async_main()); + rt.run_until_empty(); +} + +async fn async_main() { + println!("Hello from async!") +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f9bfa08 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,27 @@ +pub mod rt; + +use std::{cell::RefCell, ptr}; + +use rt::MiniAsync; +thread_local! { + static RUNTIME: RefCell<*const MiniAsync> = const { RefCell::new(ptr::null()) }; +} + +pub(crate) fn current_runtime(rt: &MiniAsync) { + RUNTIME.with_borrow_mut(|x| *x = rt as *const _); +} + +pub(crate) fn exiting_runtime() { + RUNTIME.with_borrow_mut(|x| *x = ptr::null()); +} + +/// # Safety +/// The lifetime given out by this function is static but the underlying +/// struct is not, leading to use after free if this is not paid attention to. +pub unsafe fn get_runtime() -> &'static MiniAsync { + unsafe { &*RUNTIME.with_borrow(|x| *x) } +} + +pub fn runtime(f: impl FnOnce(&MiniAsync)) { + f(unsafe { get_runtime() }) +} diff --git a/src/rt/mod.rs b/src/rt/mod.rs new file mode 100644 index 0000000..504b074 --- /dev/null +++ b/src/rt/mod.rs @@ -0,0 +1,66 @@ +mod waker; + +use std::{ + future::Future, + pin::Pin, + sync::Mutex, + task::{Context, Poll}, + vec::Vec, +}; + +pub use microlock::timer::TimerDuration; +use microlock::Lock; + +use crate::{current_runtime, exiting_runtime}; + +use self::waker::MicroWaker; + +pub type Task = Pin>>; + +pub struct MiniAsync { + tasks: Vec>, + tasks_to_add: Mutex>>, + waker: MicroWaker, + wake_attempt_delay: TimerDuration, +} + +impl MiniAsync { + pub fn new(wake_attempt_delay: TimerDuration) -> Self { + Self { + tasks: Vec::new(), + tasks_to_add: Mutex::new(Vec::new()), + waker: MicroWaker::new(), + wake_attempt_delay, + } + } + + pub fn add_boxed_task(&self, future: Pin + 'static>>) { + self.tasks_to_add.lock().unwrap().push(future); + self.waker.lock.unlock(); + } + + pub fn add_task(&self, future: impl Future + 'static) { + self.tasks_to_add.lock().unwrap().push(Box::pin(future)); + self.waker.lock.unlock(); + } + + pub fn run_until_empty(mut self) { + current_runtime(&self); + let waker = self.waker.rust_waker(); + let mut cx = Context::from_waker(&waker); + while !self.tasks.is_empty() { + let mut next_tasks = Vec::with_capacity(self.tasks.len()); + for mut task in self.tasks { + match Future::poll(task.as_mut(), &mut cx) { + Poll::Ready(_) => drop(task), + Poll::Pending => next_tasks.push(task), + }; + } + next_tasks.append(&mut self.tasks_to_add.lock().unwrap()); + self.tasks = next_tasks; + self.waker.lock.wait_here_for(self.wake_attempt_delay); + self.waker.lock.try_lock(); + } + exiting_runtime(); + } +} diff --git a/src/rt/waker.rs b/src/rt/waker.rs new file mode 100644 index 0000000..ddd17ff --- /dev/null +++ b/src/rt/waker.rs @@ -0,0 +1,37 @@ +use std::task::{RawWaker, RawWakerVTable, Waker}; + +use microlock::{Lock, TimedLock}; + +pub struct MicroWaker { + pub(crate) lock: TimedLock, +} +impl MicroWaker { + pub fn new() -> MicroWaker { + Self { + lock: TimedLock::locked(), + } + } + + pub fn rust_waker(&self) -> Waker { + unsafe { Waker::from_raw(Self::clone(self as *const _ as _)) } + } + + fn clone(me: *const ()) -> RawWaker { + RawWaker::new( + me, + &RawWakerVTable::new(Self::clone, Self::wake, Self::wake_by_ref, Self::drop), + ) + } + + unsafe fn wake(me: *const ()) { + let me = &*(me as *const Self); + me.lock.unlock(); + } + + unsafe fn wake_by_ref(me: *const ()) { + let me = &*(me as *const Self); + me.lock.unlock(); + } + + unsafe fn drop(_me: *const ()) {} +}