diff options
-rw-r--r-- | .cargo_vcs_info.json | 6 | ||||
-rw-r--r-- | .github/workflows/build-and-test.yaml | 50 | ||||
-rw-r--r-- | .github/workflows/lint.yaml | 24 | ||||
-rw-r--r-- | .github/workflows/security.yaml | 17 | ||||
-rw-r--r-- | Android.bp | 9 | ||||
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | Cargo.toml | 48 | ||||
-rw-r--r-- | Cargo.toml.orig | 31 | ||||
-rw-r--r-- | METADATA | 14 | ||||
-rw-r--r-- | TEST_MAPPING | 25 | ||||
-rw-r--r-- | cargo2android.json | 8 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/runnable.rs | 2 | ||||
-rw-r--r-- | src/task.rs | 164 | ||||
-rw-r--r-- | tests/basic.rs | 2 | ||||
-rw-r--r-- | tests/cancel.rs | 2 | ||||
-rw-r--r-- | tests/join.rs | 2 | ||||
-rw-r--r-- | tests/panic.rs | 2 | ||||
-rw-r--r-- | tests/ready.rs | 2 | ||||
-rw-r--r-- | tests/waker_panic.rs | 9 | ||||
-rw-r--r-- | tests/waker_pending.rs | 2 | ||||
-rw-r--r-- | tests/waker_ready.rs | 2 |
22 files changed, 288 insertions, 139 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..afec74b --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "e6daa4ff3caadb73c7a7ddc40034fb02430ccec8" + }, + "path_in_vcs": "" +}
\ No newline at end of file diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml deleted file mode 100644 index d98cd81..0000000 --- a/.github/workflows/build-and-test.yaml +++ /dev/null @@ -1,50 +0,0 @@ -name: Build and test - -on: - push: - branches: - - master - pull_request: - -jobs: - build_and_test: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest] - rust: [nightly, beta, stable] - steps: - - uses: actions/checkout@v2 - - - name: Install latest ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - profile: minimal - override: true - - - name: Install valgrind - run: | - sudo apt-get update - sudo apt-get install -y valgrind - - - name: Run cargo check - uses: actions-rs/cargo@v1 - with: - command: check - args: --all --bins --examples --tests --all-features - - - name: Run cargo check (no_std) - uses: actions-rs/cargo@v1 - with: - command: check - args: --all --no-default-features - - - name: Run cargo test - uses: actions-rs/cargo@v1 - with: - command: test - args: -- --test-threads=1 - env: - CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: "valgrind --leak-check=full --error-exitcode=1" diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml deleted file mode 100644 index e134a54..0000000 --- a/.github/workflows/lint.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: Lint - -on: - push: - branches: - - master - pull_request: - -jobs: - clippy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - components: clippy - - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --all-features -- -W clippy::all diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml deleted file mode 100644 index c4f7947..0000000 --- a/.github/workflows/security.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: Security audit - -on: - push: - branches: - - master - pull_request: - -jobs: - security_audit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - uses: actions-rs/audit-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} @@ -1,4 +1,5 @@ -// This file is generated by cargo2android.py --run. +// This file is generated by cargo2android.py --config cargo2android.json. +// Do not modify this file as changes will be overridden on upgrade. package { default_applicable_licenses: ["external_rust_crates_async-task_license"], @@ -38,16 +39,18 @@ license { rust_library { name: "libasync_task", + host_supported: true, crate_name: "async_task", + cargo_env_compat: true, + cargo_pkg_version: "4.1.0", srcs: ["src/lib.rs"], edition: "2018", - host_supported: true, features: [ "default", "std", ], apex_available: [ "//apex_available:platform", - "com.android.virt", + "com.android.virt", ], } diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bdcc73..668bd12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# Version 4.1.0 + +- Add `FallibleTask`. (#21) + # Version 4.0.3 - Document the return value of `Runnable::run()` better. @@ -1,23 +1,45 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + [package] +edition = "2018" +rust-version = "1.39" name = "async-task" -version = "4.0.3" +version = "4.1.0" authors = ["Stjepan Glavina <stjepang@gmail.com>"] -edition = "2018" -license = "Apache-2.0 OR MIT" -repository = "https://github.com/smol-rs/async-task" -homepage = "https://github.com/smol-rs/async-task" -documentation = "https://docs.rs/async-task" +exclude = ["/.*"] description = "Task abstraction for building executors" keywords = ["futures", "task", "executor", "spawn"] categories = ["asynchronous", "concurrency", "no-std"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-task" +[dev-dependencies.atomic-waker] +version = "1" + +[dev-dependencies.easy-parallel] +version = "3" + +[dev-dependencies.flaky_test] +version = "0.1" + +[dev-dependencies.flume] +version = "0.10" +default-features = false + +[dev-dependencies.once_cell] +version = "1" + +[dev-dependencies.smol] +version = "1" [features] default = ["std"] std = [] - -[dev-dependencies] -atomic-waker = "1.0.0" -easy-parallel = "3.1.0" -flume = { version = "0.10", default-features = false } -once_cell = "1.4.1" -smol = "1.0.1" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..b1ae509 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,31 @@ +[package] +name = "async-task" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v4.x.y" git tag +version = "4.1.0" +authors = ["Stjepan Glavina <stjepang@gmail.com>"] +edition = "2018" +rust-version = "1.39" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/async-task" +description = "Task abstraction for building executors" +keywords = ["futures", "task", "executor", "spawn"] +categories = ["asynchronous", "concurrency", "no-std"] +exclude = ["/.*"] + +[features] +default = ["std"] +std = [] + +[dev-dependencies] +atomic-waker = "1" +easy-parallel = "3" +flaky_test = "0.1" +flume = { version = "0.10", default-features = false } +once_cell = "1" +smol = "1" + +# rewrite dependencies to use the this version of async-task when running tests +[patch.crates-io] +async-task = { path = "." } @@ -1,7 +1,5 @@ name: "async_task" -description: - "Task abstraction for building executors (in rust)." - +description: "Task abstraction for building executors" third_party { url { type: HOMEPAGE @@ -9,9 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/async-task/async-task-4.0.3.crate" + value: "https://static.crates.io/crates/async_task/async_task-4.1.0.crate" } - version: "4.0.3" - last_upgrade_date { year: 2021 month: 2 day: 5 } + version: "4.1.0" license_type: NOTICE + last_upgrade_date { + year: 2022 + month: 3 + day: 1 + } } diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 0000000..fc8ec65 --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,25 @@ +// Generated by update_crate_tests.py for tests that depend on this crate. +{ + "presubmit": [ + { + "name": "ZipFuseTest" + }, + { + "name": "authfs_device_test_src_lib" + }, + { + "name": "virtualizationservice_device_test" + } + ], + "presubmit-rust": [ + { + "name": "ZipFuseTest" + }, + { + "name": "authfs_device_test_src_lib" + }, + { + "name": "virtualizationservice_device_test" + } + ] +} diff --git a/cargo2android.json b/cargo2android.json new file mode 100644 index 0000000..707d26c --- /dev/null +++ b/cargo2android.json @@ -0,0 +1,8 @@ +{ + "apex-available": [ + "//apex_available:platform", + "com.android.virt" + ], + "device": true, + "run": true +}
\ No newline at end of file @@ -82,7 +82,7 @@ mod task; mod utils; pub use crate::runnable::{spawn, spawn_unchecked, Runnable}; -pub use crate::task::Task; +pub use crate::task::{FallibleTask, Task}; #[cfg(feature = "std")] pub use crate::runnable::spawn_local; diff --git a/src/runnable.rs b/src/runnable.rs index adecbcb..cb70ef3 100644 --- a/src/runnable.rs +++ b/src/runnable.rs @@ -147,7 +147,7 @@ where /// This function is same as [`spawn()`], except it does not require [`Send`], [`Sync`], and /// `'static` on `future` and `schedule`. /// -/// Safety requirements: +/// # Safety /// /// - If `future` is not [`Send`], its [`Runnable`] must be used and dropped on the original /// thread. diff --git a/src/task.rs b/src/task.rs index b9c251f..fff918c 100644 --- a/src/task.rs +++ b/src/task.rs @@ -124,18 +124,56 @@ impl<T> Task<T> { pub async fn cancel(self) -> Option<T> { let mut this = self; this.set_canceled(); + this.fallible().await + } - struct Fut<T>(Task<T>); - - impl<T> Future for Fut<T> { - type Output = Option<T>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { - self.0.poll_task(cx) - } - } - - Fut(this).await + /// Converts this task into a [`FallibleTask`]. + /// + /// Like [`Task`], a fallible task will poll the task's output until it is + /// completed or cancelled due to its [`Runnable`][`super::Runnable`] being + /// dropped without being run. Resolves to the task's output when completed, + /// or [`None`] if it didn't complete. + /// + /// # Examples + /// + /// ``` + /// use smol::{future, Executor}; + /// use std::thread; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a future onto the executor. + /// let task = ex.spawn(async { + /// println!("Hello from a task!"); + /// 1 + 2 + /// }) + /// .fallible(); + /// + /// // Run an executor thread. + /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); + /// + /// // Wait for the task's output. + /// assert_eq!(future::block_on(task), Some(3)); + /// ``` + /// + /// ``` + /// use smol::future; + /// + /// // Schedule function which drops the runnable without running it. + /// let schedule = move |runnable| drop(runnable); + /// + /// // Create a task with the future and the schedule function. + /// let (runnable, task) = async_task::spawn(async { + /// println!("Hello from a task!"); + /// 1 + 2 + /// }, schedule); + /// runnable.schedule(); + /// + /// // Wait for the task's output. + /// assert_eq!(future::block_on(task.fallible()), None); + /// ``` + pub fn fallible(self) -> FallibleTask<T> { + FallibleTask { task: self } } /// Puts the task in canceled state. @@ -351,6 +389,12 @@ impl<T> Task<T> { } } } + + fn header(&self) -> &Header { + let ptr = self.ptr.as_ptr(); + let header = ptr as *const Header; + unsafe { &*header } + } } impl<T> Drop for Task<T> { @@ -373,11 +417,101 @@ impl<T> Future for Task<T> { impl<T> fmt::Debug for Task<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let ptr = self.ptr.as_ptr(); - let header = ptr as *const Header; - f.debug_struct("Task") - .field("header", unsafe { &(*header) }) + .field("header", self.header()) + .finish() + } +} + +/// A spawned task with a fallible response. +/// +/// This type behaves like [`Task`], however it produces an `Option<T>` when +/// polled and will return `None` if the executor dropped its +/// [`Runnable`][`super::Runnable`] without being run. +/// +/// This can be useful to avoid the panic produced when polling the `Task` +/// future if the executor dropped its `Runnable`. +#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"] +pub struct FallibleTask<T> { + task: Task<T>, +} + +impl<T> FallibleTask<T> { + /// Detaches the task to let it keep running in the background. + /// + /// # Examples + /// + /// ``` + /// use smol::{Executor, Timer}; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a deamon future. + /// ex.spawn(async { + /// loop { + /// println!("I'm a daemon task looping forever."); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }) + /// .fallible() + /// .detach(); + /// ``` + pub fn detach(self) { + self.task.detach() + } + + /// Cancels the task and waits for it to stop running. + /// + /// Returns the task's output if it was completed just before it got canceled, or [`None`] if + /// it didn't complete. + /// + /// While it's possible to simply drop the [`Task`] to cancel it, this is a cleaner way of + /// canceling because it also waits for the task to stop running. + /// + /// # Examples + /// + /// ``` + /// use smol::{future, Executor, Timer}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let ex = Executor::new(); + /// + /// // Spawn a deamon future. + /// let task = ex.spawn(async { + /// loop { + /// println!("Even though I'm in an infinite loop, you can still cancel me!"); + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// }) + /// .fallible(); + /// + /// // Run an executor thread. + /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>()))); + /// + /// future::block_on(async { + /// Timer::after(Duration::from_secs(3)).await; + /// task.cancel().await; + /// }); + /// ``` + pub async fn cancel(self) -> Option<T> { + self.task.cancel().await + } +} + +impl<T> Future for FallibleTask<T> { + type Output = Option<T>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + self.task.poll_task(cx) + } +} + +impl<T> fmt::Debug for FallibleTask<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FallibleTask") + .field("header", self.task.header()) .finish() } } diff --git a/tests/basic.rs b/tests/basic.rs index 1439e16..c223043 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -64,7 +64,7 @@ macro_rules! schedule { let guard = Guard(Box::new(0)); move |_runnable| { - &guard; + let _ = &guard; $sched.fetch_add(1, Ordering::SeqCst); } }; diff --git a/tests/cancel.rs b/tests/cancel.rs index 4f77ae5..0fe7c72 100644 --- a/tests/cancel.rs +++ b/tests/cancel.rs @@ -81,7 +81,7 @@ macro_rules! schedule { let guard = Guard(Box::new(0)); move |runnable: Runnable| { - &guard; + let _ = &guard; runnable.schedule(); $sched.fetch_add(1, Ordering::SeqCst); } diff --git a/tests/join.rs b/tests/join.rs index 17312a4..2ac7b8d 100644 --- a/tests/join.rs +++ b/tests/join.rs @@ -82,7 +82,7 @@ macro_rules! schedule { let guard = Guard(Box::new(0)); move |runnable: Runnable| { - &guard; + let _ = &guard; runnable.schedule(); $sched.fetch_add(1, Ordering::SeqCst); } diff --git a/tests/panic.rs b/tests/panic.rs index f38fb3e..09ffb28 100644 --- a/tests/panic.rs +++ b/tests/panic.rs @@ -69,7 +69,7 @@ macro_rules! schedule { let guard = Guard(Box::new(0)); move |_runnable: Runnable| { - &guard; + let _ = &guard; $sched.fetch_add(1, Ordering::SeqCst); } }; diff --git a/tests/ready.rs b/tests/ready.rs index ebfdc63..e345565 100644 --- a/tests/ready.rs +++ b/tests/ready.rs @@ -81,7 +81,7 @@ macro_rules! schedule { let guard = Guard(Box::new(0)); move |_runnable: Runnable| { - &guard; + let _ = &guard; $sched.fetch_add(1, Ordering::SeqCst); } }; diff --git a/tests/waker_panic.rs b/tests/waker_panic.rs index 68b0a81..7a7792e 100644 --- a/tests/waker_panic.rs +++ b/tests/waker_panic.rs @@ -86,7 +86,7 @@ macro_rules! schedule { let guard = Guard(Box::new(0)); let sched = move |runnable: Runnable| { - &guard; + let _ = &guard; $sched.fetch_add(1, Ordering::SeqCst); s.send(runnable).unwrap(); }; @@ -238,10 +238,15 @@ fn wake_and_cancel_during_run() { .run(); } -#[test] +#[flaky_test::flaky_test] fn cancel_and_wake_during_run() { future!(f, get_waker, POLL, DROP_F); schedule!(s, chan, SCHEDULE, DROP_S); + POLL.store(0, Ordering::SeqCst); + DROP_F.store(0, Ordering::SeqCst); + SCHEDULE.store(0, Ordering::SeqCst); + DROP_S.store(0, Ordering::SeqCst); + let (runnable, task) = async_task::spawn(f, s); runnable.run(); diff --git a/tests/waker_pending.rs b/tests/waker_pending.rs index e30af33..9c95cba 100644 --- a/tests/waker_pending.rs +++ b/tests/waker_pending.rs @@ -77,7 +77,7 @@ macro_rules! schedule { let guard = Guard(Box::new(0)); let sched = move |runnable: Runnable| { - &guard; + let _ = &guard; $sched.fetch_add(1, Ordering::SeqCst); s.send(runnable).unwrap(); }; diff --git a/tests/waker_ready.rs b/tests/waker_ready.rs index d46fd6f..10d38cb 100644 --- a/tests/waker_ready.rs +++ b/tests/waker_ready.rs @@ -83,7 +83,7 @@ macro_rules! schedule { let guard = Guard(Box::new(0)); let sched = move |runnable: Runnable| { - &guard; + let _ = &guard; $sched.fetch_add(1, Ordering::SeqCst); s.send(runnable).unwrap(); }; |