initial commit
This commit is contained in:
parent
0c557ad800
commit
8eec17b1c2
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "messing_with_async"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3"
|
|
@ -1,3 +1,5 @@
|
|||
# messing_with_async
|
||||
|
||||
don't use this.
|
||||
don't use this.
|
||||
|
||||
seriously please don't use this.
|
417
src/futures.rs
Normal file
417
src/futures.rs
Normal file
|
@ -0,0 +1,417 @@
|
|||
//! Various futures, such as [`TimerFuture`], [`AllFuture`], and [`AnyFuture`].
|
||||
//!
|
||||
//! Also includes macros such as [`any`] and [`all`] to better make use of these futures.
|
||||
|
||||
use std::{
|
||||
future::Future,
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
time::Duration,
|
||||
task::{Waker, Context, Poll},
|
||||
pin::Pin
|
||||
};
|
||||
|
||||
|
||||
|
||||
// TODO: Check consistent wording of doc comments
|
||||
|
||||
/// A future that waits for a specified amount of time.
|
||||
///
|
||||
/// ```
|
||||
/// # use messing_with_async::{async_main, futures::TimerFuture};
|
||||
/// # use std::time::Duration;
|
||||
/// # async_main!(async_main);
|
||||
/// # async fn async_main() {
|
||||
/// println!("Hello, world!");
|
||||
/// TimerFuture::new(Duration::from_secs(5)).await;
|
||||
/// println!("Goodbye, world!");
|
||||
/// # }
|
||||
/// # main();
|
||||
/// ```
|
||||
pub struct TimerFuture {
|
||||
done: Arc<Mutex<bool>>,
|
||||
waker: Arc<Mutex<Option<Waker>>>
|
||||
}
|
||||
|
||||
impl TimerFuture {
|
||||
pub fn new(dur: Duration) -> TimerFuture {
|
||||
let waker = Arc::new(Mutex::new(None));
|
||||
let waker_clone = waker.clone();
|
||||
let done = Arc::new(Mutex::new(false));
|
||||
let done_clone = done.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
thread::sleep(dur);
|
||||
*done_clone.lock().unwrap() = true;
|
||||
if let Some(waker) = waker_clone.lock().unwrap().take() {
|
||||
let waker: Waker = waker; // needed for some reason
|
||||
waker.wake();
|
||||
}
|
||||
});
|
||||
|
||||
TimerFuture {
|
||||
done,
|
||||
waker
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for TimerFuture {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
self.waker.lock().unwrap().replace(cx.waker().clone());
|
||||
if *self.done.lock().unwrap() { Poll::Ready(()) }
|
||||
else { Poll::Pending }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a future that waits for the given amount of milliseconds.
|
||||
///
|
||||
/// ```
|
||||
/// # use messing_with_async::{async_main, futures::TimerFuture};
|
||||
/// # use std::time::Duration;
|
||||
/// # async_main!(async_main);
|
||||
/// # async fn async_main() {
|
||||
/// println!("Hello, world!");
|
||||
/// sleep!(5000).await;
|
||||
/// println!("Goodbye, world!");
|
||||
/// # }
|
||||
/// # main();
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! sleep {
|
||||
($millis:expr) => {
|
||||
$crate::futures::TimerFuture::new(::core::time::Duration::from_millis($millis))
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// A future that waits for two futures to all complete concurrently.
|
||||
///
|
||||
/// ```
|
||||
/// # use messing_with_async::{async_main, AllFuture};
|
||||
/// # async_main!(async_main);
|
||||
/// # async fn async_main() {
|
||||
/// let fut = AllFuture::new(async { 5 }, async { "foobar" });
|
||||
/// assert_eq!(fut.await, (5, "foobar"));
|
||||
/// # }
|
||||
/// # main();
|
||||
/// ```
|
||||
pub struct AllFuture<T, U> {
|
||||
a: Option<Pin<Box<dyn Future<Output = T>>>>,
|
||||
b: Option<Pin<Box<dyn Future<Output = U>>>>,
|
||||
v: Option<T>,
|
||||
w: Option<U>,
|
||||
}
|
||||
|
||||
impl<T, U> AllFuture<T, U> {
|
||||
pub fn new(a: impl Future<Output = T> + 'static, b: impl Future<Output = U> + 'static) -> AllFuture<T, U> { // TODO: "a and b dont live long enough, but i wont tell you how long it should live"
|
||||
Self {
|
||||
a: Some(Box::pin(a)),
|
||||
b: Some(Box::pin(b)),
|
||||
v: None,
|
||||
w: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Unpin for AllFuture<T, U> {} // needed cause T can be !Unpin, but either way Self is Unpin
|
||||
|
||||
impl<T, U> Future for AllFuture<T, U> {
|
||||
type Output = (T, U);
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.get_mut();
|
||||
|
||||
if let Some(mut a) = this.a.take() {
|
||||
if let Poll::Ready(v) = a.as_mut().poll(cx) {
|
||||
this.v = Some(v);
|
||||
} else {
|
||||
this.a.replace(a);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut b) = this.b.take() {
|
||||
if let Poll::Ready(w) = b.as_mut().poll(cx) {
|
||||
this.w = Some(w);
|
||||
} else {
|
||||
this.b.replace(b);
|
||||
}
|
||||
}
|
||||
|
||||
if this.a.is_none() && this.b.is_none() {
|
||||
Poll::Ready((this.v.take().unwrap(), this.w.take().unwrap()))
|
||||
} else { Poll::Pending }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a future that waits for multiple futures to all complete concurrently.
|
||||
///
|
||||
/// ```
|
||||
/// # use messing_with_async::{async_main, all};
|
||||
/// # async_main!(async_main);
|
||||
/// # async fn async_main() {
|
||||
/// let fut = all!(async { 5 }, async { "foobar" }, async { true });
|
||||
/// assert_eq!(fut.await, (5, "foobar", true));
|
||||
/// # }
|
||||
/// # main();
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! all {
|
||||
(@FUTURE $fut:expr) => {
|
||||
$fut
|
||||
};
|
||||
(@FUTURE $fut:expr, $($tail:tt)+) => {
|
||||
$crate::futures::AllFuture::new($fut, all!(@FUTURE $($tail)+))
|
||||
};
|
||||
(@TUPLE $base:expr; $fut:expr; $(, $out:expr)*) => {
|
||||
($($out),*, $base)
|
||||
};
|
||||
(@TUPLE $base:expr; $fut:expr, $($fut_tail:expr),+; $($out:tt)*) => {
|
||||
all!(@TUPLE $base.1; $($fut_tail),+; $($out)*, $base.0)
|
||||
};
|
||||
($($fut:expr),+) => {
|
||||
async {
|
||||
let fut = all!(@FUTURE $($fut),+);
|
||||
let res = fut.await;
|
||||
all!(@TUPLE res; $($fut),+;)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A future that waits for multiple futures (with the same `Output` type) in a `Vec` to all complete concurrently.
|
||||
///
|
||||
/// ```
|
||||
/// # use messing_with_async::{async_main, AllVecFuture};
|
||||
/// # use std::future::Future;
|
||||
/// # async_main!(async_main);
|
||||
/// # async fn async_main() {
|
||||
/// let futures: Vec<Box<dyn Future<Output = _>>> = vec![Box::new(async { 1 }), Box::new(async { 2 }), Box::new(async { 3 })];
|
||||
/// let fut = AllVecFuture::new(futures);
|
||||
/// assert_eq!(fut.await, vec![1, 2, 3])
|
||||
/// # }
|
||||
/// # main();
|
||||
/// ```
|
||||
pub struct AllVecFuture<T> {
|
||||
futs: Vec<Option<Pin<Box<dyn Future<Output = T>>>>>,
|
||||
vals: Vec<Option<T>>
|
||||
}
|
||||
|
||||
impl<T> AllVecFuture<T> {
|
||||
pub fn new(futs: Vec<Box<dyn Future<Output = T>>>) -> Self {
|
||||
Self {
|
||||
vals: futs.iter().map(|_| None).collect(),
|
||||
futs: futs.into_iter()
|
||||
.map(|p| unsafe { Some(Pin::new_unchecked(p)) } )
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Unpin for AllVecFuture<T> {} // needed cause T can be !Unpin, but either way Self is Unpin
|
||||
|
||||
impl<T> Future for AllVecFuture<T> {
|
||||
type Output = Vec<T>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.get_mut();
|
||||
|
||||
for (i, fut_opt) in this.futs.iter_mut().enumerate() {
|
||||
if let Some(fut) = fut_opt {
|
||||
match fut.as_mut().poll(cx) {
|
||||
Poll::Ready(v) => {
|
||||
fut_opt.take();
|
||||
this.vals[i].replace(v);
|
||||
},
|
||||
Poll::Pending => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if this.vals.iter().all(Option::is_some) {
|
||||
return Poll::Ready(this.vals.iter_mut().map(Option::take).map(Option::unwrap).collect())
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a future that waits for multiple futures (with the same `Output` type) to all complete concurrently using a `Vec`.
|
||||
///
|
||||
/// ```
|
||||
/// # use messing_with_async::{async_main, all_vec};
|
||||
/// # use std::future::Future;
|
||||
/// # async_main!(async_main);
|
||||
/// # async fn async_main() {
|
||||
/// let fut = all_vec!(async { 1 }, async { 2 }, async { 3 });
|
||||
/// assert_eq!(fut.await, vec![1, 2, 3]);
|
||||
/// # }
|
||||
/// # main();
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! all_vec {
|
||||
($($fut:expr),+) => {
|
||||
async {
|
||||
let fut = $crate::futures::AllVecFuture::new(::std::vec![$(::std::boxed::Box::new($fut)),+]);
|
||||
let res = fut.await;
|
||||
res
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a type which is either a `T` or a `U`.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Sum<T, U> {
|
||||
A(T),
|
||||
B(U)
|
||||
}
|
||||
|
||||
impl<T, U> Sum<T, U> {
|
||||
pub fn is_a(&self) -> bool {
|
||||
match self {
|
||||
Self::A(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_b(&self) -> bool {
|
||||
match self {
|
||||
Self::B(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_a(self) -> Option<T> {
|
||||
match self {
|
||||
Self::A(v) => Some(v),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_b(self) -> Option<U> {
|
||||
match self {
|
||||
Self::B(v) => Some(v),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_a(self) -> T {
|
||||
match self {
|
||||
Self::A(v) => v,
|
||||
_ => panic!("Attempted to unwrap_a a Sum::B value!")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_b(self) -> U {
|
||||
match self {
|
||||
Self::B(v) => v,
|
||||
_ => panic!("Attempted to unwrap_b a Sum::A value!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A future that waits for one of two futures to complete concurrently.
|
||||
///
|
||||
/// ```
|
||||
/// # use messing_with_async::{async_main, AnyFuture, Sum, TimerFuture};
|
||||
/// # async_main!(async_main);
|
||||
/// # async fn async_main() {
|
||||
/// let fut = AnyFuture::new(async { TimerFuture::new(1000).await; 5 }, async { "foobar" });
|
||||
/// assert_eq!(fut.await, Sum::B("foobar"));
|
||||
/// # }
|
||||
/// # main();
|
||||
/// ```
|
||||
// TODO: this future is still broken, in the above doc comment after 1 second main will be polled again and panic
|
||||
pub struct AnyFuture<T, U> {
|
||||
a: Pin<Box<dyn Future<Output = T>>>,
|
||||
b: Pin<Box<dyn Future<Output = U>>>
|
||||
}
|
||||
|
||||
impl<T, U> AnyFuture<T, U> {
|
||||
pub fn new(a: impl Future<Output = T> + 'static, b: impl Future<Output = U> + 'static) -> AnyFuture<T, U> {
|
||||
Self {
|
||||
a: Box::pin(a),
|
||||
b: Box::pin(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Future for AnyFuture<T, U> {
|
||||
type Output = Sum<T, U>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if let Poll::Ready(v) = self.a.as_mut().poll(cx) {
|
||||
return Poll::Ready(Sum::A(v))
|
||||
}
|
||||
|
||||
if let Poll::Ready(v) = self.b.as_mut().poll(cx) {
|
||||
return Poll::Ready(Sum::B(v))
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a future that waits for one of multiple futures (with the same `Output` type) to complete concurrently.
|
||||
// TODO: this is also broken, dunno how to do it
|
||||
// #[macro_export]
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! any {
|
||||
(@FUTURE $fut:expr) => {
|
||||
$fut
|
||||
};
|
||||
(@FUTURE $fut:expr, $($tail:tt)+) => {
|
||||
$crate::futures::AnyFuture::new($fut, all!(@FUTURE $($tail)+))
|
||||
};
|
||||
($($fut:expr),+) => {
|
||||
async {
|
||||
let fut = any!(@FUTURE $($fut),+);
|
||||
let res = fut.await;
|
||||
todo!();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A future that waits for one of multiple futures (with the same `Output` type) in a `Vec` to complete concurrently.
|
||||
pub struct AnyVecFuture<T> {
|
||||
futs: Vec<Pin<Box<dyn Future<Output = T>>>>
|
||||
}
|
||||
|
||||
impl<T> AnyVecFuture<T> {
|
||||
pub fn new(futs: Vec<Box<dyn Future<Output = T>>>) -> AnyVecFuture<T> {
|
||||
Self {
|
||||
futs: futs.into_iter()
|
||||
.map(|p| unsafe { Pin::new_unchecked(p) })
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Future for AnyVecFuture<T> {
|
||||
type Output = (usize, T);
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
for (i, fut) in self.futs.iter_mut().enumerate() {
|
||||
match fut.as_mut().poll(cx) {
|
||||
Poll::Ready(v) => return Poll::Ready((i, v)),
|
||||
Poll::Pending => {}
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a future that waits for one of multiple futures (with the same `Output` type) to complete concurrently using a `Vec`.
|
||||
#[macro_export]
|
||||
macro_rules! any_vec {
|
||||
($($fut:expr),+) => {
|
||||
async {
|
||||
let fut = $crate::futures::AnyVecFuture::new(::std::alloc::vec![$($fut),+])
|
||||
let res = fut.await;
|
||||
res
|
||||
}
|
||||
};
|
||||
}
|
98
src/lib.rs
Normal file
98
src/lib.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
/// Sets up the given `async fn` to be the entry point.
|
||||
///
|
||||
/// ```
|
||||
/// # use messing_with_async::async_main;
|
||||
/// async_main!(async_main);
|
||||
/// async fn async_main() {
|
||||
/// println!("Hello, world!");
|
||||
/// }
|
||||
/// # main(); // needed cause the main generated by the macro isnt recognized by doctests
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! async_main {
|
||||
($ident:ident) => {
|
||||
fn main() {
|
||||
use ::core::future::Future;
|
||||
|
||||
struct Task {
|
||||
tx: ::std::sync::mpsc::SyncSender<()>
|
||||
}
|
||||
|
||||
impl ::futures::task::ArcWake for Task {
|
||||
fn wake_by_ref(arc_self: &::std::sync::Arc<Self>) {
|
||||
arc_self.tx.send(()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let (tx, rx) = ::std::sync::mpsc::sync_channel(10000);
|
||||
tx.send(()).unwrap();
|
||||
let waker = ::futures::task::waker(::std::sync::Arc::new(Task { tx }));
|
||||
let mut context = ::core::task::Context::from_waker(&waker);
|
||||
let mut future = ::std::boxed::Box::pin($ident());
|
||||
|
||||
while let Ok(_) = rx.recv() {
|
||||
match future.as_mut().poll(&mut context) {
|
||||
::core::task::Poll::Pending => {},
|
||||
::core::task::Poll::Ready(v) => v
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
use std::{sync::{mpsc::{SyncSender, self}, Arc, Mutex}, pin::Pin, future::Future, task::Context};
|
||||
|
||||
use ::futures::task::{ArcWake, waker};
|
||||
|
||||
pub struct Executor {}
|
||||
|
||||
impl Executor {
|
||||
pub fn new() -> Self {
|
||||
Executor {}
|
||||
}
|
||||
|
||||
pub fn run(&self, fut: impl Future<Output = ()> + Send + Sync + 'static) {
|
||||
let (tx, rx) = mpsc::sync_channel(10000);
|
||||
|
||||
let main = Arc::new(Task { tx: tx.clone(), fut: Mutex::new(Box::pin(fut)) });
|
||||
|
||||
tx.send(main).unwrap();
|
||||
drop(tx);
|
||||
|
||||
while let Ok(task) = rx.recv() {
|
||||
let waker = waker(task.clone());
|
||||
let mut context = Context::from_waker(&waker);
|
||||
#[allow(unused_must_use)]
|
||||
{ task.fut.lock().unwrap().as_mut().poll(&mut context); }
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct Task {
|
||||
tx: SyncSender<Arc<Task>>,
|
||||
fut: Mutex<Pin<Box<dyn Future<Output = ()> + Send + Sync>>> // dunno how to fix this, just hope everyhing is send/sync for now
|
||||
}
|
||||
|
||||
impl ArcWake for Task {
|
||||
fn wake_by_ref(arc_self: &Arc<Self>) {
|
||||
arc_self.tx.send(arc_self.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub mod futures;
|
||||
use crate::futures::*;
|
||||
|
||||
|
||||
pub trait FutureExt: Future + 'static {
|
||||
fn and<U>(self, other: impl Future<Output = U> + 'static) -> AllFuture<Self::Output, U>;
|
||||
fn or<U>(self, other: impl Future<Output = U> + 'static) -> AnyFuture<Self::Output, U>;
|
||||
}
|
||||
|
||||
impl<F: Future + 'static> FutureExt for F {
|
||||
fn and<U>(self, other: impl Future<Output = U> + 'static) -> AllFuture<F::Output, U> {
|
||||
AllFuture::new(self, other)
|
||||
}
|
||||
|
||||
fn or<U>(self, other: impl Future<Output = U> + 'static) -> AnyFuture<F::Output, U> {
|
||||
AnyFuture::new(self, other)
|
||||
}
|
||||
}
|
12
src/main.rs
Normal file
12
src/main.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use messing_with_async::{async_main, all, sleep, FutureExt};
|
||||
|
||||
async_main!(async_main);
|
||||
async fn async_main() {
|
||||
println!("hi!");
|
||||
sleep!(1000).await;
|
||||
println!("getting closer!");
|
||||
println!("{:#?}", all!(async { 5 }, async { "foobar" }, async { true }, sleep!(500)).await);
|
||||
println!("almost there!");
|
||||
println!("{:#?}", async { 5 }.and(async { "baz" }).and(async { false }).and(sleep!(2000)).await);
|
||||
println!("done!");
|
||||
}
|
Loading…
Reference in a new issue