From 9c145b513dce0ca0e71e3a35c99dda8af98f8b05 Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Tue, 20 Apr 2021 13:01:44 -0700 Subject: [PATCH] potential async-std support --- .github/workflows/ci.yml | 2 ++ Cargo.toml | 6 ++++- src/adaptive/core.rs | 9 +++++++ src/lib.rs | 56 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 569dd13..4963c8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,8 @@ jobs: run: rustup update stable - name: cargo test run: cargo test + - name: cargo test async_std + run: cargo test --no-default-features --features async-std-futures bench: name: Bench on nightly runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index e32ae99..6699b0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "impedance" -version = "0.2.3" +version = "0.2.4" authors = ["Gus Wynn "] edition = "2018" description = "Tools to mix blocking and async code" @@ -11,8 +11,11 @@ license = "MIT OR Apache-2.0" [features] default = ["tokio"] +async-std-futures = ["async-std", "futures"] [dependencies] +async-std = { version = "1.9.0", features = ["unstable"], optional = true } +futures = { version = "0.3.14", optional = true } once_cell = "1.7.2" parking_lot = "0.11.1" pin-project = "1.0.7" @@ -22,3 +25,4 @@ tokio = { version = "1.5.0", features = ["rt", "sync"], optional = true } futures = "0.3.14" serde_json = "1.0.64" tokio = { version = "1.5.0", features = ["rt-multi-thread", "macros", "sync"] } +async-std = { version = "1.9.0", features = ["unstable", "attributes"] } diff --git a/src/adaptive/core.rs b/src/adaptive/core.rs index b462ecd..3c8238b 100644 --- a/src/adaptive/core.rs +++ b/src/adaptive/core.rs @@ -9,6 +9,10 @@ use std::{ time::{Duration, Instant}, }; // TODO(azw): support more executors +#[cfg(feature = "async-std-futures")] +use async_std::task::{spawn_blocking, JoinHandle}; +#[cfg(feature = "async-std-futures")] +use futures::channel::oneshot::{channel, Receiver}; #[cfg(feature = "tokio")] use tokio::{ sync::oneshot::{channel, Receiver}, @@ -142,8 +146,13 @@ impl O + Send + 'static> Future for TimedBlock _ => false, }; + println!("GUS"); match Pin::new(jh).poll(cx) { + #[cfg(feature = "async-std-futures")] + Poll::Ready(val) => return Poll::Ready(val), + #[cfg(feature = "tokio")] Poll::Ready(Ok(val)) => return Poll::Ready(val), + #[cfg(feature = "tokio")] Poll::Ready(Err(e)) => match e.try_into_panic() { Ok(panic) => { std::panic::resume_unwind(panic); diff --git a/src/lib.rs b/src/lib.rs index 274cdb5..d608775 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,12 +33,20 @@ //! //! ## Features //! This library should be design in a way such that any executor that has a -//! `spawn_blocking` method can be used. However, currently it only implements it for -//! [`tokio`](tokio) which is in its `default_features`. +//! `spawn_blocking` method can be used: //! +// TODO(guswynn): can rustdoc auto make these links for me? +//! - `tokio`: Currently this library tries to provide good support +//! for [`tokio`](tokio) which is in its `default_features`. +//! - `async-std-futures`: This library has experimental support for using [`async-std`](https://docs.rs/async-std) (as well as +//! [`futures`](https://docs.rs/futures) internally for a oneshot channel). You will need to use `default-features +//! = false` +//! and there are caveats: First and foremost, panic payloads's are NOT ALWAYS propagated +//! correctly, they have a default failed task message when the work was moved to a thread. +//! - TODO: consider [`async_executors`](https://docs.rs/async_executors) for this abstraction pub mod adaptive; -#[cfg(test)] +#[cfg(all(test, feature = "tokio"))] mod tests { use super::*; use adaptive::{AdaptiveFuture, Token}; @@ -69,7 +77,7 @@ mod tests { } #[tokio::test] - #[should_panic] + #[should_panic(expected = "gus")] async fn test_panic_adaptive() { let thing = AdaptiveFuture::new(Token::new(), || { if false { @@ -82,7 +90,45 @@ mod tests { } #[tokio::test] - #[should_panic] + #[should_panic(expected = "gus")] + async fn test_panic_spawning() { + let thing = AdaptiveFuture::new(Token::always_spawn(), || { + if false { + 1_isize + } else { + panic!("gus"); + } + }); + assert_eq!(1, thing.await); + } +} + +#[cfg(all(test, feature = "async-std-futures"))] +mod async_std_tests { + use super::*; + use adaptive::{AdaptiveFuture, Token}; + + #[async_std::test] + async fn test_basic() { + let thing = AdaptiveFuture::new(Token::new(), || 1); + assert_eq!(1, thing.await); + } + + #[async_std::test] + #[should_panic(expected = "gus")] + async fn test_panic_adaptive() { + let thing = AdaptiveFuture::new(Token::new(), || { + if false { + 1_isize + } else { + panic!("gus"); + } + }); + assert_eq!(1, thing.await); + } + + #[async_std::test] + #[should_panic(expected = "task has failed")] async fn test_panic_spawning() { let thing = AdaptiveFuture::new(Token::always_spawn(), || { if false {