Initial commit
This commit is contained in:
commit
54e908530d
7 changed files with 167 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
16
Cargo.lock
generated
Normal file
16
Cargo.lock
generated
Normal file
|
@ -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"
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "microasync"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
microlock = "0.2.0"
|
13
examples/basic.rs
Normal file
13
examples/basic.rs
Normal file
|
@ -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!")
|
||||
}
|
27
src/lib.rs
Normal file
27
src/lib.rs
Normal file
|
@ -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() })
|
||||
}
|
66
src/rt/mod.rs
Normal file
66
src/rt/mod.rs
Normal file
|
@ -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<T> = Pin<Box<dyn Future<Output = T>>>;
|
||||
|
||||
pub struct MiniAsync {
|
||||
tasks: Vec<Task<()>>,
|
||||
tasks_to_add: Mutex<Vec<Task<()>>>,
|
||||
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<Box<impl Future<Output = ()> + 'static>>) {
|
||||
self.tasks_to_add.lock().unwrap().push(future);
|
||||
self.waker.lock.unlock();
|
||||
}
|
||||
|
||||
pub fn add_task(&self, future: impl Future<Output = ()> + '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();
|
||||
}
|
||||
}
|
37
src/rt/waker.rs
Normal file
37
src/rt/waker.rs
Normal file
|
@ -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 ()) {}
|
||||
}
|
Loading…
Add table
Reference in a new issue