├── .github ├── PULL_REQUEST_TEMPLATE.md ├── funding.yml └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── Cargo.yml ├── LICENSE ├── README.md ├── TODO.md ├── build.rs ├── ci ├── clippy.bash ├── doc.bash ├── test.bash ├── ulimit_memlock.bash └── wasm.bash ├── deny.toml ├── examples ├── async_std.rs ├── glommio_ct.rs ├── spawn_handle_multi.rs ├── timer.rs ├── tokio_ct.rs ├── tokio_io.rs ├── tokio_tp.rs ├── tracing.rs ├── trait_set.rs └── yield_now.rs ├── src ├── exec │ ├── async_global.rs │ ├── async_std.rs │ ├── bindgen.rs │ ├── glommio_ct.rs │ ├── localpool.rs │ ├── mod.rs │ ├── threadpool.rs │ ├── tokio_ct.rs │ ├── tokio_tp.rs │ └── tracing.rs ├── iface │ ├── blocking_handle.rs │ ├── join_handle.rs │ ├── local_spawn_handle.rs │ ├── mod.rs │ ├── spawn_blocking.rs │ ├── spawn_handle.rs │ ├── timer.rs │ └── yield_now.rs └── lib.rs └── tests ├── async_global.rs ├── async_global_wasm.rs ├── async_std.rs ├── async_std_wasm.rs ├── bindgen.rs ├── common.rs ├── glommio_ct.rs ├── localpool.rs ├── threadpool.rs ├── tokio_ct.rs ├── tokio_tp.rs └── unpin.rs /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | liberapay: najamelan 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on : [push, pull_request] 3 | 4 | jobs: 5 | 6 | linux-stable: 7 | 8 | name: Linux Rust Stable 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | 13 | - name: Install latest stable Rust 14 | uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: stable 17 | override: true 18 | components: clippy 19 | 20 | 21 | - name: Checkout crate 22 | uses: actions/checkout@v3 23 | 24 | - name: Run tests 25 | run : ci/test.bash 26 | 27 | - name: Check semver 28 | uses: obi1kenobi/cargo-semver-checks-action@v1 29 | with: 30 | version-tag-prefix: '' 31 | 32 | linux-nightly: 33 | 34 | name: Linux Rust Nightly 35 | runs-on: ubuntu-latest 36 | 37 | steps: 38 | 39 | - name: Install latest nightly Rust 40 | uses: actions-rs/toolchain@v1 41 | with: 42 | toolchain: nightly 43 | override: true 44 | components: clippy 45 | 46 | 47 | - name: Checkout crate 48 | uses: actions/checkout@v3 49 | 50 | 51 | - name: Run clippy 52 | run : bash ci/clippy.bash 53 | 54 | 55 | - name: Build documentation 56 | run : bash ci/doc.bash 57 | 58 | 59 | - name: Run tests 60 | run : bash ci/test.bash 61 | # run : sudo bash -icE "ulimit -l512; ci/test.bash" 62 | 63 | 64 | - name: Install cargo-tarpaulin binary crate 65 | uses: actions-rs/install@v0.1 66 | with: 67 | crate: cargo-tarpaulin 68 | version: latest 69 | use-tool-cache: true 70 | 71 | - name: Run cargo-tarpaulin 72 | run : | 73 | cargo tarpaulin --out Xml 74 | 75 | - name: Upload to codecov.io 76 | uses: codecov/codecov-action@v1.5.2 77 | 78 | - name: Run cargo-deny 79 | uses: EmbarkStudios/cargo-deny-action@v1 80 | 81 | 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # async_executors - CHANGELOG 2 | 3 | 4 | ## [Unreleased] 5 | 6 | [Unreleased]: https://github.com/najamelan/async_executors/compare/0.7.0...dev 7 | 8 | 9 | ## [0.7.0] - 2023-07-22 10 | 11 | [0.7.0]: https://github.com/najamelan/async_executors/compare/0.6.0..0.7.0 12 | 13 | ### Added 14 | 15 | - **BREAKING**: Create tokio executors from currently entered runtimes as well as handles. 16 | 17 | ### Removed 18 | 19 | - **BREAKING**: The _tokio_ builder types. As tokio now exposes the `runtime_flavour`, we 20 | can use it to keep you from creating a runtime of the wrong type. Thus the builder types 21 | are no longer needed. 22 | 23 | ### Updated 24 | - **BREAKING**: glommio to v0.8 25 | 26 | ## [0.6.0] - 2022-04-24 27 | 28 | [0.6.0]: https://github.com/najamelan/async_executors/compare/0.5.1..0.6.0 29 | 30 | ### Added 31 | 32 | - **BREAKING**: update glommio to 0.7. 33 | 34 | 35 | ## [0.5.1] - 2022-01-06 36 | 37 | [0.5.1]: https://github.com/najamelan/async_executors/compare/0.5.0..0.5.1 38 | 39 | ### Added 40 | 41 | - forward SpawnBlocking trait from `tracing_futures::Instrumented` and `tracing_futures::WithDispatch`. 42 | 43 | 44 | ## [0.5.0] - 2022-01-04 45 | 46 | [0.5.0]: https://github.com/najamelan/async_executors/compare/0.4.2..0.5.0 47 | 48 | ### Added 49 | 50 | - **BREAKING**: update glommio to 0.6. 51 | - add `SpawnBlocking` trait and `BlockingHandle`. 52 | 53 | 54 | ## [0.4.2] - 2021-06-10 55 | 56 | [0.4.2]: https://github.com/najamelan/async_executors/compare/0.4.1...0.4.2 57 | 58 | ### Fixed 59 | - feature `external_doc` is removed in rustdoc 1.54. 60 | 61 | ### Added 62 | - support for Glommio current thread executor. Thanks to @qiujiangkun. 63 | - `YieldNow` trait. 64 | - `SpawnBlocking` trait and `BlockingHandle`. 65 | - `Timer` trait for agnostic sleep and timeout. Thanks to @nmathewson for sharing 66 | their work on tor-rtcompat. 67 | - `TokioIo` trait. 68 | 69 | 70 | ## [0.4.1] - 2021-04-24 71 | 72 | [0.4.1]: https://github.com/najamelan/async_executors/compare/0.4.0...0.4.1 73 | 74 | ### Fixed 75 | - a missing feature flag on futures-util. 76 | 77 | 78 | ## [0.4.0] - 2021-01-01 79 | 80 | [0.4.0]: https://github.com/najamelan/async_executors/compare/0.4.0-beta.2...0.4.0 81 | 82 | ### Added 83 | - support for async-global-executor v2. 84 | - support for tokio v1 85 | 86 | 87 | ## [0.4.0-beta.2] - 2020-11-05 88 | 89 | [0.4.0-beta.2]: https://github.com/najamelan/async_executors/compare/0.4.0-beta.1...0.4.0-beta.2 90 | 91 | ### Added 92 | - support for async-global-executor. 93 | 94 | 95 | ## [0.4.0-beta.1] - 2020-11-01 96 | 97 | [0.4.0-beta.1]: https://github.com/najamelan/async_executors/compare/0.3.0...0.4.0-beta.1 98 | 99 | ### Added 100 | - BREAKING CHANGE: support tokio 0.3. Will go out of beta when tokio releases 1.0. 101 | - add example for use with tracing. 102 | 103 | ### Fixed 104 | - update cargo deny. 105 | 106 | 107 | ## [0.3.0] - 2020-06-10 108 | 109 | [0.3.0]: https://github.com/najamelan/async_executors/compare/0.3.0-beta.1...0.3.0 110 | 111 | ### Fixed 112 | - update to async-std 1.6. Local spawn still requires the `unstable` feature on async-std so _async_executors_ enables that. 113 | 114 | ## [0.3.0-beta.1] - 2020-05-10 115 | 116 | [0.3.0-beta.1]: https://github.com/najamelan/async_executors/compare/0.2.2...0.3.0-beta.1 117 | 118 | ### Fixed 119 | - futures 0.3.5 has been released, so we no longer have to vendor RemoteHandle. It means we are now `forbid( unsafe )` and 120 | the spawn_handle feature has been removed since we no longer require extra dependencies in order to provide the traits. 121 | - update to async-std 1.6.0-beta.1. Async-std is now backed by smol. It now supports Wasm and `LocalSpawn`. Local spawning 122 | is unstable, so we turn on the unstable feature. As they are still in beta, we reflect that in our version. 123 | 124 | 125 | ## [0.2.2] - 2020-04-25 126 | 127 | [0.2.2]: https://github.com/najamelan/async_executors/compare/0.2.1...0.2.2 128 | 129 | ### Fixed 130 | - Temporarily remove forbid unsafe. A non-breaking change update of pin-utils now causes a macro to no longer pass. 131 | We have this because we vendor RemoteHandle from futures until they release a new version. 132 | - Improve an error message for a panic on JoinHandle. 133 | 134 | 135 | ## [0.2.1] - 2020-04-08 136 | 137 | [0.2.1]: https://github.com/najamelan/async_executors/compare/0.2.0...0.2.1 138 | 139 | ### Fixed 140 | - JoinHandle::detach didn't work properly. Sorry, my bad as it wasn't even tested. 141 | - remove the Unpin impl from JoinHandle. The joinhandle is still Unpin anyway. 142 | - run cargo deny in CI. 143 | - Vamp up the docs, removing some errors and adding examples. 144 | - `TokioCt` and `TokioTp` block_on no longer require `&mut self`, just `&self`. Since they 145 | implement `Clone`, it didn't protect against re-entrance anyway. 146 | - improve performance of `spawn_handle_local` on `TokioCt` as I mistakenly thought `tokio::JoinHandle` 147 | required `T` to be `Send`, so I was not using the native `JoinHandle`. 148 | - only build on default target on docs.rs. 149 | - clean up and configure the CI configuration. 150 | 151 | ## [0.2.0] - 2020-02-29 152 | 153 | [0.2.0]: https://github.com/najamelan/async_executors/compare/0.1.0...0.2.0 154 | 155 | ### Fixed 156 | - **BREAKING CHANGE**: the API of SpawnHandle has been reworked. 0.1 had double traits, one 157 | not object safe. There were two reasons for this: 158 | - the os version needed an extra boxing. Benchmarks showed that the overhead from this is neglectable. 159 | - the os version needs to have the output type on the trait instead of on the spawn function. 160 | this is inconvenient if you need to take in an executor that can spawn for several output types, but 161 | since it is merely inconvenient, I feel this is not a good enough argument to have the traits in 2 162 | versions. Workaround examples have been added to the documentation of `SpawnHandle` and `LocalSpawnHandle`. 163 | 164 | The `SpawnHandle` trait now takes a `FutureObj` instead of a `Pin>`. This should be better 165 | for `no_std` compatibility. An extension trait has been added much like `SpawnExt` in the futures lib to 166 | allow spawning futures without having to manually create a `FutureObj`. 167 | 168 | - tracing-futures 0.2.3 is out, so no patching required anymore. 169 | - RemoteHandle is still vendored until the next release of futures. 170 | - TokioCt now uses tokio::task::LocalSet, removing the single line of unsafe we had. 171 | This also improves performance dramatically. Thanks @Yandros for pointing out 172 | LocalSet. 173 | 174 | ## [0.1.0] - 2020-02-24 175 | 176 | [0.1.0]: https://github.com/najamelan/async_executors/compare/0.1.0-alpha.2...0.1.0 177 | 178 | ### Fixed 179 | - Re-add TokioHandle to work around the fact that tokio::Runtime can't be dropped in async context. 180 | 181 | ## 0.1.0-alpha.2 - 2020-02-19 182 | 183 | - fix error in feature name. 184 | - clean up readme. 185 | 186 | ## 0.1.0-alpha.1 - 2020-02-19 187 | 188 | This is an alpha release because: 189 | 190 | - tracing integration should be tested 191 | - examples for tracing/bindgen/spawn_handle/using futures executors 192 | - remote_handle currently vendored until: https://github.com/rust-lang/futures-rs/pull/2081 lands 193 | - tracing-futures currently patched until 0.2.3 to get the needed PR's 194 | - no cross framework support for spawn_blocking yet. 195 | - wasm-bindgen-cli does not compile on windows, breaking CI: https://github.com/rustwasm/wasm-bindgen/issues/2006 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This repository accepts contributions. Ideas, questions, feature requests and bug reports can be filed through Github issues. 4 | 5 | Pull Requests are welcome on Github. By committing pull requests, you accept that your code might be modified and reformatted to fit the project coding style or to improve the implementation. Contributed code is considered licensed under the same license as the rest of the project unless explicitly agreed otherwise. See the `LICENCE` file. 6 | 7 | Please discuss what you want to see modified before filing a pull request if you don't want to be doing work that might be rejected. 8 | 9 | 10 | ## Code formatting 11 | 12 | I understand my code formatting style is quite uncommon, but it is deliberate and helps readability for me. Unfortunately, it cannot be achieved with automated tools like `rustfmt`. **Feel free to contribute code formatted however you are comfortable writing it**. I am happy to reformat during the review process. If you are uncomfortable reading my code, I suggest running `rustfmt` on the entire source tree or set your line-height to 1.2 instead of the common 1.5, which will make it look a lot less over the top. 13 | 14 | 15 | # git workflow 16 | 17 | Please file PR's against the `dev` branch, don't forget to update the documentation. 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # Auto-generated from "Cargo.yml" 2 | [badges] 3 | [badges.maintenance] 4 | status = "actively-developed" 5 | 6 | [badges.travis-ci] 7 | repository = "najamelan/async_executors" 8 | 9 | [build-dependencies] 10 | rustc_version = "^0.4" 11 | 12 | [dependencies] 13 | blanket = "^0.3" 14 | pin-project = "^1" 15 | 16 | [dependencies.async-global-executor] 17 | default-features = false 18 | optional = true 19 | version = "^2" 20 | 21 | [dependencies.async_std_crate] 22 | features = ["unstable"] 23 | optional = true 24 | package = "async-std" 25 | version = "^1.6" 26 | 27 | [dependencies.futures-core] 28 | version = "^0.3" 29 | 30 | [dependencies.futures-executor] 31 | optional = true 32 | version = "^0.3" 33 | 34 | [dependencies.futures-task] 35 | version = "^0.3" 36 | 37 | [dependencies.futures-timer] 38 | optional = true 39 | version = "^3" 40 | 41 | [dependencies.futures-util] 42 | features = ["channel"] 43 | version = "^0.3" 44 | 45 | [dependencies.glommio_crate] 46 | optional = true 47 | package = "glommio" 48 | version = "^0.8" 49 | 50 | [dependencies.tokio] 51 | optional = true 52 | version = "^1" 53 | 54 | [dependencies.tracing-futures] 55 | features = ["futures-03"] 56 | optional = true 57 | version = "^0.2" 58 | 59 | [dependencies.wasm-bindgen-futures] 60 | optional = true 61 | version = "^0.4" 62 | 63 | [dev-dependencies] 64 | static_assertions = "^1" 65 | trait-set = "^0.3" 66 | 67 | [dev-dependencies.futures] 68 | features = ["thread-pool"] 69 | version = "^0.3" 70 | 71 | [dev-dependencies.tokio] 72 | features = ["macros", "rt"] 73 | version = "^1" 74 | 75 | [dev-dependencies.tracing-subscriber] 76 | default-features = false 77 | features = ["fmt"] 78 | version = "^0.3.5" 79 | 80 | [dev-dependencies.tracing_crate] 81 | package = "tracing" 82 | version = "^0.1" 83 | 84 | [[example]] 85 | name = "async_std" 86 | path = "examples/async_std.rs" 87 | required-features = ["notwasm", "async_std"] 88 | 89 | [[example]] 90 | name = "tokio_ct" 91 | path = "examples/tokio_ct.rs" 92 | required-features = ["notwasm", "tokio_ct"] 93 | 94 | [[example]] 95 | name = "tokio_tp" 96 | path = "examples/tokio_tp.rs" 97 | required-features = ["notwasm", "tokio_tp"] 98 | 99 | [[example]] 100 | name = "spawn_handle_multi" 101 | path = "examples/spawn_handle_multi.rs" 102 | required-features = ["notwasm", "async_std"] 103 | 104 | [[example]] 105 | name = "trait_set" 106 | path = "examples/trait_set.rs" 107 | required-features = ["notwasm", "async_std"] 108 | 109 | [[example]] 110 | name = "tracing" 111 | path = "examples/tracing.rs" 112 | required-features = ["notwasm", "tracing", "async_std"] 113 | 114 | [[example]] 115 | name = "glommio_ct" 116 | path = "examples/glommio_ct.rs" 117 | required-features = ["notwasm", "glommio"] 118 | 119 | [[example]] 120 | name = "timer" 121 | path = "examples/timer.rs" 122 | required-features = ["notwasm", "async_std", "tokio_ct", "tokio_timer"] 123 | 124 | [[example]] 125 | name = "tokio_io" 126 | path = "examples/tokio_io.rs" 127 | required-features = ["notwasm", "async_global_tokio", "async_std_tokio", "tokio_ct", "tokio_io"] 128 | 129 | [[example]] 130 | name = "yield_now" 131 | path = "examples/yield_now.rs" 132 | required-features = ["notwasm", "tokio_ct"] 133 | 134 | [features] 135 | async_global = ["async-global-executor"] 136 | async_global_tokio = ["async_global", "async-global-executor/tokio"] 137 | async_std = ["async_std_crate"] 138 | async_std_tokio = ["async_std", "async_std_crate/tokio1"] 139 | bindgen = ["wasm-bindgen-futures"] 140 | default = ["notwasm"] 141 | glommio = ["glommio_crate"] 142 | localpool = ["futures-executor"] 143 | notwasm = [] 144 | threadpool = ["futures-executor/thread-pool"] 145 | timer = ["futures-timer"] 146 | tokio_ct = ["tokio/rt"] 147 | tokio_io = ["tokio/net", "tokio/process"] 148 | tokio_timer = ["tokio/time"] 149 | tokio_tp = ["tokio/rt-multi-thread"] 150 | tracing = ["tracing-futures"] 151 | 152 | [package] 153 | authors = ["Naja Melan "] 154 | categories = ["asynchronous", "concurrency"] 155 | description = "Implements Spawn, SpawnLocal and SpawnHandle for commonly used executors." 156 | documentation = "https://docs.rs/async_executors" 157 | edition = "2021" 158 | exclude = ["examples", "tests", "TODO.md", "deny.toml", ".travis.yml", "CONTRIBUTING.md"] 159 | keywords = ["async", "executor", "futures"] 160 | license = "Unlicense" 161 | name = "async_executors" 162 | readme = "README.md" 163 | repository = "https://github.com/najamelan/async_executors" 164 | version = "0.7.0" 165 | 166 | [package.metadata] 167 | [package.metadata.docs] 168 | [package.metadata.docs.rs] 169 | all-features = true 170 | targets = [] 171 | 172 | [profile] 173 | [profile.release] 174 | codegen-units = 1 175 | 176 | [target] 177 | [target."cfg(not(target_os = \"unknown\"))"] 178 | [target."cfg(not(target_os = \"unknown\"))".dev-dependencies] 179 | futures-timer = "^3" 180 | 181 | [target."cfg(not(target_os = \"unknown\"))".dev-dependencies.tokio] 182 | features = ["io-util", "net", "rt-multi-thread"] 183 | version = "^1" 184 | 185 | [target."cfg(target_arch = \"wasm32\")"] 186 | [target."cfg(target_arch = \"wasm32\")".dependencies] 187 | [target."cfg(target_arch = \"wasm32\")".dependencies.futures-timer] 188 | features = ["wasm-bindgen"] 189 | optional = true 190 | version = "^3" 191 | 192 | [target."cfg(target_arch = \"wasm32\")".dev-dependencies] 193 | wasm-bindgen-test = "^0.3" 194 | -------------------------------------------------------------------------------- /Cargo.yml: -------------------------------------------------------------------------------- 1 | package: 2 | 3 | # When releasing to crates.io: 4 | # 5 | # - last check for all TODO, FIXME, expect, unwrap, todo!, unreachable!. 6 | # - recheck log statements (informative, none left that were just for development, ...) 7 | # - `cargo +nightly doc --no-deps --all-features --open` and re-read and final polish of documentation. 8 | # 9 | # - Update CHANGELOG.md. 10 | # - Update version numbers in Cargo.yml, Cargo.toml, install section of readme. 11 | # 12 | # - `touch **.rs && cargo clippy --tests --examples --benches --all-features` 13 | # - `cargo update` 14 | # - `cargo outdated --root-deps-only` 15 | # - `cargo audit` 16 | # - `cargo udeps --all-targets --all-features` 17 | # - `cargo crev crate verify --show-all --recursive` and review. 18 | # - 'cargo test --all-targets --all-features' 19 | # 20 | # - push dev and verify CI result 21 | # - check code coverage 22 | # - `cargo test` on dependent crates 23 | # 24 | # - cargo publish 25 | # - `git checkout master && git merge dev --no-ff` 26 | # - `git tag x.x.x` with version number. 27 | # - `git push && git push --tags` 28 | # 29 | version : 0.7.0 30 | name : async_executors 31 | authors : [ Naja Melan ] 32 | description : Implements Spawn, SpawnLocal and SpawnHandle for commonly used executors. 33 | documentation : https://docs.rs/async_executors 34 | repository : https://github.com/najamelan/async_executors 35 | readme : README.md 36 | keywords : [ async, executor, futures ] 37 | categories : [ asynchronous, concurrency ] 38 | license : Unlicense 39 | edition : '2021' 40 | exclude : [ examples, tests, TODO.md, deny.toml, .travis.yml, CONTRIBUTING.md ] 41 | 42 | metadata: 43 | docs: 44 | rs: 45 | all-features: true 46 | targets : [] 47 | 48 | 49 | 50 | # Please see the readme for details. 51 | # 52 | features: 53 | 54 | # The `notwasm` feature only exists so that cargo test doesn't try to compile the examples when testing 55 | # wasm. Ignore. 56 | # 57 | default : [ notwasm ] 58 | 59 | 60 | # Turn on futures-timer. This will provide timer functionality for executors 61 | # that don't have it built in: AsyncGlobal, Bindgen, LocalPool, ThreadPool. 62 | # Will also implement Timer on tokio executors if the tokio_timer feature is not enabled. 63 | # 64 | timer : [ futures-timer ] 65 | 66 | # Enable integration with tracing-futures. This implements the SpawnHandle, 67 | # Io and Timer traits on wrapped executors Instrumented and WithDispatch. 68 | # 69 | tracing: [ tracing-futures ] 70 | 71 | 72 | ### Executors ### 73 | 74 | # Add support for the executor from async-global-executor. 75 | # 76 | async_global: [ async-global-executor ] 77 | 78 | # Make sure async-io is enabled on async-global-executor. 79 | # This will implement TokioIo on AsyncGlobal. Not available on Wasm. 80 | # 81 | async_global_tokio: [ async_global, async-global-executor/tokio ] 82 | 83 | 84 | # Enables the async-std task executor. 85 | # Also implements the AsyncIo and Timer traits. Currently async-io is always loaded when 86 | # async-std is. See: https://github.com/async-rs/async-std/issues/975 87 | # 88 | async_std : [ async_std_crate ] 89 | 90 | # Makes sure the tokio reactor is running. Implements TokioIo for AsyncStd. 91 | # Not available on Wasm. 92 | # 93 | async_std_tokio : [ async_std, async_std_crate/tokio1 ] 94 | 95 | 96 | # Enables the tokio current_thread executor. Not available on Wasm. 97 | # 98 | tokio_ct : [ tokio/rt ] 99 | 100 | # Enables the tokio thread_pool executor. Not available on Wasm. 101 | # 102 | tokio_tp : [ tokio/rt-multi-thread ] 103 | 104 | # Make sure a tokio reactor is running. 105 | # This implements the TokioIo trait on the two tokio executors. 106 | # 107 | tokio_io: [ tokio/net, tokio/process ] 108 | 109 | # Enable tokio timer functionality. Implements the Timer trait 110 | # it on tokio executors. 111 | # 112 | tokio_timer: [ tokio/time ] 113 | 114 | 115 | # Enables the wasm-bindgen-futures executor. Only available on Wasm. 116 | # If feature timer is enabled, this will implement Timer trait on Bindgen. 117 | # 118 | bindgen : [ wasm-bindgen-futures ] 119 | 120 | 121 | # Enabled the glommio executor. Linux only. Requires kernel 5.8+. 122 | # GlommioCt implements Timer and GlommioIo. 123 | # 124 | glommio : [ glommio_crate ] 125 | 126 | 127 | # Add support for the futures LocalPool to SpawnHandle and LocalSpawnHandle. 128 | # LocalPool will implement Timer if the timer feature is enabled. 129 | # 130 | localpool: [ futures-executor ] 131 | 132 | # Add support for the futures ThreadPool to SpawnHandle and LocalSpawnHandle. 133 | # ThreadPool will implement Timer if the timer feature is enabled. 134 | # 135 | threadpool: [ futures-executor/thread-pool ] 136 | 137 | 138 | 139 | # only used internally, don't use 140 | # 141 | notwasm: [] 142 | 143 | 144 | badges: 145 | 146 | maintenance : { status : actively-developed } 147 | travis-ci : { repository : najamelan/async_executors } 148 | 149 | 150 | dependencies: 151 | 152 | # Public deps 153 | # 154 | futures-core : { version: ^0.3 } 155 | futures-task : { version: ^0.3 } 156 | futures-util : { version: ^0.3, features: [ channel ] } 157 | futures-executor : { version: ^0.3, optional: true } 158 | futures-timer : { version: ^3 , optional: true } 159 | tracing-futures : { version: ^0.2, optional: true, features: [futures-03] } 160 | glommio_crate : { version: ^0.8, optional: true, package: glommio } 161 | tokio : { version: ^1 , optional: true } 162 | 163 | # Private deps 164 | # 165 | async-global-executor : { version: ^2 , optional: true, default-features: false } 166 | async_std_crate : { version: ^1.6 , optional: true, package: async-std, features: [ unstable ] } 167 | pin-project : ^1 168 | blanket : ^0.3 169 | 170 | # necessary for the crate to compile for `cargo doc` 171 | # 172 | wasm-bindgen-futures: { version: ^0.4, optional: true } 173 | 174 | 175 | dev-dependencies: 176 | 177 | static_assertions : ^1 178 | futures : { version: ^0.3 , features: [ thread-pool ] } 179 | tracing-subscriber: { version: ^0.3.5, default-features: false, features: [ fmt ] } 180 | tracing_crate : { version: ^0.1 , package: tracing } 181 | trait-set : ^0.3 182 | tokio : { version: ^1 , features: [macros, rt] } 183 | 184 | 185 | build-dependencies: 186 | 187 | rustc_version: ^0.4 188 | 189 | 190 | profile: 191 | 192 | release: 193 | 194 | codegen-units: 1 195 | 196 | 197 | target: 198 | 199 | 'cfg(target_arch = "wasm32")': 200 | 201 | 202 | dependencies: 203 | 204 | futures-timer: { version: ^3, features: [wasm-bindgen], optional: true } 205 | 206 | dev-dependencies: 207 | 208 | wasm-bindgen-test: ^0.3 209 | 210 | 211 | 212 | 'cfg(not(target_os = "unknown"))': 213 | 214 | dev-dependencies: 215 | 216 | tokio : { version: ^1, features: [ io-util, net, rt-multi-thread ] } 217 | futures-timer : ^3 218 | 219 | 220 | 221 | example: 222 | 223 | - name : async_std 224 | path : examples/async_std.rs 225 | required-features: [ notwasm, async_std ] 226 | 227 | - name : tokio_ct 228 | path : examples/tokio_ct.rs 229 | required-features: [ notwasm, tokio_ct ] 230 | 231 | - name : tokio_tp 232 | path : examples/tokio_tp.rs 233 | required-features: [ notwasm, tokio_tp ] 234 | 235 | - name : spawn_handle_multi 236 | path : examples/spawn_handle_multi.rs 237 | required-features: [ notwasm, async_std ] 238 | 239 | - name : trait_set 240 | path : examples/trait_set.rs 241 | required-features: [ notwasm, async_std ] 242 | 243 | - name : tracing 244 | path : examples/tracing.rs 245 | required-features: [ notwasm, tracing, async_std ] 246 | 247 | - name : glommio_ct 248 | path : examples/glommio_ct.rs 249 | required-features: [ notwasm, glommio ] 250 | 251 | - name : timer 252 | path : examples/timer.rs 253 | required-features: [ notwasm, async_std, tokio_ct, tokio_timer ] 254 | 255 | - name : tokio_io 256 | path : examples/tokio_io.rs 257 | required-features: [ notwasm, async_global_tokio, async_std_tokio, tokio_ct, tokio_io ] 258 | 259 | - name : yield_now 260 | path : examples/yield_now.rs 261 | required-features: [ notwasm, tokio_ct ] 262 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async_executors 2 | 3 | [![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) 4 | [![Build Status](https://api.travis-ci.org/najamelan/async_executors.svg?branch=master)](https://travis-ci.org/najamelan/async_executors) 5 | [![Docs](https://docs.rs/async_executors/badge.svg)](https://docs.rs/async_executors) 6 | [![crates.io](https://img.shields.io/crates/v/async_executors.svg)](https://crates.io/crates/async_executors) 7 | 8 | 9 | > Abstract over different executors. 10 | 11 | _async_executors_ aims to help you write executor agnostic libraries. We express common executor functionality in traits and implement it for the most used executors. This way libraries can require the exact functionality they need and client code can use any executor they choose as long as it can provide the required functionality. 12 | 13 | Available traits are grouped in the [`iface`] module. We also implement [`Spawn`](futures_util::task::Spawn) and/or [`LocalSpawn`](futures_util::task::LocalSpawn) traits from _futures_. 14 | 15 | All supported executors are turned on with features, see below. 16 | 17 | 18 | ## Table of Contents 19 | 20 | - [Install](#install) 21 | - [Upgrade](#upgrade) 22 | - [Dependencies](#dependencies) 23 | - [Security](#security) 24 | - [Features](#features) 25 | - [General Features](#general-features) 26 | - [Executor Specific](#executor-specific) 27 | - [Usage](#usage) 28 | - [Basic Example](#basic-example) 29 | - [API](#api) 30 | - [Contributing](#contributing) 31 | - [Code of Conduct](#code-of-conduct) 32 | - [License](#license) 33 | 34 | 35 | ## Install 36 | 37 | With [cargo add](https://github.com/killercup/cargo-edit): 38 | `cargo add async_executors` 39 | 40 | With [cargo yaml](https://gitlab.com/storedbox/cargo-yaml): 41 | ```yaml 42 | dependencies: 43 | 44 | async_executors: ^0.7 45 | ``` 46 | 47 | With Cargo.toml 48 | ```toml 49 | [dependencies] 50 | 51 | async_executors = "0.7" 52 | ``` 53 | 54 | ### Upgrade 55 | 56 | Please check out the [changelog](https://github.com/najamelan/async_executors/blob/master/CHANGELOG.md) when upgrading. 57 | 58 | 59 | ### Dependencies 60 | 61 | This crate has few dependencies. Cargo will automatically handle it's dependencies for you. 62 | 63 | The only hard dependencies are `futures-task` and `futures-util`. The rest are the optional dependencies to turn on support for each executor. 64 | 65 | ## Features 66 | 67 | This crate has a lot of features. Lets go over them: 68 | 69 | ### General features 70 | - `tracing`: when enabled, all traits are re-implemented for [`tracing-futures::Instrumented`] and [`tracing-futures::WithDispatch`]. 71 | - `timer` : Turns on the _futures-timer_ crate. This enables executors to async sleep. On _tokio_, alternatively you can enable `tokio_timer` to enable the tokio native timer. _async_std_, when not on wasm, provides a timer without needing this feature. 72 | 73 | ### Executor specific: 74 | - `async_global` : Turns on the executor from [_async-global-executor_](https://docs.rs/async-global-executor). 75 | Supports Wasm, `!Send` tasks. 76 | - `async_global_tokio`: Makes sure a tokio reactor is running for tasks spawned on [`AsyncGlobal`]. [`AsyncGlobal`] will implement the [`TokioIo`] trait. 77 | - `async_std` : Turns on the executor from the [_async-std_](https://docs.rs/async-std) crate. Supports Wasm and `!Send` tasks. 78 | - `async_std_tokio` : Makes sure a tokio reactor is running for tasks spawned on [`AsyncStd`]. [`AsyncStd`] will implement the [`TokioIo`] trait. 79 | - `glommio` : Turns on the executor from the [_glommio_](https://docs.rs/glommio) crate. Single threaded, Linux 5.8+ only. Supports `!Send` tasks. 80 | - `tokio_ct` : Tokio Current thread, enables a single threaded runtime from the [_tokio_](https://docs.rs/tokio) crate. Supports `!Send` tasks. 81 | - `tokio_tp` : Tokio threadpool, enables a threadpool runtime from the [_tokio_](https://docs.rs/tokio) crate. 82 | - `tokio_timer` : Will enable the `time` feature on _tokio_ and call `enable_time()` on any tokio runtimes you create. For tokio runtimes, this takes precedence over the `timer` feature. 83 | - `tokio_io` : Will enable the `net` and `process` features on _tokio_ and call `enable_reactor()` on any tokio runtimes you create. [`TokioCt`] and [`TokioTp`] will implement the [`TokioIo`] trait. 84 | - `localpool` : Enables the single threaded executor from [_futures-executor_](http://docs.rs/futures-executor). Supports `!Send` tasks. `LocalPool` and `LocalSpawner` will be re-exported from this crate and have our traits implemented. 85 | - `threadpool` : Enables the treadpool executor from [_futures-executor_](http://docs.rs/futures-executor). `ThreadPool` will be re-exported from this crate and have our traits implemented. 86 | - `bindgen` : Enables the single threaded executor from [_wasm-bindgen-futures_](https://docs.rs/wasm-bindgen-futures). Wasm only. Supports `!Send` tasks. 87 | 88 | 89 | 90 | ## Security 91 | 92 | The crate itself uses `#[ forbid(unsafe_code) ]`. 93 | 94 | Our dependencies use unsafe. 95 | 96 | 97 | ## Performance 98 | 99 | Most wrappers are very thin but the `Spawn` and `LocalSpawn` traits do imply boxing the future. With executors boxing futures 100 | to put them in a queue you probably get 2 heap allocations per spawn. 101 | 102 | `JoinHandle` uses the native `JoinHandle` types from _tokio_ and _async-std_ to avoid the overhead from `RemoteHandle`, but for _async-std_, wrap the future in `Abortable` to create consistent behavior across all executors. The `JoinHandle` provided cancels it's future on drop unless you call `detach` on it. 103 | 104 | `SpawnHandle` and `LocalSpawnHandle` require boxing the future twice, just like `Spawn` and `LocalSpawn`. 105 | 106 | Existing benchmarks for all executors can be found in [executor_benchmarks](https://github.com/najamelan/executor_benchmarks). 107 | 108 | 109 | ## Usage 110 | 111 | ### For API providers 112 | 113 | When writing a library that needs to spawn, you probably shouldn't lock your clients into one framework or another. It's usually not appropriate to setup your own thread pool for spawning futures. It belongs to the application developer to decide where futures are spawned and it might not be appreciated if libraries bring in extra dependencies on a framework. 114 | 115 | In order to get round this you can take an executor in as a parameter from client code and spawn your futures on the provided executor. Currently the only two traits that are kind of widely available for this are `Spawn` and `LocalSpawn` from the _futures_ library. Unfortunately, other executor providers do not implement these traits. So by publishing an API that relies on these traits, you would have been restricting the clients to use the executors from _futures_, or start implementing their own wrappers that implement the traits. 116 | 117 | _Async_executors_ has wrappers providing impls on various executors, namely _tokio_, _async_std_, _wasm_bindgen_, ... As such you can just use the trait bounds and refer your users to this crate if they want to use any of the supported executors. 118 | 119 | All wrappers also implement `Clone`, `Debug` and the zero sized ones also `Copy`. You can express you will need to clone in your API: `impl Spawn + Clone`. 120 | 121 | Note that you should never use `block_on` inside async contexts. Depending on the executor, this might hang or panic. Some backends we use like _tokio_ and `RemoteHandle` from _futures_ use `catch_unwind`, so try to keep futures unwind safe. 122 | 123 | #### Spawning with handles 124 | 125 | You can use the `SpawnHandle` and `LocalSpawnHandle` traits as bounds for obtaining join handles. 126 | 127 | ##### Example 128 | 129 | ```rust, ignore 130 | use 131 | { 132 | async_executors :: { JoinHandle, SpawnHandle, SpawnHandleExt } , 133 | std :: { sync::Arc } , 134 | futures :: { FutureExt, executor::{ ThreadPool, block_on } } , 135 | }; 136 | 137 | 138 | // Example of a library function that needs an executor. Just use impl Trait. 139 | // 140 | fn needs_exec( exec: impl SpawnHandle<()> ) 141 | { 142 | let handle = exec.spawn_handle( async {} ); 143 | } 144 | 145 | 146 | // A type that needs to hold on to an executor during it's lifetime. Here it 147 | // must be heap allocated. 148 | // 149 | struct SomeObj{ exec: Arc< dyn SpawnHandle > } 150 | 151 | 152 | impl SomeObj 153 | { 154 | pub fn new( exec: Arc< dyn SpawnHandle > ) -> SomeObj 155 | { 156 | SomeObj{ exec } 157 | } 158 | 159 | fn run( &self ) -> JoinHandle 160 | { 161 | self.exec.spawn_handle( async{ 5 } ).expect( "spawn" ) 162 | } 163 | } 164 | 165 | 166 | fn main() 167 | { 168 | let exec = ThreadPool::new().expect( "build threadpool" ); 169 | let obj = SomeObj::new( Arc::new(exec) ); 170 | 171 | let x = block_on( obj.run() ); 172 | 173 | assert_eq!( x, 5 ); 174 | } 175 | ``` 176 | 177 | As you can see from the above example, the output of the future is a type parameter on `SpawnHandle`. This is necessary because putting it on the method would make the trait no longer object safe, which means it couldn't be stored unless as a type parameter. 178 | 179 | The best way to define a combination of abilities you need is by making your own trait alias (here shown with a macro from the [trait_set](https://docs.rs/trait-set/) crate, but you can write it out yourself with a blanket implementation if you want): 180 | 181 | ```rust, ignore 182 | use async_executors::*; 183 | 184 | trait_set::trait_set! 185 | { 186 | pub trait LibExec = SpawnHandle<()> + SpawnHandle + Timer + YieldNow + Clone; 187 | } 188 | 189 | pub fn lib_function( _exec: impl LibExec ) {} 190 | 191 | ``` 192 | 193 | All implementers of `SpawnHandle` must support any output type. Thus adding more `SpawnHandle` bounds to `LibExec` should not be a breaking change. 194 | 195 | 196 | ### For API consumers 197 | 198 | You can basically pass the wrapper types provided in _async_executors_ to API's that take any of the following. Traits are also implemented for `Rc`, `Arc`, `&`, `&mut`, `Box` and `Instrumented` and `WithDispatch` from _tracing-futures_ wrappers: 199 | 200 | - `impl Spawn` 201 | - `impl LocalSpawn` 202 | - `impl SpawnHandle` 203 | - `impl LocalSpawnHandle` 204 | - `impl SpawnBlocking` 205 | - `impl YieldNow` 206 | - `impl Timer` 207 | - `impl TokioIo` 208 | 209 | All wrappers also implement `Clone`, `Debug` and the zero sized ones also `Copy`. 210 | 211 | Some executors are a bit special, so make sure to check the API docs for the one you intend to use. Some also provide extra methods like `block_on` which will call a framework specific `block_on` rather than the one from _futures_. 212 | 213 | #### Example 214 | 215 | ```rust, ignore 216 | use 217 | { 218 | async_executors :: { AsyncStd, SpawnHandle, TokioTp } , 219 | std :: { convert::TryFrom } , 220 | }; 221 | 222 | fn needs_exec( exec: impl SpawnHandle<()> + SpawnHandle ){}; 223 | 224 | // AsyncStd is zero sized, so it's easy to instantiate. 225 | // 226 | needs_exec( AsyncStd ); 227 | 228 | // We need a builder type for tokio, as we guarantee by the type of TokioTp that it 229 | // will be a threadpool. 230 | // 231 | let tp = TokioTp::new().expect( "build threadpool" ); 232 | 233 | needs_exec( tp ); 234 | ``` 235 | 236 | For more examples, check out the [examples directory](https://github.com/najamelan/async_executors/tree/master/examples). If you want to get a more polished API for adhering to structured concurrency, check out [_async_nursery_](https://crates.io/crates/async_nursery). 237 | 238 | ## API 239 | 240 | API documentation can be found on [docs.rs](https://docs.rs/async_executors). 241 | 242 | 243 | ## Contributing 244 | 245 | Please check out the [contribution guidelines](https://github.com/najamelan/async_executors/blob/master/CONTRIBUTING.md). 246 | 247 | 248 | ### Testing 249 | 250 | Run `ci/test.bash` and `ci/wasm.bash` to run all tests. 251 | 252 | 253 | ### Code of conduct 254 | 255 | Any of the behaviors described in [point 4 "Unacceptable Behavior" of the Citizens Code of Conduct](https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md#4-unacceptable-behavior) are not welcome here and might get you banned. If anyone including maintainers and moderators of the project fail to respect these/your limits, you are entitled to call them out. 256 | 257 | ## License 258 | 259 | [Unlicence](https://unlicense.org/) 260 | 261 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO: 2 | 3 | - clarify in docs and cargo.yml that it is LocalSpawner that is the executor and not LocalPool. 4 | - async-io also provides a timer. So maybe in async-global-executor if async-io is already enabled, we should use 5 | that instead of futures-timer. 6 | - implement Timer for futures-timer? 7 | - verify unwind safety of all our public types and make sure the traits are correctly implemented or not. 8 | - pass on traits to Nursery in async_nursery where possible. 9 | 10 | - test for JoinHandle being Send when Out is Send. Currently was caught just by an example. 11 | - test what happens when creating 2 LocalExecutor in one thread. 12 | - glommio's CPU pinning. 13 | 14 | - should LocalSpawnHandle imply SpawnHandle? If you can spawn a !Send future, normally you can always spawn a Send one. 15 | It would mean that API's that take in a LocalSpawnHandle can also use spawn_handle. Eg. nursery can impl Nurse also on 16 | an executor that is `impl LocalSpawnHandle`. What does futures do with Spawn and LocalSpawn? 17 | 18 | - wrapping the executors of the futures library would make it easier to interop with TokioCt if they were wrapped and we put block_on on the wrapper for consistent api. For running entire test suits on different executors for example. That is because with tokio ct you have to call block_on. 19 | 20 | 21 | # Wrap up 22 | 23 | - CI - fix windows wasm testing 24 | 25 | 26 | ## Consistent behavior: 27 | 28 | The anwser to each point here needs to be the same for all supported executors and the ones from the futures library. 29 | 30 | ### Spawning 31 | 32 | ✔ what happens if the spawned future panics? 33 | 34 | ✔ on all executors except tokio, the executor thread will unwind. Tokio uses catch_unwind and 35 | the fact that the future panicked can only be observed by awaiting the joinhandle, but 36 | the Spawn and LocalSpawn traits do not return a JoinHandle, so there is no way to tell. 37 | 38 | ✔ is spawning fallible or infallible? 39 | We turn everything to fallible in line with the futures executors. 40 | 41 | ### JoinHandle 42 | 43 | ✔ provide both detach and drop. 44 | ✔ what happens if the joinhandle get's dropped. 45 | ✔ what happens if the future panics. 46 | 47 | ✔ remote handle unwinds the thread that is awaiting the handle 48 | ✔ async_std unwinds both 49 | ✔ tokio unwinds none. 50 | 51 | I brought tokio in line with remote_handle, but async_std will unwind the executor thread. This inconsistency remains. 52 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // Detect the rustc channel 2 | // 3 | use rustc_version::{ version_meta, Channel }; 4 | 5 | fn main() 6 | { 7 | // Set cfg flags depending on release channel 8 | // 9 | match version_meta().unwrap().channel 10 | { 11 | Channel::Stable => println!( "cargo:rustc-cfg=stable" ), 12 | Channel::Beta => println!( "cargo:rustc-cfg=beta" ), 13 | Channel::Nightly => println!( "cargo:rustc-cfg=nightly" ), 14 | Channel::Dev => println!( "cargo:rustc-cfg=rustc_dev" ), 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ci/clippy.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # fail fast 4 | # 5 | set -e 6 | 7 | # print each command before it's executed 8 | # 9 | set -x 10 | 11 | cargo clippy --tests --examples --benches --all-features -- -D warnings 12 | 13 | rustup target add wasm32-unknown-unknown 14 | cargo clippy --tests --target wasm32-unknown-unknown --features "bindgen timer async_std async_global" -- -D warnings 15 | -------------------------------------------------------------------------------- /ci/doc.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # fail fast 4 | # 5 | set -e 6 | 7 | # print each command before it's executed 8 | # 9 | set -x 10 | 11 | export RUSTFLAGS="-D warnings" 12 | 13 | # only works on nightly because of features like doc_cfg and external_doc 14 | # 15 | cargo doc --all-features --no-deps 16 | cargo test --doc --all-features 17 | -------------------------------------------------------------------------------- /ci/test.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # fail fast 4 | # 5 | set -e 6 | 7 | # print each command before it's executed 8 | # 9 | set -x 10 | 11 | export RUSTFLAGS="-D warnings" 12 | 13 | cargo check 14 | cargo check --features async_global 15 | cargo check --features async_std 16 | cargo check --features glommio 17 | cargo check --features tokio_tp 18 | cargo check --features tokio_ct 19 | cargo check --features localpool 20 | cargo check --features threadpool 21 | 22 | 23 | # Do not try to test glommio on github actions. Glommio requires the rlimit to be raised, but we found 24 | # no proper way to get it working. 25 | # 26 | if [ -z "$GITHUB_ACTION" ] 27 | then 28 | 29 | # Currently doc tests in readme will fail without all features, because we have no way of turning on 30 | # the features for the doctest. 31 | # 32 | cargo test --all-features 33 | 34 | # All executors but nothing extra. 35 | # 36 | cargo test --features "async_global async_std bindgen localpool threadpool tokio_ct tokio_tp glommio" 37 | 38 | # timer 39 | # 40 | cargo test --features "timer async_global async_std localpool threadpool tokio_ct tokio_tp glommio" 41 | 42 | else # CI 43 | 44 | # all features without glommio: 45 | # 46 | cargo test --features "async_global async_global_tokio async_std async_std_tokio localpool threadpool tokio_ct tokio_tp tokio_io tokio_timer timer tracing bindgen notwasm" 47 | 48 | cargo test --features "async_global async_std bindgen localpool threadpool tokio_ct tokio_tp" 49 | cargo test --features "timer async_global async_std localpool threadpool tokio_ct tokio_tp" 50 | fi 51 | 52 | 53 | cargo test --features "tokio_io async_global async_std tokio_ct tokio_tp" 54 | cargo test --features "tokio_timer tokio_ct tokio_tp" 55 | -------------------------------------------------------------------------------- /ci/ulimit_memlock.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # fail fast 4 | # 5 | set -e 6 | 7 | # print each command before it's executed 8 | # 9 | set -x 10 | 11 | # Required for io_uring. 12 | # 13 | sudo bash -c "ulimit -l 512" 14 | -------------------------------------------------------------------------------- /ci/wasm.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # fail fast 4 | # 5 | set -e 6 | 7 | # print each command before it's executed 8 | # 9 | set -x 10 | 11 | export RUSTFLAGS="-D warnings" 12 | 13 | # --no-default-features is needed to turn of notwasm so this won't try to compile examples 14 | # features don't work in wasm-pack, so using cargo test directly here 15 | # 16 | wasm-pack test --firefox --headless -- --no-default-features 17 | wasm-pack test --firefox --headless -- --no-default-features --features "bindgen" 18 | wasm-pack test --firefox --headless -- --no-default-features --features "async_std" 19 | wasm-pack test --firefox --headless -- --no-default-features --features "async_global" 20 | wasm-pack test --firefox --headless -- --no-default-features --features "timer bindgen async_std async_global" 21 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | # This template contains all of the possible sections and their default values 2 | 3 | # Note that all fields that take a lint level have these possible values: 4 | # * deny - An error will be produced and the check will fail 5 | # * warn - A warning will be produced, but the check will not fail 6 | # * allow - No warning or error will be produced, though in some cases a note 7 | # will be 8 | 9 | # The values provided in this template are the default values that will be used 10 | # when any section or field is not specified in your own configuration 11 | 12 | # If 1 or more target triples (and optionally, target_features) are specified, 13 | # only the specified targets will be checked when running `cargo deny check`. 14 | # This means, if a particular package is only ever used as a target specific 15 | # dependency, such as, for example, the `nix` crate only being used via the 16 | # `target_family = "unix"` configuration, that only having windows targets in 17 | # this list would mean the nix crate, as well as any of its exclusive 18 | # dependencies not shared by any other crates, would be ignored, as the target 19 | # list here is effectively saying which targets you are building for. 20 | targets = [ 21 | # The triple can be any string, but only the target triples built in to 22 | # rustc (as of 1.40) can be checked against actual config expressions 23 | #{ triple = "x86_64-unknown-linux-musl" }, 24 | # You can also specify which target_features you promise are enabled for a 25 | # particular target. target_features are currently not validated against 26 | # the actual valid features supported by the target architecture. 27 | #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, 28 | ] 29 | 30 | # This section is considered when running `cargo deny check advisories` 31 | # More documentation for the advisories section can be found here: 32 | # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html 33 | [advisories] 34 | # The path where the advisory database is cloned/fetched into 35 | db-path = "~/.cargo/advisory-db" 36 | # The url of the advisory database to use 37 | db-urls = [ "https://github.com/rustsec/advisory-db" ] 38 | # The lint level for security vulnerabilities 39 | vulnerability = "deny" 40 | # The lint level for unmaintained crates 41 | unmaintained = "warn" 42 | # The lint level for crates that have been yanked from their source registry 43 | yanked = "warn" 44 | # The lint level for crates with security notices. Note that as of 45 | # 2019-12-17 there are no security notice advisories in 46 | # https://github.com/rustsec/advisory-db 47 | notice = "warn" 48 | # A list of advisory IDs to ignore. Note that ignored advisories will still 49 | # output a note when they are encountered. 50 | ignore = [ 51 | #"RUSTSEC-0000-0000", 52 | ] 53 | # Threshold for security vulnerabilities, any vulnerability with a CVSS score 54 | # lower than the range specified will be ignored. Note that ignored advisories 55 | # will still output a note when they are encountered. 56 | # * None - CVSS Score 0.0 57 | # * Low - CVSS Score 0.1 - 3.9 58 | # * Medium - CVSS Score 4.0 - 6.9 59 | # * High - CVSS Score 7.0 - 8.9 60 | # * Critical - CVSS Score 9.0 - 10.0 61 | #severity-threshold = 62 | 63 | # This section is considered when running `cargo deny check licenses` 64 | # More documentation for the licenses section can be found here: 65 | # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html 66 | [licenses] 67 | # The lint level for crates which do not have a detectable license 68 | unlicensed = "deny" 69 | # List of explictly allowed licenses 70 | # See https://spdx.org/licenses/ for list of possible licenses 71 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. 72 | allow = [ 73 | "MIT", 74 | "Apache-2.0", 75 | # "Apache-2.0 WITH LLVM-exception", 76 | "Unlicense", 77 | ] 78 | # List of explictly disallowed licenses 79 | # See https://spdx.org/licenses/ for list of possible licenses 80 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. 81 | deny = [ 82 | #"Nokia", 83 | ] 84 | # Lint level for licenses considered copyleft 85 | copyleft = "allow" 86 | # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses 87 | # * both - The license will be approved if it is both OSI-approved *AND* FSF 88 | # * either - The license will be approved if it is either OSI-approved *OR* FSF 89 | # * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF 90 | # * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved 91 | # * neither - This predicate is ignored and the default lint level is used 92 | allow-osi-fsf-free = "either" 93 | # Lint level used when no other predicates are matched 94 | # 1. License isn't in the allow or deny lists 95 | # 2. License isn't copyleft 96 | # 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" 97 | default = "deny" 98 | # The confidence threshold for detecting a license from license text. 99 | # The higher the value, the more closely the license text must be to the 100 | # canonical license text of a valid SPDX license file. 101 | # [possible values: any between 0.0 and 1.0]. 102 | confidence-threshold = 0.8 103 | # Allow 1 or more licenses on a per-crate basis, so that particular licenses 104 | # aren't accepted for every possible crate as with the normal allow list 105 | exceptions = [ 106 | # Each entry is the crate and version constraint, and its specific allow 107 | # list 108 | #{ allow = ["Zlib"], name = "adler32", version = "*" }, 109 | ] 110 | 111 | # Some crates don't have (easily) machine readable licensing information, 112 | # adding a clarification entry for it allows you to manually specify the 113 | # licensing information 114 | #[[licenses.clarify]] 115 | # The name of the crate the clarification applies to 116 | #name = "ring" 117 | # THe optional version constraint for the crate 118 | #version = "*" 119 | # The SPDX expression for the license requirements of the crate 120 | #expression = "MIT AND ISC AND OpenSSL" 121 | # One or more files in the crate's source used as the "source of truth" for 122 | # the license expression. If the contents match, the clarification will be used 123 | # when running the license check, otherwise the clarification will be ignored 124 | # and the crate will be checked normally, which may produce warnings or errors 125 | # depending on the rest of your configuration 126 | #license-files = [ 127 | # Each entry is a crate relative path, and the (opaque) hash of its contents 128 | #{ path = "LICENSE", hash = 0xbd0eed23 } 129 | #] 130 | 131 | [licenses.private] 132 | # If true, ignores workspace crates that aren't published, or are only 133 | # published to private registries 134 | ignore = false 135 | # One or more private registries that you might publish crates to, if a crate 136 | # is only published to private registries, and ignore is true, the crate will 137 | # not have its license(s) checked 138 | registries = [ 139 | #"https://sekretz.com/registry 140 | ] 141 | 142 | # This section is considered when running `cargo deny check bans`. 143 | # More documentation about the 'bans' section can be found here: 144 | # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html 145 | [bans] 146 | # Lint level for when multiple versions of the same crate are detected 147 | multiple-versions = "warn" 148 | # The graph highlighting used when creating dotgraphs for crates 149 | # with multiple versions 150 | # * lowest-version - The path to the lowest versioned duplicate is highlighted 151 | # * simplest-path - The path to the version with the fewest edges is highlighted 152 | # * all - Both lowest-version and simplest-path are used 153 | highlight = "all" 154 | # List of crates that are allowed. Use with care! 155 | allow = [ 156 | #{ name = "ansi_term", version = "=0.11.0" }, 157 | ] 158 | # List of crates to deny 159 | deny = [ 160 | # Each entry the name of a crate and a version range. If version is 161 | # not specified, all versions will be matched. 162 | 163 | { name = "plutonium" }, # Crate intended to hide unsafe usage. 164 | { name = "fake-static" }, # Crate intended to demonstrate soundness bug in safe code. 165 | ] 166 | # Certain crates/versions that will be skipped when doing duplicate detection. 167 | skip = [ 168 | #{ name = "ansi_term", version = "=0.11.0" }, 169 | ] 170 | # Similarly to `skip` allows you to skip certain crates during duplicate 171 | # detection. Unlike skip, it also includes the entire tree of transitive 172 | # dependencies starting at the specified crate, up to a certain depth, which is 173 | # by default infinite 174 | skip-tree = [ 175 | #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, 176 | ] 177 | 178 | # This section is considered when running `cargo deny check sources`. 179 | # More documentation about the 'sources' section can be found here: 180 | # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html 181 | [sources] 182 | # Lint level for what to happen when a crate from a crate registry that is not 183 | # in the allow list is encountered 184 | unknown-registry = "warn" 185 | # Lint level for what to happen when a crate from a git repository that is not 186 | # in the allow list is encountered 187 | unknown-git = "warn" 188 | # List of URLs for allowed crate registries. Defaults to the crates.io index 189 | # if not specified. If it is specified but empty, no registries are allowed. 190 | allow-registry = ["https://github.com/rust-lang/crates.io-index"] 191 | # List of URLs for allowed Git repositories 192 | allow-git = [] 193 | -------------------------------------------------------------------------------- /examples/async_std.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | futures::task :: { Spawn, SpawnExt } , 4 | async_executors :: { AsyncStd } , 5 | }; 6 | 7 | 8 | fn lib_function( exec: impl Spawn ) 9 | { 10 | exec.spawn( async 11 | { 12 | println!( "I can spawn from a library" ); 13 | 14 | }).expect( "spawn task" ); 15 | } 16 | 17 | 18 | fn main() 19 | { 20 | lib_function( AsyncStd ); 21 | 22 | // Don't do this at home, but in a very basic example like here, the process might exit before 23 | // the future runs. Use join handles, or channels to synchronize in real code if you need to wait 24 | // for some task to finish. 25 | // 26 | std::thread::sleep( std::time::Duration::from_millis(10) ); 27 | } 28 | -------------------------------------------------------------------------------- /examples/glommio_ct.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | async_executors :: { GlommioCt } , 4 | futures::channel :: { oneshot, oneshot::Sender } , 5 | futures::task :: { Spawn, SpawnExt } , 6 | glommio_crate :: { LocalExecutorBuilder, Placement } , 7 | }; 8 | 9 | 10 | 11 | fn lib_function( exec: impl Spawn, tx: Sender<&'static str> ) 12 | { 13 | exec.spawn( async 14 | { 15 | tx.send( "I can spawn from a library" ).expect( "send string" ); 16 | 17 | }).expect( "spawn task" ); 18 | } 19 | 20 | 21 | 22 | fn main() 23 | { 24 | // You provide the builder, and async_executors will set the right scheduler. 25 | // Of course you can set other configuration on the builder before. 26 | // 27 | let builder = LocalExecutorBuilder::new( Placement::Unbound ); 28 | let exec = GlommioCt::new( builder ).expect( "create exec" ); 29 | 30 | let program = async 31 | { 32 | let (tx, rx) = oneshot::channel(); 33 | 34 | lib_function( &exec, tx ); 35 | 36 | println!( "{}", rx.await.expect( "receive on channel" ) ); 37 | }; 38 | 39 | exec.block_on( program ); 40 | } 41 | -------------------------------------------------------------------------------- /examples/spawn_handle_multi.rs: -------------------------------------------------------------------------------- 1 | //! An example showing how to take an executor in an API that can use SpawnHandle 2 | //! with multiple output types. 3 | //! 4 | //! You can also make your object generic over the executor. This shows how you can avoid that. 5 | // 6 | use 7 | { 8 | std :: { sync::Arc } , 9 | async_executors :: { AsyncStd, SpawnHandle, SpawnHandleExt } , 10 | }; 11 | 12 | 13 | // We create a custom trait and tell the compiler that it can only ever be implemented 14 | // when the receiver implements all of the SpawnHandle variants we need. 15 | // 16 | // See the trait_set example for a more ergonomic way of doing this. 17 | // 18 | pub trait CustomSpawnHandle : SpawnHandle + SpawnHandle + Send + Sync {} 19 | 20 | // A blanket impl for all types that fit our requirements. The compiler actually 21 | // recognises this as a proof that any dyn CustomSpawnHandle will also implement 22 | // SpawnHandle and SpawnHandle, so we can call methods even from 23 | // SpawnHandleExt on it. 24 | // 25 | impl CustomSpawnHandle for T 26 | 27 | where T: SpawnHandle + SpawnHandle + Send + Sync 28 | 29 | {} 30 | 31 | 32 | struct Connection 33 | { 34 | // Since it's hard to get a clone on a trait object, we just wrap it 35 | // in an Arc, that way we can pass it to child tasks. Spawning does 36 | // not require mutable access. 37 | // 38 | exec: Arc< dyn CustomSpawnHandle > , 39 | } 40 | 41 | 42 | impl Connection 43 | { 44 | pub fn new( exec: Arc< dyn CustomSpawnHandle > ) -> Self 45 | { 46 | Self { exec } 47 | } 48 | 49 | // Use the API we wanted, hooray. 50 | // 51 | pub async fn run( &self ) 52 | { 53 | let process_request = async { String::from( "Processing request" ) }; 54 | let other_request = async { 5 }; 55 | 56 | let request_handle = self.exec.spawn_handle( process_request ).expect( "spawn process_request" ); 57 | let other_handle = self.exec.spawn_handle( other_request ).expect( "spawn other_request" ); 58 | 59 | println!( "A string from process_request: {}", request_handle.await ); 60 | println!( "A u8 from other_request: {}" , other_handle .await ); 61 | } 62 | } 63 | 64 | 65 | fn main() 66 | { 67 | let exec = Arc::new( AsyncStd ); 68 | let ex2 = exec.clone(); 69 | 70 | // We can't just spawn conn.run(), because it takes &self, but a spawned 71 | // task needs to be 'static. Since conn now lives inside the async task, 72 | // we don't borrow a local variable from our main function. The compiler 73 | // can assert that conn lives at least as long as the task itself. 74 | // 75 | let task = async move 76 | { 77 | let conn = Connection::new( ex2 ); 78 | 79 | conn.run().await; 80 | }; 81 | 82 | 83 | let join_handle = exec.spawn_handle( task ).expect( "spawn task" ); 84 | 85 | AsyncStd::block_on( join_handle ); 86 | } 87 | -------------------------------------------------------------------------------- /examples/timer.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates how to use an agnostic timer. 2 | //! When you specify the Timer trait, the client must give you an executor 3 | //! that supports timer operations. 4 | //! 5 | //! In this example, async-std draws it's timer from async-io and when not 6 | //! on wasm there currently is no way to turn it off. 7 | //! 8 | //! TokioCt can get it's timer from tokio with the tokio_timer feature, or 9 | //! from futures-timer if the timer feature is enabled. As we need to specify 10 | //! the required features for examples in Cargo.toml, you'll have to enable both. 11 | //! In reality you only need to choose one. 12 | //! 13 | //! cargo run --example timer --features "notwasm tokio_timer timer async_std tokio_ct" 14 | //! 15 | //! See the API docs for detailed explanation of how the features interact. 16 | //! 17 | //! Expected output: 18 | //! 19 | //! async-std: running for 1s. 20 | //! async-std: running for 2s. 21 | //! async-std: running for 3s. 22 | //! async-std: running for 4s. 23 | //! async-std: running for 5s. 24 | //! tokio current thread: running for 1s. 25 | //! tokio current thread: running for 2s. 26 | //! tokio current thread: running for 3s. 27 | //! tokio current thread: running for 4s. 28 | //! tokio current thread: running for 5s. 29 | // 30 | use 31 | { 32 | async_executors :: { AsyncStd, TokioCt, Timer, SpawnHandle } , 33 | trait_set :: { trait_set } , 34 | std :: { time::Duration } , 35 | }; 36 | 37 | pub type DynResult = Result >; 38 | 39 | 40 | trait_set! 41 | { 42 | pub trait LibExec = SpawnHandle<()> + Timer; 43 | } 44 | 45 | 46 | async fn lib_function( exec_name: &str, exec: impl LibExec ) 47 | { 48 | for i in 1..6 49 | { 50 | exec.sleep( Duration::from_secs(1) ).await; 51 | println!( "{}: running for {}s.", exec_name, i ); 52 | } 53 | } 54 | 55 | 56 | fn main() -> DynResult<()> 57 | { 58 | AsyncStd::block_on( lib_function( "async-std", AsyncStd ) ); 59 | 60 | let tokio_ct = TokioCt::new()?; 61 | 62 | tokio_ct.block_on( lib_function( "tokio current thread", tokio_ct.clone() ) ); 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /examples/tokio_ct.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | futures::task :: { LocalSpawn, LocalSpawnExt } , 4 | async_executors :: { TokioCt } , 5 | std :: { rc::Rc } , 6 | futures::channel :: { oneshot, oneshot::Sender } , 7 | }; 8 | 9 | 10 | fn lib_function( exec: impl LocalSpawn, tx: Sender ) 11 | { 12 | exec.spawn_local( async 13 | { 14 | let not_send = Rc::new(5); 15 | 16 | tx.send( format!( "I can spawn a !Send future from a library with tokio runtime: {}", ¬_send ) ).expect( "send string" ); 17 | 18 | }).expect( "spawn task" ); 19 | } 20 | 21 | 22 | fn main() 23 | { 24 | // If you need to configure tokio, you can use `tokio::runtimer::Builder` 25 | // to create your runtime and then create the `TokioCt` from it. 26 | // 27 | let exec = TokioCt::new().expect( "create tokio threadpool" ); 28 | 29 | let program = async 30 | { 31 | let (tx, rx) = oneshot::channel(); 32 | 33 | lib_function( &exec, tx ); 34 | println!( "{}", rx.await.expect( "receive on channel" ) ); 35 | }; 36 | 37 | exec.block_on( program ); 38 | } 39 | -------------------------------------------------------------------------------- /examples/tokio_io.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates how you can use Tokio io primitives on an 2 | //! agnostic executor. Use the `TokioIo` trait to request an executor 3 | //! that runs tasks in the context of a tokio reactor. 4 | //! 5 | //! The features `async_global_tokio` and `async_std_tokio` will turn on the 6 | //! corresponding features on the underlying libraries. 7 | //! 8 | //! The `tokio_io` feature will enable `net` and `process` on tokio and make 9 | //! sure the executor builder has `enable_reactor()`. 10 | //! 11 | //! Run with: 12 | //! 13 | //! cargo run --example tokio_io --features "tokio_ct, async_global_tokio, async_std_tokio, tokio_io" 14 | //! 15 | //! See the API docs for detailed explanation of how the features interact. 16 | //! 17 | //! Expected output: 18 | //! 19 | //! Testing tokio::net::TcpStream spawned from async-global-executor 20 | //! Testing tokio::net::TcpStream spawned from async-std 21 | //! Testing tokio::net::TcpStream spawned from tokio current thread 22 | // 23 | use 24 | { 25 | async_executors :: { AsyncGlobal, AsyncStd, TokioCt, TokioIo, SpawnHandle, SpawnHandleExt } , 26 | trait_set :: { trait_set } , 27 | tokio::net :: { TcpListener, TcpStream } , 28 | }; 29 | 30 | 31 | pub type DynResult = Result >; 32 | 33 | 34 | /// Creates a connected pair of sockets. 35 | /// Uses tokio tcp stream. This will only work if the reactor is running. 36 | // 37 | pub async fn socket_pair() -> DynResult< (TcpStream, TcpStream) > 38 | { 39 | // port 0 = let the OS choose 40 | // 41 | let listener = TcpListener::bind("127.0.0.1:0").await?; 42 | let stream1 = TcpStream::connect(listener.local_addr()?).await?; 43 | let stream2 = listener.accept().await?.0; 44 | 45 | Ok( (stream1, stream2) ) 46 | } 47 | 48 | 49 | trait_set! 50 | { 51 | pub trait LibExec = SpawnHandle<()> + TokioIo; 52 | } 53 | 54 | 55 | async fn lib_function( exec_name: &str, exec: impl LibExec ) -> DynResult<()> 56 | { 57 | println!( "Testing tokio::net::TcpStream spawned from {}", exec_name ); 58 | 59 | let test = async 60 | { 61 | // This will panic if the tokio reactor is not running. 62 | // 63 | let _ = socket_pair().await.expect( "socket_pair" ); 64 | }; 65 | 66 | exec.spawn_handle( test )?.await; 67 | 68 | Ok(()) 69 | } 70 | 71 | 72 | fn main() -> DynResult<()> 73 | { 74 | AsyncGlobal::block_on( lib_function( "async-global-executor", AsyncGlobal ) )?; 75 | AsyncStd ::block_on( lib_function( "async-std" , AsyncStd ) )?; 76 | 77 | let tokio_ct = &TokioCt::new()?; 78 | 79 | tokio_ct.block_on( lib_function( "tokio current thread", tokio_ct.clone() ) )?; 80 | 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /examples/tokio_tp.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | futures::task :: { Spawn, SpawnExt } , 4 | async_executors :: { TokioTp } , 5 | futures::channel :: { oneshot, oneshot::Sender } , 6 | }; 7 | 8 | 9 | fn lib_function( exec: impl Spawn, tx: Sender<&'static str> ) 10 | { 11 | exec.spawn( async 12 | { 13 | tx.send( "I can spawn from a library" ).expect( "send string" ); 14 | 15 | }).expect( "spawn task" ); 16 | } 17 | 18 | 19 | fn main() 20 | { 21 | // You provide the builder, and async_executors will set the right scheduler. 22 | // Of course you can set other configuration on the builder before. 23 | // 24 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 25 | 26 | let program = async 27 | { 28 | let (tx, rx) = oneshot::channel(); 29 | 30 | lib_function( &exec, tx ); 31 | println!( "{}", rx.await.expect( "receive on channel" ) ); 32 | }; 33 | 34 | exec.block_on( program ); 35 | } 36 | -------------------------------------------------------------------------------- /examples/tracing.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | async_executors :: { AsyncStd, SpawnHandle, SpawnHandleExt } , 4 | tracing_futures :: { Instrument } , 5 | tracing_crate as tracing , 6 | }; 7 | 8 | 9 | async fn lib_function( exec: impl SpawnHandle<()> ) 10 | { 11 | exec.spawn_handle( async 12 | { 13 | tracing::info!( "I can spawn from a library" ); 14 | 15 | }).expect( "spawn task" ).await; 16 | } 17 | 18 | 19 | 20 | fn main() 21 | { 22 | tracing_subscriber::fmt::Subscriber::builder() 23 | 24 | .with_max_level( tracing::Level::INFO ) 25 | .init() 26 | ; 27 | 28 | let exec = AsyncStd.instrument( tracing::info_span!( "tracing-example" ) ); 29 | 30 | AsyncStd::block_on( lib_function(exec) ); 31 | 32 | tracing::info!( "end of main, not instrumented." ); 33 | } 34 | -------------------------------------------------------------------------------- /examples/trait_set.rs: -------------------------------------------------------------------------------- 1 | //! An example showing how to take an executor in an API that can use SpawnHandle 2 | //! with multiple output types. This one uses the trait-set crate to avoid writing the blanket impl. 3 | //! 4 | //! You can also make your object generic over the executor. This shows how you can avoid that. 5 | // 6 | use 7 | { 8 | std :: { sync::Arc } , 9 | async_executors :: { AsyncStd, SpawnHandle, SpawnHandleExt } , 10 | trait_set :: { trait_set } , 11 | }; 12 | 13 | 14 | // We create a custom trait and tell the compiler that it can only ever be implemented 15 | // when the receiver implements all of the SpawnHandle variants we need. 16 | // 17 | trait_set! 18 | { 19 | pub trait CustomSpawnHandle = SpawnHandle + SpawnHandle + Send + Sync; 20 | } 21 | 22 | 23 | struct Connection 24 | { 25 | // Since it's hard to get a clone on a trait object, we just wrap it 26 | // in an Arc, that way we can pass it to child tasks. Spawning does 27 | // not require mutable access. 28 | // 29 | exec: Arc< dyn CustomSpawnHandle > , 30 | } 31 | 32 | 33 | impl Connection 34 | { 35 | pub fn new( exec: Arc< dyn CustomSpawnHandle > ) -> Self 36 | { 37 | Self { exec } 38 | } 39 | 40 | // Use the API we wanted, hooray. 41 | // 42 | pub async fn run( &self ) 43 | { 44 | let process_request = async { String::from( "Processing request" ) }; 45 | let other_request = async { 5 }; 46 | 47 | let request_handle = self.exec.spawn_handle( process_request ).expect( "spawn process_request" ); 48 | let other_handle = self.exec.spawn_handle( other_request ).expect( "spawn other_request" ); 49 | 50 | println!( "A string from process_request: {}", request_handle.await ); 51 | println!( "A u8 from other_request: {}" , other_handle .await ); 52 | } 53 | } 54 | 55 | 56 | fn main() 57 | { 58 | let exec = Arc::new( AsyncStd ); 59 | let ex2 = exec.clone(); 60 | 61 | // We can't just spawn conn.run(), because it takes &self, but a spawned 62 | // task needs to be 'static. Since conn now lives inside the async task, 63 | // we don't borrow a local variable from our main function. The compiler 64 | // can assert that conn lives at least as long as the task itself. 65 | // 66 | let task = async move 67 | { 68 | let conn = Connection::new( ex2 ); 69 | 70 | conn.run().await; 71 | }; 72 | 73 | 74 | let join_handle = exec.spawn_handle( task ).expect( "spawn task" ); 75 | 76 | AsyncStd::block_on( join_handle ); 77 | } 78 | -------------------------------------------------------------------------------- /examples/yield_now.rs: -------------------------------------------------------------------------------- 1 | //! This example shows how you can yield to an executor. In this case we can count on 2 | //! the other task that is waiting to run first. Obviously that will only work on a 3 | //! single threaded executor. 4 | //! 5 | //! In any case yield_now will allow other tasks to run if they are waiting. 6 | //! 7 | // 8 | use 9 | { 10 | async_executors :: { TokioCt, SpawnHandle, SpawnHandleExt, YieldNow } , 11 | std :: { sync::{ atomic::{ AtomicBool, Ordering::SeqCst }, Arc } } , 12 | }; 13 | 14 | 15 | pub type DynResult = Result< T, Box >; 16 | 17 | 18 | 19 | // Use same exec to run this function as you pass in. 20 | // 21 | pub async fn lib_function( exec: impl SpawnHandle<()> + YieldNow ) -> DynResult<()> 22 | { 23 | let flag = Arc::new( AtomicBool::new( false ) ); 24 | let flag2 = flag.clone(); 25 | 26 | let task = async move 27 | { 28 | flag2.store( true, SeqCst ); 29 | println!( "I am subtask" ); 30 | }; 31 | 32 | let handle = exec.spawn_handle( task )?; 33 | 34 | println!( "I am yielding" ); 35 | exec.yield_now().await; 36 | 37 | // by now task should have run because of the yield_now. 38 | // 39 | assert!( flag.load(SeqCst) ); 40 | 41 | handle.await; 42 | 43 | Ok(()) 44 | } 45 | 46 | 47 | 48 | fn main() -> DynResult<()> 49 | { 50 | let exec = TokioCt::new().expect( "create tokio threadpool" ); 51 | 52 | exec.block_on( lib_function( &exec ) ) 53 | } 54 | -------------------------------------------------------------------------------- /src/exec/async_global.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | crate :: { SpawnHandle, LocalSpawnHandle, JoinHandle } , 4 | futures_task :: { FutureObj, LocalFutureObj, Spawn, LocalSpawn, SpawnError } , 5 | 6 | async_global_executor as async_global, 7 | }; 8 | 9 | 10 | /// An executor that spawns tasks on async-global-executor. In contrast to the other executors, this one 11 | /// is not self contained, because async-global-executor does not provide an API that allows that, 12 | /// so the threadpool is global. 13 | /// 14 | /// It works on Wasm. 15 | // 16 | #[ derive( Copy, Clone, Default ) ] 17 | // 18 | #[ cfg_attr( nightly, doc(cfg( feature = "async_global" )) ) ] 19 | // 20 | pub struct AsyncGlobal; 21 | 22 | impl AsyncGlobal 23 | { 24 | /// Create a new AsyncGlobal wrapper, forwards to `Default::default`. 25 | /// 26 | pub fn new() -> Self 27 | { 28 | Self 29 | } 30 | 31 | 32 | /// Wrapper around [async_global_executor::block_on]. This is not available on Wasm 33 | /// as Wasm does not have threads and you're not allowed to block the only thread you have. 34 | // 35 | // TODO: is target_arch = "wasm32" not a better way to express this? 36 | // 37 | #[ cfg(not( target_os = "unknown")) ] 38 | // 39 | #[ cfg_attr( nightly, doc(cfg(not( target_os = "unknown" ))) ) ] 40 | // 41 | pub fn block_on(future: F) -> F::Output 42 | { 43 | async_global::block_on( future ) 44 | } 45 | } 46 | 47 | 48 | #[ cfg( target_arch = "wasm32" ) ] 49 | // 50 | impl Spawn for AsyncGlobal 51 | { 52 | fn spawn_obj( &self, future: FutureObj<'static, ()> ) -> Result<(), SpawnError> 53 | { 54 | async_global::spawn_local( future ).detach(); 55 | 56 | Ok(()) 57 | } 58 | } 59 | 60 | 61 | 62 | #[ cfg(not( target_arch = "wasm32" )) ] 63 | // 64 | impl Spawn for AsyncGlobal 65 | { 66 | fn spawn_obj( &self, future: FutureObj<'static, ()> ) -> Result<(), SpawnError> 67 | { 68 | async_global::spawn( future ).detach(); 69 | 70 | Ok(()) 71 | } 72 | } 73 | 74 | 75 | 76 | #[ cfg( not(target_arch = "wasm32") ) ] 77 | // 78 | impl SpawnHandle for AsyncGlobal 79 | { 80 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 81 | { 82 | let handle = async_global::spawn( future ); 83 | 84 | Ok( JoinHandle::async_global(handle) ) 85 | } 86 | } 87 | 88 | 89 | 90 | #[ cfg( target_arch = "wasm32" ) ] 91 | // 92 | impl SpawnHandle for AsyncGlobal 93 | { 94 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 95 | { 96 | let handle = async_global::spawn_local( future ); 97 | 98 | Ok( JoinHandle::async_global(handle) ) 99 | } 100 | } 101 | 102 | 103 | 104 | impl LocalSpawnHandle for AsyncGlobal 105 | { 106 | fn spawn_handle_local_obj( &self, future: LocalFutureObj<'static, Out> ) -> Result, SpawnError> 107 | { 108 | let handle = async_global::spawn_local( future ); 109 | 110 | Ok( JoinHandle::async_global(handle) ) 111 | } 112 | } 113 | 114 | 115 | 116 | impl LocalSpawn for AsyncGlobal 117 | { 118 | fn spawn_local_obj( &self, future: LocalFutureObj<'static, ()> ) -> Result<(), SpawnError> 119 | { 120 | async_global::spawn_local( future ).detach(); 121 | 122 | Ok(()) 123 | } 124 | } 125 | 126 | 127 | impl crate::YieldNow for AsyncGlobal {} 128 | 129 | 130 | 131 | #[ cfg( not(target_arch = "wasm32") ) ] 132 | // 133 | impl crate::SpawnBlocking for AsyncGlobal 134 | { 135 | fn spawn_blocking( &self, f: F ) -> crate::BlockingHandle 136 | 137 | where F: FnOnce() -> R + Send + 'static , 138 | { 139 | let handle = async_global::spawn_blocking( f ); 140 | 141 | crate::BlockingHandle::async_global( Box::pin( handle ) ) 142 | } 143 | 144 | 145 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> crate::BlockingHandle 146 | { 147 | self.spawn_blocking( f ) 148 | } 149 | } 150 | 151 | 152 | 153 | 154 | impl std::fmt::Debug for AsyncGlobal 155 | { 156 | fn fmt( &self, f: &mut std::fmt::Formatter<'_> ) -> std::fmt::Result 157 | { 158 | write!( f, "AsyncGlobal executor" ) 159 | } 160 | } 161 | 162 | 163 | 164 | /// Signal io can be used on this executor. 165 | // 166 | #[ cfg(all( not(target_arch = "wasm32"), feature = "async_global_io" )) ] 167 | // 168 | #[ cfg_attr( nightly, doc(cfg(all( not(target_arch = "wasm32"), feature = "async_global_io" ))) ) ] 169 | // 170 | impl crate::AsyncIo for AsyncGlobal {} 171 | 172 | 173 | /// Signal io can be used on this executor. 174 | // 175 | #[ cfg(all( not(target_arch = "wasm32"), feature = "async_global_tokio" )) ] 176 | // 177 | #[ cfg_attr( nightly, doc(cfg(all( not(target_arch = "wasm32"), feature = "async_global_tokio" ))) ) ] 178 | // 179 | impl crate::TokioIo for AsyncGlobal {} 180 | 181 | 182 | 183 | #[ cfg( feature = "timer" ) ] 184 | // 185 | #[ cfg_attr( nightly, doc(cfg(all( feature = "timer", feature = "async_global" ))) ) ] 186 | // 187 | impl crate::Timer for AsyncGlobal 188 | { 189 | fn sleep( &self, dur: std::time::Duration ) -> futures_core::future::BoxFuture<'static, ()> 190 | { 191 | Box::pin( futures_timer::Delay::new( dur ) ) 192 | } 193 | } 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /src/exec/async_std.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | crate :: { SpawnHandle, LocalSpawnHandle, JoinHandle, } , 4 | futures_task :: { FutureObj, LocalFutureObj, Spawn, LocalSpawn, SpawnError } , 5 | futures_util :: { future::abortable } , 6 | 7 | async_std_crate as async_std, 8 | }; 9 | 10 | 11 | /// An executor that spawns tasks on async-std. In contrast to the other executors, this one 12 | /// is not self contained, because async-std does not provide an API that allows that, 13 | /// so the threadpool is global. 14 | /// 15 | /// It works on Wasm. 16 | // 17 | #[ derive( Copy, Clone, Default ) ] 18 | // 19 | #[ cfg_attr( nightly, doc(cfg( feature = "async_std" )) ) ] 20 | // 21 | pub struct AsyncStd; 22 | 23 | impl AsyncStd 24 | { 25 | /// Create a new AsyncStd wrapper. 26 | /// 27 | pub fn new() -> Self 28 | { 29 | Self 30 | } 31 | 32 | 33 | /// Wrapper around [async_std::task::block_on](::async_std_crate::task::block_on()). This is not available on Wasm 34 | /// as Wasm does not have threads and you're not allowed to block the only thread you have. 35 | // 36 | #[cfg(not(target_os = "unknown"))] 37 | #[ cfg_attr( nightly, doc(cfg(not( target_os = "unknown" ))) ) ] 38 | // 39 | pub fn block_on(future: F) -> F::Output 40 | { 41 | async_std::task::block_on( future ) 42 | } 43 | } 44 | 45 | 46 | 47 | #[ cfg( target_arch = "wasm32" ) ] 48 | // 49 | impl Spawn for AsyncStd 50 | { 51 | fn spawn_obj( &self, future: FutureObj<'static, ()> ) -> Result<(), SpawnError> 52 | { 53 | async_std::task::spawn_local( future ); 54 | 55 | Ok(()) 56 | } 57 | } 58 | 59 | 60 | 61 | #[ cfg(not( target_arch = "wasm32" )) ] 62 | // 63 | impl Spawn for AsyncStd 64 | { 65 | fn spawn_obj( &self, future: FutureObj<'static, ()> ) -> Result<(), SpawnError> 66 | { 67 | async_std::task::spawn( future ); 68 | 69 | Ok(()) 70 | } 71 | } 72 | 73 | 74 | 75 | #[ cfg( not(target_arch = "wasm32") ) ] 76 | // 77 | impl SpawnHandle for AsyncStd 78 | { 79 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 80 | { 81 | let (fut, a_handle) = abortable( future ); 82 | let handle = async_std::task::spawn( fut ); 83 | 84 | Ok( JoinHandle::async_std(handle, a_handle) ) 85 | } 86 | } 87 | 88 | 89 | 90 | // async-std only exposes local_spawn on wasm. 91 | // 92 | #[ cfg( target_arch = "wasm32" ) ] 93 | // 94 | impl SpawnHandle for AsyncStd 95 | { 96 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 97 | { 98 | let (fut, a_handle) = abortable( future ); 99 | let handle = async_std::task::spawn_local( fut ); 100 | 101 | Ok( JoinHandle::async_std(handle, a_handle) ) 102 | 103 | } 104 | } 105 | 106 | 107 | 108 | impl LocalSpawnHandle for AsyncStd 109 | { 110 | fn spawn_handle_local_obj( &self, future: LocalFutureObj<'static, Out> ) -> Result, SpawnError> 111 | { 112 | let (fut, a_handle) = abortable( future ); 113 | let handle = async_std::task::spawn_local( fut ); 114 | 115 | Ok( JoinHandle::async_std(handle, a_handle)) 116 | } 117 | } 118 | 119 | 120 | 121 | impl LocalSpawn for AsyncStd 122 | { 123 | fn spawn_local_obj( &self, future: LocalFutureObj<'static, ()> ) -> Result<(), SpawnError> 124 | { 125 | // We drop the JoinHandle, so the task becomes detached. 126 | // 127 | drop( async_std::task::spawn_local(future) ); 128 | 129 | Ok(()) 130 | } 131 | } 132 | 133 | 134 | 135 | impl crate::YieldNow for AsyncStd {} 136 | 137 | 138 | 139 | #[ cfg( not(target_arch = "wasm32") ) ] 140 | // 141 | impl crate::SpawnBlocking for AsyncStd 142 | { 143 | fn spawn_blocking( &self, f: F ) -> crate::BlockingHandle 144 | 145 | where F: FnOnce() -> R + Send + 'static , 146 | { 147 | let handle = async_std::task::spawn_blocking( f ); 148 | 149 | crate::BlockingHandle::async_std( handle ) 150 | } 151 | 152 | 153 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> crate::BlockingHandle 154 | { 155 | self.spawn_blocking( f ) 156 | } 157 | } 158 | 159 | 160 | 161 | impl std::fmt::Debug for AsyncStd 162 | { 163 | fn fmt( &self, f: &mut std::fmt::Formatter<'_> ) -> std::fmt::Result 164 | { 165 | write!( f, "AsyncStd executor" ) 166 | } 167 | } 168 | 169 | 170 | 171 | /// Signal io can be used on this executor. 172 | // 173 | #[ cfg(all( not(target_arch = "wasm32"), feature="async_std_tokio" )) ] 174 | // 175 | #[ cfg_attr( nightly, doc(cfg(all( not(target_arch = "wasm32"), feature="async_std_tokio" ))) ) ] 176 | // 177 | impl crate::TokioIo for AsyncStd {} 178 | 179 | 180 | 181 | 182 | 183 | 184 | #[ cfg(not( target_arch = "wasm32" )) ] 185 | // 186 | impl crate::Timer for AsyncStd 187 | { 188 | fn sleep( &self, dur: std::time::Duration ) -> futures_core::future::BoxFuture<'static, ()> 189 | { 190 | Box::pin( async_std::task::sleep(dur) ) 191 | } 192 | } 193 | 194 | 195 | 196 | 197 | 198 | // On wasm async_std future is not Send, so use futures-timer. 199 | // 200 | #[ cfg(all( target_arch = "wasm32", feature = "timer" )) ] 201 | // 202 | impl crate::Timer for AsyncStd 203 | { 204 | fn sleep( &self, dur: std::time::Duration ) -> futures_core::future::BoxFuture<'static, ()> 205 | { 206 | Box::pin( futures_timer::Delay::new(dur) ) 207 | } 208 | } 209 | 210 | 211 | -------------------------------------------------------------------------------- /src/exec/bindgen.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | crate :: { SpawnHandle, LocalSpawnHandle, JoinHandle, YieldNow } , 4 | wasm_bindgen_futures :: { spawn_local } , 5 | futures_task :: { FutureObj, LocalFutureObj, Spawn, LocalSpawn, SpawnError } , 6 | futures_util :: { FutureExt } , 7 | }; 8 | 9 | 10 | /// A type that implements [`Spawn`], [`LocalSpawn`], [`SpawnHandle`](crate::SpawnHandle) and [`LocalSpawnHandle`](crate::LocalSpawnHandle). 11 | /// Spawns on the _wasm-bingen-futures_ executor. The executor is global, eg. not self contained 12 | /// and zero sized. 13 | // 14 | #[ derive( Copy, Clone, Default ) ] 15 | // 16 | #[ cfg_attr( nightly, doc(cfg(all( feature = "bindgen", target_arch = "wasm32" ))) ) ] 17 | // 18 | pub struct Bindgen; 19 | 20 | 21 | impl Bindgen 22 | { 23 | /// Create a new Bindgen wrapper. 24 | /// 25 | pub fn new() -> Self 26 | { 27 | Self 28 | } 29 | } 30 | 31 | 32 | 33 | impl Spawn for Bindgen 34 | { 35 | fn spawn_obj( &self, future: FutureObj<'static, ()> ) -> Result<(), SpawnError> 36 | { 37 | spawn_local( future ); 38 | 39 | Ok(()) 40 | } 41 | } 42 | 43 | 44 | 45 | impl LocalSpawn for Bindgen 46 | { 47 | fn spawn_local_obj( &self, future: LocalFutureObj<'static, ()> ) -> Result<(), SpawnError> 48 | { 49 | spawn_local( future ); 50 | 51 | Ok(()) 52 | } 53 | } 54 | 55 | 56 | 57 | impl SpawnHandle for Bindgen 58 | { 59 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 60 | { 61 | let (fut, handle) = future.remote_handle(); 62 | spawn_local(fut); 63 | 64 | Ok( JoinHandle::remote_handle(handle) ) 65 | } 66 | } 67 | 68 | 69 | 70 | impl LocalSpawnHandle for Bindgen 71 | { 72 | fn spawn_handle_local_obj( &self, future: LocalFutureObj<'static, Out> ) -> Result, SpawnError> 73 | { 74 | let (fut, handle) = future.remote_handle(); 75 | spawn_local(fut); 76 | 77 | Ok( JoinHandle::remote_handle(handle) ) 78 | } 79 | } 80 | 81 | 82 | 83 | impl YieldNow for Bindgen {} 84 | 85 | 86 | 87 | impl std::fmt::Debug for Bindgen 88 | { 89 | fn fmt( &self, f: &mut std::fmt::Formatter<'_> ) -> std::fmt::Result 90 | { 91 | write!( f, "WASM Bindgen executor" ) 92 | } 93 | } 94 | 95 | 96 | 97 | #[ cfg( feature = "timer" ) ] 98 | // 99 | #[ cfg_attr( nightly, doc(cfg(all( feature = "timer", feature = "bindgen" ))) ) ] 100 | // 101 | impl crate::Timer for Bindgen 102 | { 103 | fn sleep( &self, dur: std::time::Duration ) -> futures_core::future::BoxFuture<'static, ()> 104 | { 105 | futures_timer::Delay::new( dur ).boxed() 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/exec/glommio_ct.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | crate :: { LocalSpawnHandle, SpawnHandle, JoinHandle, YieldNow } , 4 | std :: { future::Future, rc::Rc } , 5 | futures_task :: { FutureObj, LocalSpawn, Spawn, SpawnError } , 6 | futures_util :: { FutureExt, task::LocalSpawnExt, future::LocalFutureObj } , 7 | glommio_crate :: { LocalExecutor, LocalExecutorBuilder, GlommioError } , 8 | }; 9 | 10 | 11 | /// Single threaded [glommio](https://docs.rs/glommio) executor. This executor works 12 | /// on Linux 5.8+ only. 13 | /// 14 | /// You will probably need to augment your rlimit_memlock. See glommio documentation 15 | /// for details. 16 | /// 17 | /// This always has io_uring enabled and will pull in quite some dependencies including 18 | /// liburing from C. 19 | /// 20 | /// Glommio has some specific behavior. It will always poll spawned tasks immediately, 21 | /// so a spawned task will pre-empt the currently running task until it's first await. 22 | /// 23 | /// When it comes to YieldNow, glommio does not yield unless the task has been running for 24 | /// some time. The glommio method is actually called `yield_if_needed`. 25 | /// 26 | /// # Panics 27 | /// 28 | /// Calling spawn from outside [`block_on`](GlommioCt::block_on) will panic. 29 | // 30 | #[ derive(Debug, Clone) ] 31 | // 32 | #[ cfg_attr( nightly, doc(cfg( feature = "glommio" )) ) ] 33 | // 34 | pub struct GlommioCt 35 | { 36 | exec: Rc, 37 | } 38 | 39 | 40 | impl GlommioCt 41 | { 42 | /// Create an executor. Note: in order to spawn you need to run [`block_on`](Self::block_on). 43 | // 44 | pub fn new( builder: LocalExecutorBuilder ) -> Result< Self, GlommioError<()> > 45 | { 46 | let exec = Rc::new( builder.make()? ); 47 | 48 | Ok( Self{ exec } ) 49 | } 50 | 51 | 52 | 53 | /// Runs the executor until the given future completes. 54 | /// This is the entry point of the executor. Calls to spawn will only work from the 55 | /// context of the future provided here. 56 | // 57 | pub fn block_on( &self, future: F ) -> F::Output 58 | { 59 | self.exec.run( future ) 60 | } 61 | } 62 | 63 | 64 | 65 | impl LocalSpawn for GlommioCt 66 | { 67 | fn spawn_local_obj( &self, future: LocalFutureObj<'static, ()> ) -> Result<(), SpawnError> 68 | { 69 | glommio_crate::spawn_local( future ).detach(); 70 | Ok(()) 71 | } 72 | } 73 | 74 | 75 | 76 | impl LocalSpawnHandle for GlommioCt 77 | { 78 | fn spawn_handle_local_obj( &self, future: LocalFutureObj<'static, Out> ) 79 | 80 | -> Result, SpawnError> 81 | { 82 | let (remote, handle) = future.remote_handle(); 83 | 84 | glommio_crate::spawn_local( remote ).detach(); 85 | 86 | Ok( JoinHandle::remote_handle(handle) ) 87 | } 88 | } 89 | 90 | 91 | 92 | impl Spawn for GlommioCt 93 | { 94 | fn spawn_obj( &self, future: FutureObj<'static, ()> ) -> Result<(), SpawnError> 95 | { 96 | self.spawn_local( future ) 97 | } 98 | } 99 | 100 | 101 | 102 | impl SpawnHandle for GlommioCt 103 | { 104 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 105 | { 106 | let (remote, handle) = future.remote_handle(); 107 | 108 | glommio_crate::spawn_local( remote ).detach(); 109 | 110 | Ok( JoinHandle::remote_handle(handle) ) 111 | } 112 | } 113 | 114 | 115 | 116 | #[ cfg( feature = "timer" ) ] 117 | // 118 | impl crate::Timer for GlommioCt 119 | { 120 | fn sleep( &self, dur: std::time::Duration ) -> futures_core::future::BoxFuture<'static, ()> 121 | { 122 | futures_timer::Delay::new( dur ).boxed() 123 | } 124 | } 125 | 126 | 127 | impl YieldNow for GlommioCt 128 | { 129 | /// Await this future in order to yield to the executor. 130 | // 131 | fn yield_now( &self ) -> crate::YieldNowFut 132 | { 133 | // only yield if any other tasks are waiting. 134 | // 135 | if glommio_crate::executor().need_preempt() 136 | { 137 | crate::YieldNowFut{ done: false } 138 | } 139 | 140 | else 141 | { 142 | // This will return Ready immediately. 143 | // 144 | crate::YieldNowFut{ done: true } 145 | } 146 | } 147 | } 148 | 149 | 150 | 151 | #[ cfg(test) ] 152 | // 153 | mod tests 154 | { 155 | use super::*; 156 | 157 | // It's important that this is not Send, as we allow spawning !Send futures on it. 158 | // 159 | static_assertions::assert_not_impl_any!( GlommioCt: Send, Sync ); 160 | } 161 | -------------------------------------------------------------------------------- /src/exec/localpool.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | crate :: { JoinHandle, SpawnHandle, LocalSpawnHandle, YieldNow } , 4 | futures_task :: { SpawnError, FutureObj, LocalFutureObj } , 5 | futures_util :: { future::{ FutureExt }, task::{ SpawnExt, LocalSpawnExt } } , 6 | futures_executor :: { LocalSpawner } , 7 | 8 | }; 9 | 10 | 11 | impl SpawnHandle for LocalSpawner 12 | { 13 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 14 | { 15 | let (fut, handle) = future.remote_handle(); 16 | 17 | self.spawn( fut )?; 18 | 19 | Ok( JoinHandle::remote_handle(handle)) 20 | } 21 | } 22 | 23 | 24 | 25 | impl LocalSpawnHandle for LocalSpawner 26 | { 27 | fn spawn_handle_local_obj( &self, future: LocalFutureObj<'static, Out> ) -> Result, SpawnError> 28 | { 29 | let (fut, handle) = future.remote_handle(); 30 | 31 | self.spawn_local( fut )?; 32 | 33 | Ok( JoinHandle::remote_handle(handle) ) 34 | } 35 | } 36 | 37 | 38 | #[ cfg( feature = "timer" ) ] 39 | // 40 | #[ cfg_attr( nightly, doc(cfg(all( feature = "timer", feature = "async_global" ))) ) ] 41 | // 42 | impl crate::Timer for LocalSpawner 43 | { 44 | fn sleep( &self, dur: std::time::Duration ) -> futures_core::future::BoxFuture<'static, ()> 45 | { 46 | futures_timer::Delay::new( dur ).boxed() 47 | } 48 | } 49 | 50 | 51 | impl YieldNow for LocalSpawner {} 52 | -------------------------------------------------------------------------------- /src/exec/mod.rs: -------------------------------------------------------------------------------- 1 | #[ cfg( feature = "tokio_ct" ) ] mod tokio_ct; 2 | #[ cfg( feature = "tokio_ct" ) ] pub use tokio_ct::*; 3 | 4 | #[ cfg( feature = "tokio_tp" ) ] mod tokio_tp; 5 | #[ cfg( feature = "tokio_tp" ) ] pub use tokio_tp::*; 6 | 7 | #[ cfg( feature = "async_global" ) ] mod async_global; 8 | #[ cfg( feature = "async_global" ) ] pub use async_global::*; 9 | 10 | #[ cfg( feature = "async_std" ) ] mod async_std; 11 | #[ cfg( feature = "async_std" ) ] pub use async_std::*; 12 | 13 | #[ cfg( feature = "glommio" ) ] mod glommio_ct; 14 | #[ cfg( feature = "glommio" ) ] pub use glommio_ct::*; 15 | 16 | #[ cfg( feature = "bindgen" ) ] mod bindgen; 17 | #[ cfg( feature = "bindgen" ) ] pub use bindgen::*; 18 | 19 | #[ cfg( feature = "localpool" ) ] mod localpool; 20 | #[ cfg( feature = "threadpool" ) ] mod threadpool; 21 | #[ cfg( feature = "tracing" ) ] mod tracing; 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/exec/threadpool.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | crate :: { JoinHandle, SpawnHandle } , 4 | futures_task :: { SpawnError, FutureObj } , 5 | futures_util :: { future::{ FutureExt }, task::SpawnExt } , 6 | futures_executor :: { ThreadPool } , 7 | 8 | }; 9 | 10 | 11 | impl SpawnHandle for ThreadPool 12 | { 13 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 14 | { 15 | let (fut, handle) = future.remote_handle(); 16 | 17 | self.spawn( fut )?; 18 | 19 | Ok( JoinHandle::remote_handle(handle) ) 20 | } 21 | } 22 | 23 | 24 | impl crate::YieldNow for ThreadPool {} 25 | 26 | 27 | 28 | #[ cfg( feature = "timer" ) ] 29 | // 30 | #[ cfg_attr( nightly, doc(cfg(all( feature = "timer", feature = "async_global" ))) ) ] 31 | // 32 | impl crate::Timer for ThreadPool 33 | { 34 | fn sleep( &self, dur: std::time::Duration ) -> futures_core::future::BoxFuture<'static, ()> 35 | { 36 | futures_timer::Delay::new( dur ).boxed() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/exec/tracing.rs: -------------------------------------------------------------------------------- 1 | #![ cfg_attr( nightly, doc(cfg( feature = "tracing" )) ) ] 2 | // 3 | use 4 | { 5 | futures_util :: { future::{ FutureExt } } , 6 | futures_task :: { SpawnError, LocalFutureObj, FutureObj } , 7 | crate::iface :: { * } , 8 | tracing_futures :: { Instrument, Instrumented, WithDispatch } , 9 | }; 10 | 11 | 12 | 13 | impl SpawnHandle for Instrumented where T: SpawnHandle, Out: 'static + Send 14 | { 15 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 16 | { 17 | let fut = future.instrument( self.span().clone() ); 18 | 19 | self.inner().spawn_handle_obj( FutureObj::new(fut.boxed()) ) 20 | } 21 | } 22 | 23 | 24 | 25 | impl SpawnHandle for WithDispatch where T: SpawnHandle, Out: 'static + Send 26 | { 27 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError> 28 | { 29 | let fut = self.with_dispatch( future ); 30 | 31 | self.inner().spawn_handle_obj( FutureObj::new(fut.boxed()) ) 32 | } 33 | } 34 | 35 | 36 | 37 | impl LocalSpawnHandle for Instrumented where T: LocalSpawnHandle, Out: 'static 38 | { 39 | fn spawn_handle_local_obj( &self, future: LocalFutureObj<'static, Out> ) -> Result, SpawnError> 40 | { 41 | let fut = future.instrument( self.span().clone() ); 42 | 43 | self.inner().spawn_handle_local_obj( LocalFutureObj::new(fut.boxed_local()) ) 44 | } 45 | } 46 | 47 | 48 | 49 | impl LocalSpawnHandle for WithDispatch where T: LocalSpawnHandle, Out: 'static 50 | { 51 | fn spawn_handle_local_obj( &self, future: LocalFutureObj<'static, Out> ) -> Result, SpawnError> 52 | { 53 | let fut = self.with_dispatch(future); 54 | 55 | self.inner().spawn_handle_local_obj( LocalFutureObj::new(fut.boxed_local()) ) 56 | } 57 | } 58 | 59 | 60 | 61 | impl Timer for Instrumented where T: Timer 62 | { 63 | fn sleep( &self, dur: std::time::Duration ) -> futures_core::future::BoxFuture<'static, ()> 64 | { 65 | self.inner().sleep( dur ).instrument( self.span().clone() ).boxed() 66 | } 67 | } 68 | 69 | 70 | 71 | impl Timer for WithDispatch where T: Timer 72 | { 73 | fn sleep( &self, dur: std::time::Duration ) -> futures_core::future::BoxFuture<'static, ()> 74 | { 75 | self.with_dispatch( self.inner().sleep( dur ) ).boxed() 76 | } 77 | } 78 | 79 | 80 | impl TokioIo for Instrumented where T: TokioIo {} 81 | impl TokioIo for WithDispatch where T: TokioIo {} 82 | 83 | impl YieldNow for Instrumented where T: YieldNow {} 84 | impl YieldNow for WithDispatch where T: YieldNow {} 85 | 86 | 87 | 88 | impl SpawnBlocking for Instrumented where T: SpawnBlocking, R: Send + 'static 89 | { 90 | fn spawn_blocking( &self, f: F ) -> BlockingHandle 91 | 92 | where F : FnOnce() -> R + Send + 'static , 93 | Self: Sized , 94 | { 95 | self.inner().spawn_blocking(f) 96 | } 97 | 98 | /// Runs the provided closure on a thread where blocking is acceptable. This part of the trait is 99 | /// object safe but your closure must be boxed and you cannot have a return value. 100 | // 101 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> BlockingHandle 102 | { 103 | self.inner().spawn_blocking_dyn(f) 104 | } 105 | } 106 | 107 | 108 | impl SpawnBlocking for WithDispatch where T: SpawnBlocking, R: Send + 'static 109 | { 110 | fn spawn_blocking( &self, f: F ) -> BlockingHandle 111 | 112 | where F : FnOnce() -> R + Send + 'static , 113 | Self: Sized , 114 | { 115 | self.inner().spawn_blocking(f) 116 | } 117 | 118 | /// Runs the provided closure on a thread where blocking is acceptable. This part of the trait is 119 | /// object safe but your closure must be boxed and you cannot have a return value. 120 | // 121 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> BlockingHandle 122 | { 123 | self.inner().spawn_blocking_dyn(f) 124 | } 125 | } 126 | 127 | 128 | -------------------------------------------------------------------------------- /src/iface/blocking_handle.rs: -------------------------------------------------------------------------------- 1 | #[ allow(unused_imports) ] // some imports are conditional on features 2 | // 3 | use 4 | { 5 | std :: { future::Future, sync::atomic::{ AtomicBool, Ordering } } , 6 | std :: { task::{ Poll, Context }, pin::Pin } , 7 | futures_util:: { future::{ AbortHandle, Aborted, RemoteHandle }, ready } , 8 | super :: *, 9 | }; 10 | 11 | 12 | #[ cfg( feature = "async_global" ) ] 13 | // 14 | type BoxedFut = Pin + Send >>; 15 | 16 | 17 | /// A framework agnostic BlockingHandle type. This is returned by [`SpawnBlocking`](crate::SpawnBlocking). 18 | /// Await this handle for the output of the task. As opposed to a [`JoinHandle`], you can't cancel a blocking 19 | /// task once it has started running. If you drop this after the task starts running, it will just detach 20 | /// and let the task run in the background. 21 | // 22 | #[ derive( Debug ) ] 23 | // 24 | pub struct BlockingHandle( InnerBh ); 25 | 26 | 27 | impl BlockingHandle 28 | { 29 | /// Make a wrapper around [`tokio::task::JoinHandle`]. 30 | // 31 | #[ cfg(any( feature = "tokio_tp", feature = "tokio_ct" )) ] 32 | // 33 | pub fn tokio( handle: TokioJoinHandle ) -> Self 34 | { 35 | Self( InnerBh::Tokio(handle) ) 36 | } 37 | 38 | 39 | /// Make a wrapper around [`async_global_executor::Task`]. 40 | // 41 | #[ cfg( feature = "async_global" ) ] 42 | // 43 | pub fn async_global( task: BoxedFut ) -> Self 44 | { 45 | Self( InnerBh::AsyncGlobal(task) ) 46 | } 47 | 48 | 49 | /// Make a wrapper around [`async_std::task::JoinHandle`](async_std_crate::task::JoinHandle). 50 | // 51 | #[ cfg( feature = "async_std" ) ] 52 | // 53 | pub fn async_std( handle: AsyncStdJoinHandle ) -> Self 54 | { 55 | Self( InnerBh::AsyncStd(handle) ) 56 | } 57 | } 58 | 59 | 60 | 61 | #[ allow(dead_code) ] 62 | // 63 | enum InnerBh 64 | { 65 | /// Wrapper around tokio BlockingHandle. 66 | // 67 | #[ cfg(any( feature = "tokio_tp", feature = "tokio_ct" )) ] 68 | // 69 | Tokio( TokioJoinHandle ), 70 | 71 | /// Wrapper around AsyncStd BlockingHandle. 72 | // 73 | #[ cfg( feature = "async_global" ) ] 74 | // 75 | AsyncGlobal( BoxedFut ), 76 | 77 | /// Wrapper around AsyncStd BlockingHandle. 78 | // 79 | #[ cfg( feature = "async_std" ) ] 80 | // 81 | AsyncStd( AsyncStdJoinHandle ), 82 | 83 | // Since the other variants are behind feature flags, the generic won't be 84 | // used if we don't include this. 85 | // 86 | Phantom( std::marker::PhantomData< fn()->T > ), 87 | } 88 | 89 | 90 | 91 | impl Future for BlockingHandle 92 | { 93 | type Output = T; 94 | 95 | fn poll( self: Pin<&mut Self>, _cx: &mut Context<'_> ) -> Poll 96 | { 97 | match &mut self.get_mut().0 98 | { 99 | #[ cfg(any( feature = "tokio_tp", feature = "tokio_ct" )) ] 100 | // 101 | InnerBh::Tokio(handle) => 102 | { 103 | match ready!( Pin::new( handle ).poll( _cx ) ) 104 | { 105 | Ok(t) => Poll::Ready( t ), 106 | 107 | Err(e) => panic!( "Task has been canceled or has panicked. \ 108 | Are you dropping the executor to early? Error: {}", e ), 109 | } 110 | } 111 | 112 | #[ cfg( feature = "async_std" ) ] InnerBh::AsyncStd ( handle ) => Pin::new( handle ).poll( _cx ) , 113 | #[ cfg( feature = "async_global" ) ] InnerBh::AsyncGlobal( task ) => Pin::new( task ).poll( _cx ) , 114 | 115 | InnerBh::Phantom(_) => unreachable!(), 116 | } 117 | } 118 | } 119 | 120 | 121 | 122 | impl std::fmt::Debug for InnerBh 123 | { 124 | fn fmt( &self, f: &mut std::fmt::Formatter<'_> ) -> std::fmt::Result 125 | { 126 | write!( f, "InnerBh" ) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/iface/join_handle.rs: -------------------------------------------------------------------------------- 1 | #[ allow(unused_imports) ] // some imports are conditional on features 2 | // 3 | use 4 | { 5 | std :: { future::Future, sync::atomic::{ AtomicBool, Ordering } } , 6 | std :: { task::{ Poll, Context }, pin::Pin } , 7 | futures_util:: { future::{ AbortHandle, Aborted, RemoteHandle }, ready } , 8 | super :: *, 9 | }; 10 | 11 | 12 | 13 | 14 | /// A framework agnostic JoinHandle type. Cancels the future on dropping the handle. 15 | /// You can call [`detach`](JoinHandle::detach) to leave the future running when dropping the handle. 16 | /// 17 | /// This leverages the performance gains from the native join handles compared to 18 | /// [RemoteHandle](futures_util::future::RemoteHandle) where possible. 19 | /// 20 | /// It does wrap futures in [Abortable](futures_util::future::Abortable) where needed as 21 | /// [_async-std_](async_std_crate)'s canceling is asynchronous, which we can't call during drop. 22 | /// 23 | /// # Panics 24 | /// 25 | /// There is an inconsistency between executors when it comes to a panicking task. 26 | /// Generally we unwind the thread on which the handle is awaited when a task panics, 27 | /// but async-std will also let the executor working thread unwind. No `catch_unwind` was added to 28 | /// bring async-std in line with the other executors here. 29 | /// 30 | /// Awaiting the JoinHandle can also panic if you drop the executor before it completes. 31 | // 32 | #[ derive( Debug ) ] 33 | // 34 | #[ must_use = "JoinHandle will cancel your future when dropped unless you await it." ] 35 | // 36 | pub struct JoinHandle { inner: InnerJh } 37 | 38 | 39 | 40 | impl JoinHandle 41 | { 42 | /// Make a wrapper around [`tokio::task::JoinHandle`]. 43 | // 44 | #[ cfg(any( feature = "tokio_tp", feature = "tokio_ct" )) ] 45 | // 46 | pub fn tokio( handle: TokioJoinHandle ) -> Self 47 | { 48 | let detached = false; 49 | let inner = InnerJh::Tokio { handle, detached }; 50 | 51 | Self{ inner } 52 | } 53 | 54 | 55 | 56 | /// Make a wrapper around [`async_global_executor::Task`]. 57 | // 58 | #[ cfg( feature = "async_global" ) ] 59 | // 60 | pub fn async_global( task: AsyncGlobalTask ) -> Self 61 | { 62 | let task = Some( task ); 63 | let inner = InnerJh::AsyncGlobal{ task }; 64 | 65 | Self{ inner } 66 | } 67 | 68 | 69 | 70 | /// Make a wrapper around [`async_std::task::JoinHandle`](async_std_crate::task::JoinHandle). The task needs to 71 | /// be wrapped in an abortable so we can cancel it on drop. 72 | // 73 | #[ cfg( feature = "async_std" ) ] 74 | // 75 | pub fn async_std 76 | ( 77 | handle : AsyncStdJoinHandle> , 78 | a_handle: AbortHandle , 79 | 80 | ) -> Self 81 | { 82 | let detached = false; 83 | let inner = InnerJh::AsyncStd{ handle, a_handle, detached }; 84 | 85 | Self{ inner } 86 | } 87 | 88 | 89 | /// Make a wrapper around [`futures_util::future::RemoteHandle`]. 90 | // 91 | pub fn remote_handle( handle: RemoteHandle ) -> Self 92 | { 93 | let inner = InnerJh::RemoteHandle{ handle: Some(handle) }; 94 | 95 | Self{ inner } 96 | } 97 | } 98 | 99 | 100 | 101 | #[ derive(Debug) ] #[ allow(dead_code) ] 102 | // 103 | enum InnerJh 104 | { 105 | /// Wrapper around tokio JoinHandle. 106 | // 107 | #[ cfg(any( feature = "tokio_tp", feature = "tokio_ct" )) ] 108 | // 109 | Tokio 110 | { 111 | handle : TokioJoinHandle , 112 | detached: bool , 113 | }, 114 | 115 | /// Wrapper around AsyncStd JoinHandle. 116 | // 117 | #[ cfg( feature = "async_global" ) ] 118 | // 119 | AsyncGlobal 120 | { 121 | task: Option< AsyncGlobalTask > , 122 | }, 123 | 124 | /// Wrapper around AsyncStd JoinHandle. 125 | // 126 | #[ cfg( feature = "async_std" ) ] 127 | // 128 | AsyncStd 129 | { 130 | handle : AsyncStdJoinHandle> , 131 | a_handle: AbortHandle , 132 | detached: bool , 133 | }, 134 | 135 | /// Wrapper around futures RemoteHandle. 136 | // 137 | RemoteHandle 138 | { 139 | handle: Option>, 140 | }, 141 | } 142 | 143 | 144 | 145 | impl JoinHandle 146 | { 147 | /// Drops this handle without canceling the underlying future. 148 | /// 149 | /// This method can be used if you want to drop the handle, but let the execution continue. 150 | // 151 | pub fn detach( mut self ) 152 | { 153 | match &mut self.inner 154 | { 155 | #[ cfg(any( feature = "tokio_tp", feature = "tokio_ct" )) ] 156 | // 157 | InnerJh::Tokio{ ref mut detached, .. } => 158 | { 159 | // only other use of this is in Drop impl and we consume self here, 160 | // so there cannot be any race as this does not sync things across threads, 161 | // hence Relaxed ordering. 162 | // 163 | *detached = true; 164 | } 165 | 166 | #[ cfg( feature = "async_global" ) ] InnerJh::AsyncGlobal{ task } => 167 | { 168 | let task = task.take(); 169 | task.unwrap().detach(); 170 | } 171 | 172 | #[ cfg( feature = "async_std" ) ] InnerJh::AsyncStd{ ref mut detached, .. } => 173 | { 174 | *detached = true; 175 | } 176 | 177 | InnerJh::RemoteHandle{ handle } => 178 | { 179 | if let Some(rh) = handle.take() { rh.forget() }; 180 | } 181 | } 182 | } 183 | } 184 | 185 | 186 | 187 | impl Future for JoinHandle 188 | { 189 | type Output = T; 190 | 191 | fn poll( self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll 192 | { 193 | match &mut self.get_mut().inner 194 | { 195 | #[ cfg(any( feature = "tokio_tp", feature = "tokio_ct" )) ] 196 | // 197 | InnerJh::Tokio{ handle, .. } => 198 | { 199 | match ready!( Pin::new( handle ).poll( cx ) ) 200 | { 201 | Ok (t) => Poll::Ready( t ), 202 | 203 | Err(e) => 204 | { 205 | panic!( "Task has been canceled or it has panicked. Are you dropping the executor to early? Error: {}", e ); 206 | } 207 | } 208 | } 209 | 210 | 211 | #[ cfg( feature = "async_std" ) ] InnerJh::AsyncStd{ handle, .. } => 212 | { 213 | match ready!( Pin::new( handle ).poll( cx ) ) 214 | { 215 | Ok (t) => Poll::Ready( t ), 216 | Err(_) => unreachable!(), 217 | } 218 | } 219 | 220 | 221 | #[ cfg( feature = "async_global" ) ] InnerJh::AsyncGlobal{ task, .. } => 222 | { 223 | Pin::new( task.as_mut().unwrap() ).poll( cx ) 224 | } 225 | 226 | 227 | InnerJh::RemoteHandle{ ref mut handle } => Pin::new( handle ).as_pin_mut().expect( "no polling after detach" ).poll( cx ), 228 | } 229 | } 230 | } 231 | 232 | 233 | 234 | impl Drop for JoinHandle 235 | { 236 | // see reasoning about Relaxed atomic in detach(). 237 | // 238 | fn drop( &mut self ) 239 | { 240 | match &mut self.inner 241 | { 242 | #[ cfg(any( feature = "tokio_tp", feature = "tokio_ct" )) ] 243 | // 244 | InnerJh::Tokio{ handle, detached, .. } => 245 | 246 | if !*detached { handle.abort() }, 247 | 248 | 249 | #[ cfg( feature = "async_std" ) ] InnerJh::AsyncStd { a_handle, detached, .. } => 250 | 251 | if !*detached { a_handle.abort() }, 252 | 253 | 254 | // Nothing needs to be done, just drop it. 255 | // 256 | #[ cfg( feature = "async_global" ) ] InnerJh::AsyncGlobal { .. } => {} 257 | 258 | 259 | InnerJh::RemoteHandle{ .. } => {}, 260 | }; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/iface/local_spawn_handle.rs: -------------------------------------------------------------------------------- 1 | #[ allow(unused_imports) ] 2 | // 3 | use 4 | { 5 | futures_task :: { SpawnError, LocalFutureObj } , 6 | futures_util :: { task::{ LocalSpawnExt }, future::{ FutureExt, abortable } } , 7 | crate :: { JoinHandle } , 8 | std :: { pin::Pin, future::Future, sync::{ Arc, atomic::AtomicBool }, rc::Rc } , 9 | blanket :: { blanket } , 10 | }; 11 | 12 | 13 | /// This is similar to [`SpawnHandle`](crate::SpawnHandle) except that it allows spawning `!Send` futures. Please see 14 | /// the docs on [`SpawnHandle`](crate::SpawnHandle). 15 | // 16 | #[ blanket(derive( Ref, Mut, Box, Arc, Rc )) ] 17 | // 18 | pub trait LocalSpawnHandle 19 | { 20 | /// Spawn a future and return a [`JoinHandle`] that can be awaited for the output of the future. 21 | // 22 | fn spawn_handle_local_obj( &self, future: LocalFutureObj<'static, Out> ) -> Result, SpawnError>; 23 | } 24 | 25 | 26 | /// Lets you spawn a `!Send` future and get a [`JoinHandle`] to await the output of a future. 27 | // 28 | pub trait LocalSpawnHandleExt : LocalSpawnHandle 29 | { 30 | /// Convenience trait for passing in a generic future to [`LocalSpawnHandle`]. Much akin to `LocalSpawn` and `LocalSpawnExt` in the 31 | /// futures library. 32 | // 33 | fn spawn_handle_local( &self, future: impl Future + 'static ) -> Result, SpawnError>; 34 | } 35 | 36 | 37 | impl LocalSpawnHandleExt for T 38 | 39 | where T : LocalSpawnHandle + ?Sized , 40 | Out: 'static , 41 | { 42 | fn spawn_handle_local( &self, future: impl Future + 'static ) -> Result, SpawnError> 43 | { 44 | self.spawn_handle_local_obj( LocalFutureObj::new(future.boxed_local()) ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/iface/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod blocking_handle ; 2 | pub(crate) mod join_handle ; 3 | pub(crate) mod local_spawn_handle ; 4 | pub(crate) mod spawn_blocking ; 5 | pub(crate) mod spawn_handle ; 6 | pub(crate) mod timer ; 7 | pub(crate) mod yield_now ; 8 | 9 | pub use blocking_handle ::*; 10 | pub use join_handle ::*; 11 | pub use local_spawn_handle ::*; 12 | pub use spawn_blocking ::*; 13 | pub use spawn_handle ::*; 14 | pub use timer ::*; 15 | pub use yield_now ::*; 16 | 17 | 18 | 19 | #[ cfg( feature = "async_global" ) ] 20 | // 21 | use async_global_executor::{ Task as AsyncGlobalTask }; 22 | 23 | #[ cfg( feature = "async_std" ) ] 24 | // 25 | use async_std_crate::{ task::JoinHandle as AsyncStdJoinHandle }; 26 | 27 | #[ cfg(any( feature = "tokio_tp", feature = "tokio_ct" )) ] 28 | // 29 | use tokio::{ task::JoinHandle as TokioJoinHandle }; 30 | 31 | 32 | 33 | /// Trait indicating that tokio IO can be used with the executor that 34 | /// implements it. Currently this can be enabled through features on [`TokioCt`](crate::TokioCt), 35 | /// [`TokioTp`](crate::TokioTp), [`AsyncGlobal`](crate::AsyncGlobal) and [`AsyncStd`](crate::AsyncStd). 36 | /// 37 | /// This means a tokio reactor will be running and that the network types from tokio (eg. `TcpStream`) will work. 38 | // 39 | pub trait TokioIo {} 40 | -------------------------------------------------------------------------------- /src/iface/spawn_blocking.rs: -------------------------------------------------------------------------------- 1 | #[ allow(unused_imports) ] 2 | // 3 | use 4 | { 5 | futures_util :: { future::{ FutureExt, abortable }, task::SpawnExt } , 6 | futures_task :: { SpawnError, FutureObj } , 7 | crate :: { BlockingHandle } , 8 | std :: { pin::Pin, future::Future, sync::{ Arc, atomic::AtomicBool }, rc::Rc } , 9 | blanket :: { blanket } , 10 | }; 11 | 12 | 13 | /// Indicate the executor can provide a threadpool for blocking operations. 14 | /// There is two methods of this trait. One of them requires boxing the closure 15 | /// and the other is not object safe. 16 | // 17 | // Doesn't work with blanket. 18 | // #[ blanket(derive( Mut, Box, Arc, Rc )) ] 19 | // 20 | pub trait SpawnBlocking where R: Send + 'static 21 | { 22 | /// Runs the provided closure on a thread where blocking is acceptable. 23 | // 24 | fn spawn_blocking( &self, f: F ) -> BlockingHandle 25 | 26 | where F : FnOnce() -> R + Send + 'static , 27 | Self: Sized , 28 | ; 29 | 30 | /// Runs the provided closure on a thread where blocking is acceptable. This part of the trait is 31 | /// object safe but your closure must be boxed and you cannot have a return value. 32 | // 33 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> BlockingHandle; 34 | } 35 | 36 | 37 | impl SpawnBlocking for &T where T: SpawnBlocking, R: Send + 'static 38 | { 39 | fn spawn_blocking( &self, f: F ) -> BlockingHandle 40 | 41 | where F: FnOnce() -> R + Send + 'static , 42 | T: Sized , 43 | { 44 | (**self).spawn_blocking( f ) 45 | } 46 | 47 | 48 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> BlockingHandle 49 | { 50 | (**self).spawn_blocking_dyn( f ) 51 | } 52 | } 53 | 54 | 55 | impl> SpawnBlocking for &mut T where T: SpawnBlocking, R: Send + 'static 56 | { 57 | fn spawn_blocking( &self, f: F ) -> BlockingHandle 58 | 59 | where F: FnOnce() -> R + Send + 'static , 60 | T: Sized , 61 | { 62 | (**self).spawn_blocking( f ) 63 | } 64 | 65 | 66 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> BlockingHandle 67 | { 68 | (**self).spawn_blocking_dyn( f ) 69 | } 70 | } 71 | 72 | 73 | impl> SpawnBlocking for Box where T: SpawnBlocking, R: Send + 'static 74 | { 75 | fn spawn_blocking( &self, f: F ) -> BlockingHandle 76 | 77 | where F: FnOnce() -> R + Send + 'static , 78 | T: Sized , 79 | { 80 | (**self).spawn_blocking( f ) 81 | } 82 | 83 | 84 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> BlockingHandle 85 | { 86 | (**self).spawn_blocking_dyn( f ) 87 | } 88 | } 89 | 90 | 91 | impl> SpawnBlocking for Arc where T: SpawnBlocking, R: Send + 'static 92 | { 93 | fn spawn_blocking( &self, f: F ) -> BlockingHandle 94 | 95 | where F: FnOnce() -> R + Send + 'static , 96 | T: Sized , 97 | { 98 | (**self).spawn_blocking( f ) 99 | } 100 | 101 | 102 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> BlockingHandle 103 | { 104 | (**self).spawn_blocking_dyn( f ) 105 | } 106 | } 107 | 108 | 109 | impl> SpawnBlocking for Rc where T: SpawnBlocking, R: Send + 'static 110 | { 111 | fn spawn_blocking( &self, f: F ) -> BlockingHandle 112 | 113 | where F: FnOnce() -> R + Send + 'static , 114 | T: Sized , 115 | { 116 | (**self).spawn_blocking( f ) 117 | } 118 | 119 | 120 | fn spawn_blocking_dyn( &self, f: Box< dyn FnOnce()->R + Send > ) -> BlockingHandle 121 | { 122 | (**self).spawn_blocking_dyn( f ) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/iface/spawn_handle.rs: -------------------------------------------------------------------------------- 1 | #[ allow(unused_imports) ] 2 | // 3 | use 4 | { 5 | futures_util :: { future::{ FutureExt, abortable }, task::SpawnExt } , 6 | futures_task :: { SpawnError, FutureObj } , 7 | crate :: { JoinHandle } , 8 | std :: { pin::Pin, future::Future, sync::{ Arc, atomic::AtomicBool }, rc::Rc } , 9 | blanket :: { blanket } , 10 | }; 11 | 12 | 13 | /// Lets you spawn and get a [JoinHandle] to await the output of a future. 14 | /// 15 | /// This trait works much like the [`Spawn`](futures_task::Spawn) trait from the futures library. 16 | /// It takes a [`FutureObj`] so we can hopefully make it `no_std` compatible when needed. This 17 | /// also allows it to be object safe. For convenience, there is [`SpawnHandleExt`] which allows you 18 | /// to spawn a generic future directly without having to manually make the [`FutureObj`]. 19 | /// 20 | /// [`SpawnHandleExt`] is automatically implemented but must be in scope, so this works: 21 | /// 22 | /// ```rust 23 | /// use async_executors::{ SpawnHandle, SpawnHandleExt }; 24 | /// 25 | /// async fn need_exec( exec: impl SpawnHandle<()> ) 26 | /// { 27 | /// let join_handle = exec.spawn_handle( async {} ).expect( "spawn" ); 28 | /// 29 | /// join_handle.await; 30 | /// } 31 | /// ``` 32 | /// 33 | /// and so does this: 34 | /// 35 | /// ```rust 36 | /// use async_executors::{ SpawnHandle, SpawnHandleExt }; 37 | /// 38 | /// async fn need_exec( exec: Box< dyn SpawnHandle<()> > ) 39 | /// { 40 | /// let join_handle = exec.spawn_handle( async {} ).expect( "spawn" ); 41 | /// 42 | /// join_handle.await; 43 | /// } 44 | /// ``` 45 | /// 46 | /// One inconvenience of it having to be object safe is that the trait needs to be generic over the 47 | /// output parameter. This can be annoying if you need an executor that can spawn futures with different 48 | /// output parameters. Normally you should always be able to know which ones you need. If not 49 | /// you will have to make the type that stores the executor generic over the output type as well. 50 | /// 51 | /// So to enable several output types you can use the 52 | /// [following workaround](https://github.com/najamelan/async_executors/tree/master/examples/spawn_handle_multi.rs). 53 | /// You can also use the [trait-set](https://crates.io/crates/trait-set) crate to make that [more streamlined](https://github.com/najamelan/async_executors/tree/master/examples/trait_set.rs). 54 | // 55 | #[ blanket(derive( Ref, Mut, Box, Arc, Rc )) ] 56 | // 57 | pub trait SpawnHandle 58 | { 59 | /// Spawn a future and return a [`JoinHandle`] that can be awaited for the output of the future. 60 | // 61 | fn spawn_handle_obj( &self, future: FutureObj<'static, Out> ) -> Result, SpawnError>; 62 | } 63 | 64 | /// Convenience trait for passing in a generic future to [`SpawnHandle`]. Much akin to `Spawn` and `SpawnExt` in the 65 | /// futures library. 66 | // 67 | pub trait SpawnHandleExt : SpawnHandle 68 | { 69 | /// Spawn a future and return a [JoinHandle] that can be awaited for the output of the future. 70 | // 71 | fn spawn_handle( &self, future: impl Future + Send + 'static ) -> Result, SpawnError>; 72 | } 73 | 74 | 75 | impl SpawnHandleExt for T 76 | 77 | where T : SpawnHandle + ?Sized , 78 | Out: 'static + Send , 79 | { 80 | fn spawn_handle( &self, future: impl Future + Send + 'static ) -> Result, SpawnError> 81 | { 82 | self.spawn_handle_obj( FutureObj::new(future.boxed()) ) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/iface/timer.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | std :: { time::Duration, future::Future, task::{ Poll, Context }, pin::Pin } , 4 | futures_core :: { future::BoxFuture } , 5 | 6 | pin_project::pin_project, 7 | }; 8 | 9 | /// Represents the fact that an executor has timer functionality. 10 | /// 11 | // Implementation: 12 | // - for tokio: use tokio when tokio_time feature is enabled, futures-timer otherwise. 13 | // - for async-global-executor: use futures-timer. 14 | // - for glommio: has own timer that can't be turned off. But we don't use it because 15 | // it's not Send. 16 | // - for bindgen: use futures-timer 17 | // - for async-std: has a timer that cannot be turned off. Isn't Send on Wasm. 18 | // 19 | // The trait needs to be available inconditionally, as a library must be able 20 | // to depend on it without specifying a backend. 21 | // 22 | #[ blanket::blanket( derive( Ref, Mut, Rc, Arc, Box ) ) ] 23 | // 24 | pub trait Timer 25 | { 26 | /// Future that resolves after a given duration. 27 | // 28 | #[ must_use = "sleep() returns a future, which does nothing unless awaited" ] 29 | // 30 | fn sleep( &self, dur: Duration ) -> BoxFuture<'static, ()>; 31 | } 32 | 33 | 34 | 35 | 36 | // The following code was taken from tor-rtcompat https://gitlab.torproject.org/tpo/core/arti/-/blob/main/tor-rtcompat/src/timer.rs 37 | // This is licenced: "MIT OR Apache-2.0". 38 | // 39 | 40 | 41 | 42 | /// An extension trait on [`Timer`] for timeouts and clock delays. 43 | // 44 | pub trait TimerExt: Timer 45 | { 46 | /// Wrap a [`Future`] with a timeout. 47 | /// 48 | /// The output of the new future will be the returned value of 49 | /// `future` if it completes within `duration`. Otherwise, it 50 | /// will be `Err(TimeoutError)`. 51 | /// 52 | /// # Limitations 53 | /// 54 | /// This uses [`Timer::sleep`] for its timer, and is 55 | /// subject to the same limitations. 56 | // 57 | #[ must_use = "timeout() returns a future, which does nothing unless awaited." ] 58 | // 59 | fn timeout( &self, duration: Duration, future: F ) -> Timeout 60 | { 61 | let sleep_future = self.sleep( duration ); 62 | 63 | Timeout { future, sleep_future } 64 | } 65 | } 66 | 67 | 68 | impl TimerExt for T {} 69 | 70 | 71 | /// An error value given when a function times out. 72 | /// 73 | /// This value is generated when the timeout from 74 | /// [`TimerExt::timeout`] expires before the provided future 75 | /// is ready. 76 | // 77 | #[ derive( Copy, Clone, Debug, Eq, PartialEq ) ] 78 | // 79 | #[allow(clippy::exhaustive_structs)] 80 | // 81 | pub struct TimeoutError; 82 | 83 | 84 | impl std::error::Error for TimeoutError {} 85 | 86 | 87 | impl std::fmt::Display for TimeoutError 88 | { 89 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result 90 | { 91 | write!( f, "Timeout expired" ) 92 | } 93 | } 94 | 95 | 96 | impl From for std::io::Error 97 | { 98 | fn from( err: TimeoutError ) -> std::io::Error 99 | { 100 | std::io::Error::new( std::io::ErrorKind::TimedOut, err ) 101 | } 102 | } 103 | 104 | 105 | /// A timeout returned by [`TimerExt::timeout`]. 106 | // 107 | #[pin_project] 108 | // 109 | pub struct Timeout 110 | { 111 | /// The future we want to execute. 112 | // 113 | #[pin] future: T, 114 | 115 | /// The future implementing the timeout. 116 | // 117 | sleep_future: BoxFuture<'static, ()>, 118 | } 119 | 120 | 121 | 122 | impl Future for Timeout 123 | 124 | where T: Future, 125 | 126 | { 127 | type Output = Result< T::Output, TimeoutError >; 128 | 129 | 130 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll 131 | { 132 | let this = self.project(); 133 | 134 | 135 | if let Poll::Ready(x) = this.future.poll(cx) 136 | { 137 | return Poll::Ready(Ok(x)); 138 | } 139 | 140 | 141 | match this.sleep_future.as_mut().poll(cx) 142 | { 143 | Poll::Pending => Poll::Pending , 144 | Poll::Ready(()) => Poll::Ready( Err(TimeoutError) ) , 145 | } 146 | } 147 | } 148 | 149 | 150 | impl std::fmt::Debug for Timeout 151 | { 152 | fn fmt( &self, f: &mut std::fmt::Formatter<'_> ) -> std::fmt::Result 153 | { 154 | write!( f, "Timeout future" ) 155 | } 156 | } 157 | 158 | -------------------------------------------------------------------------------- /src/iface/yield_now.rs: -------------------------------------------------------------------------------- 1 | use 2 | { 3 | std::{ future::Future, pin::Pin, task::{ Context, Poll } } , 4 | blanket::blanket, 5 | }; 6 | 7 | /// Trait indicating that tasks can yield to the executor. This put's 8 | /// the current task at the back of the schedulers queue, giving other 9 | /// tasks a chance to run. 10 | /// 11 | /// In practice for most executors this just returns a future that will, 12 | /// the first time it is polled, wake up the waker and then return Pending. 13 | /// 14 | /// The problem with using the executors native implementation is that they 15 | /// generally return an opaque future we would have to box. 16 | // 17 | #[ blanket( derive(Ref, Mut, Arc, Rc, Box) ) ] 18 | // 19 | pub trait YieldNow 20 | { 21 | /// Await this future in order to yield to the executor. 22 | // 23 | fn yield_now( &self ) -> YieldNowFut 24 | { 25 | YieldNowFut{ done: false } 26 | } 27 | } 28 | 29 | 30 | 31 | /// Future returned by [`YieldNow::yield_now`]. 32 | // 33 | #[must_use = "YieldNowFut doesn't do anything unless polled or awaited."] 34 | // 35 | #[ derive( Debug, Copy, Clone ) ] 36 | // 37 | pub struct YieldNowFut 38 | { 39 | pub(crate) done: bool, 40 | } 41 | 42 | 43 | impl Future for YieldNowFut 44 | { 45 | type Output = (); 46 | 47 | fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll<()> 48 | { 49 | if self.done 50 | { 51 | return Poll::Ready(()); 52 | } 53 | 54 | self.done = true; 55 | cx.waker().wake_by_ref(); 56 | Poll::Pending 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![ cfg_attr( nightly, feature(doc_cfg) ) ] 2 | #![ doc = include_str!("../README.md") ] 3 | 4 | #![ doc ( html_root_url = "https://docs.rs/async_executors" ) ] 5 | #![ deny ( missing_docs ) ] 6 | #![ forbid( unsafe_code ) ] 7 | #![ allow ( clippy::suspicious_else_formatting ) ] 8 | 9 | #![ warn 10 | ( 11 | anonymous_parameters , 12 | missing_copy_implementations , 13 | missing_debug_implementations , 14 | nonstandard_style , 15 | rust_2018_idioms , 16 | single_use_lifetimes , 17 | trivial_casts , 18 | trivial_numeric_casts , 19 | unreachable_pub , 20 | unused_extern_crates , 21 | unused_qualifications , 22 | variant_size_differences , 23 | )] 24 | 25 | 26 | /// The executor implementations. 27 | // 28 | pub mod exec; 29 | 30 | /// The traits exposed by this crate. 31 | // 32 | pub mod iface; 33 | 34 | #[ cfg( any 35 | ( 36 | feature = "tokio_ct", 37 | feature = "tokio_tp", 38 | feature = "async_global", 39 | feature = "async_std", 40 | feature = "glommio", 41 | feature = "bindgen" 42 | )) ] 43 | pub use exec::*; 44 | pub use iface::*; 45 | 46 | // Re-export for convenience. 47 | // 48 | #[ cfg( feature = "localpool" ) ] pub use futures_executor::LocalPool; 49 | #[ cfg( feature = "localpool" ) ] pub use futures_executor::LocalSpawner; 50 | #[ cfg( feature = "threadpool" ) ] pub use futures_executor::ThreadPool; 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /tests/async_global.rs: -------------------------------------------------------------------------------- 1 | #![ cfg(all( feature = "async_global", not(target_os = "unknown") )) ] 2 | // 3 | // Tested: 4 | // 5 | // ✔ pass a AsyncGlobal to a function that takes exec: `impl Spawn` 6 | // ✔ pass a &AsyncGlobal to a function that takes exec: `&impl Spawn` 7 | // ✔ pass a &AsyncGlobal to a function that takes exec: `impl Spawn` 8 | // ✔ pass a &AsyncGlobal to a function that takes exec: `impl Spawn + Clone` 9 | // ✔ pass a Arc to a function that takes exec: `impl Spawn` 10 | // ✔ pass a AsyncGlobal to a function that takes exec: `impl SpawnHandle` 11 | // ✔ pass a Arc to a function that takes exec: `impl SpawnHandle` 12 | // ✔ pass a &AsyncGlobal to a function that takes exec: `&dyn SpawnHandle` 13 | // 14 | // ✔ pass a AsyncGlobal to a function that takes exec: `impl LocalSpawn` 15 | // ✔ pass a &AsyncGlobal to a function that takes exec: `&impl LocalSpawn` 16 | // ✔ pass a &AsyncGlobal to a function that takes exec: `impl LocalSpawn` 17 | // ✔ pass a &AsyncGlobal to a function that takes exec: `impl LocalSpawn + Clone` 18 | // ✔ pass a Arc to a function that takes exec: `impl LocalSpawn`. 19 | // ✔ pass a AsyncGlobal to a function that takes exec: `impl LocalSpawnHandle` 20 | // ✔ pass an Rc to a function that takes exec: `impl LocalSpawnHandle` 21 | // ✔ pass a &AsyncGlobal to a function that takes exec: `&dyn LocalSpawnHandle` 22 | // 23 | // ✔ pass an AsyncGlobal to a function that requires a SpawnBlocking. 24 | // ✔ pass an AsyncGlobal to a function that requires an object safe SpawnBlocking. 25 | // ✔ pass an AsyncGlobal to a function that requires a Timer. 26 | // ✔ Verify AsyncGlobal does not implement Timer when feature is not enabled. 27 | // ✔ Verify Timeout future. 28 | // 29 | // ✔ Verify tokio_io works when the async_global_tokio feature is enabled. 30 | // ✔ Verify tokio_io doesn't work when the async_global_tokio feature is not enabled. 31 | // 32 | // ✔ Joinhandle::detach allows task to keep running. 33 | // ✔ Joinhandle::drop aborts the task. 34 | // 35 | mod common; 36 | 37 | use 38 | { 39 | common :: { * } , 40 | futures :: { channel::mpsc, StreamExt } , 41 | std :: { time::Duration } , 42 | futures_timer :: { Delay } , 43 | }; 44 | 45 | 46 | // pass a AsyncGlobal to a function that takes exec: `impl Spawn` 47 | // 48 | #[ test ] 49 | // 50 | fn spawn() 51 | { 52 | let (tx, mut rx) = mpsc::channel( 1 ); 53 | let exec = AsyncGlobal; 54 | 55 | increment( 4, exec, tx ); 56 | 57 | let result = AsyncGlobal::block_on( rx.next() ).expect( "Some" ); 58 | 59 | assert_eq!( 5u8, result ); 60 | } 61 | 62 | 63 | // pass a &AsyncGlobal to a function that takes exec: `&impl Spawn` 64 | // 65 | #[ test ] 66 | // 67 | fn spawn_ref() 68 | { 69 | let (tx, mut rx) = mpsc::channel( 1 ); 70 | let exec = AsyncGlobal; 71 | 72 | increment_ref( 4, &exec, tx ); 73 | 74 | let result = AsyncGlobal::block_on( rx.next() ).expect( "Some" ); 75 | 76 | assert_eq!( 5u8, result ); 77 | } 78 | 79 | 80 | // pass a &AsyncGlobal to a function that takes exec: `impl Spawn` 81 | // 82 | #[ test ] 83 | // 84 | fn spawn_with_ref() 85 | { 86 | let (tx, mut rx) = mpsc::channel( 1 ); 87 | let exec = AsyncGlobal; 88 | 89 | #[allow(clippy::needless_borrow)] 90 | increment( 4, &exec, tx ); 91 | 92 | let result = AsyncGlobal::block_on( rx.next() ).expect( "Some" ); 93 | 94 | assert_eq!( 5u8, result ); 95 | } 96 | 97 | 98 | // pass a &AsyncGlobal to a function that takes exec: `impl Spawn + Clone` 99 | // 100 | #[ test ] 101 | // 102 | fn spawn_clone_with_ref() 103 | { 104 | let (tx, mut rx) = mpsc::channel( 1 ); 105 | let exec = AsyncGlobal; 106 | 107 | increment_clone( 4, &exec, tx ); 108 | 109 | let result = AsyncGlobal::block_on( rx.next() ).expect( "Some" ); 110 | 111 | assert_eq!( 5u8, result ); 112 | } 113 | 114 | 115 | // pass a Arc to a function that takes exec: `impl Spawn`. 116 | // Possible since futures 0.3.2. 117 | // 118 | #[ test ] 119 | // 120 | fn spawn_clone_with_arc() 121 | { 122 | let (tx, mut rx) = mpsc::channel( 1 ); 123 | let exec = AsyncGlobal; 124 | 125 | increment( 4, Arc::new(exec), tx ); 126 | 127 | let result = AsyncGlobal::block_on( rx.next() ).expect( "Some" ); 128 | 129 | assert_eq!( 5u8, result ); 130 | } 131 | 132 | 133 | // pass a AsyncGlobal to a function that takes exec: `impl SpawnHandle` 134 | // 135 | #[ test ] 136 | // 137 | fn spawn_handle() 138 | { 139 | let exec = AsyncGlobal; 140 | let result = AsyncGlobal::block_on( increment_spawn_handle( 4, exec ) ); 141 | 142 | assert_eq!( 5u8, result ); 143 | } 144 | 145 | 146 | // pass an Arc to a function that takes exec: `impl SpawnHandle` 147 | // 148 | #[ test ] 149 | // 150 | fn spawn_handle_arc() 151 | { 152 | let exec = AsyncGlobal; 153 | let result = AsyncGlobal::block_on( increment_spawn_handle( 4, Arc::new(exec) ) ); 154 | 155 | assert_eq!( 5u8, result ); 156 | } 157 | 158 | 159 | // pass a AsyncGlobal to a function that takes exec: `&dyn SpawnHandle` 160 | // 161 | #[ test ] 162 | // 163 | fn spawn_handle_os() 164 | { 165 | let exec = AsyncGlobal; 166 | let result = AsyncGlobal::block_on( increment_spawn_handle_os( 4, &exec ) ); 167 | 168 | assert_eq!( 5u8, result ); 169 | } 170 | 171 | 172 | struct DropNotify 173 | { 174 | tx: Option>, 175 | } 176 | 177 | impl Drop for DropNotify 178 | { 179 | fn drop( &mut self ) 180 | { 181 | self.tx.take().unwrap().send(()).expect( "notify drop" ); 182 | } 183 | } 184 | 185 | 186 | // Joinhandle::drop aborts the task. 187 | // Make sure that a task that is currently waiting for it's waker to be woken up 188 | // get's dropped when JoinHandle is dropped. 189 | // 190 | #[ test ] 191 | // 192 | fn join_handle_abort() 193 | { 194 | let exec = AsyncGlobal; 195 | let (tx , rx) = oneshot::channel::<()>(); 196 | 197 | let join_handle = exec.spawn_handle( async move 198 | { 199 | let _notify = DropNotify{ tx: Some(tx) }; 200 | 201 | // This will never end. 202 | // 203 | #[allow(clippy::let_unit_value)] 204 | let () = std::future::pending().await; 205 | 206 | }).expect( "spawn task" ); 207 | 208 | AsyncGlobal::block_on( async move 209 | { 210 | // Don't drop the handle before the task is scheduled by the executor. 211 | // 212 | Delay::new( Duration::from_millis(10) ).await; 213 | 214 | drop( join_handle ); 215 | 216 | // This should not deadlock. 217 | // 218 | assert!( rx.await.is_ok() ); 219 | }); 220 | } 221 | 222 | 223 | // Joinhandle::detach does not aborts the task. 224 | // 225 | #[ test ] 226 | // 227 | fn join_handle_detach() 228 | { 229 | let exec = AsyncGlobal; 230 | let (out_tx , out_rx) = oneshot::channel::<()>(); 231 | let (in_tx , in_rx ) = oneshot::channel::<()>(); 232 | 233 | let join_handle = exec.spawn_handle( async move 234 | { 235 | in_rx.await.expect( "receive in" ); 236 | 237 | out_tx.send(()).expect( "send out" ); 238 | 239 | }).expect( "spawn task" ); 240 | 241 | 242 | AsyncGlobal::block_on( async move 243 | { 244 | // This will drop the handle. 245 | // 246 | join_handle.detach(); 247 | 248 | // When commenting out this line, the next one does hang. 249 | // 250 | in_tx.send(()).expect( "send in" ); 251 | 252 | // This should not deadlock. 253 | // 254 | assert!( out_rx.await.is_ok() ); 255 | }); 256 | } 257 | 258 | 259 | // ------------------ Local 260 | // 261 | 262 | 263 | // pass a AsyncGlobal to a function that takes exec: `impl LocalSpawn` 264 | // 265 | #[ test ] 266 | // 267 | fn spawn_local() 268 | { 269 | let (tx, mut rx) = mpsc::channel( 1 ); 270 | 271 | let res = AsyncGlobal::block_on( async 272 | { 273 | increment_local( 4, AsyncGlobal, tx ); 274 | 275 | rx.next().await.expect( "Some" ) 276 | }); 277 | 278 | assert_eq!( 5u8, res ); 279 | } 280 | 281 | 282 | // pass a &AsyncGlobal to a function that takes exec: `&impl LocalSpawn` 283 | // 284 | #[ test ] 285 | // 286 | fn spawn_ref_local() 287 | { 288 | let (tx, mut rx) = mpsc::channel( 1 ); 289 | 290 | let res = AsyncGlobal::block_on( async 291 | { 292 | increment_ref_local( 4, &AsyncGlobal, tx ); 293 | 294 | rx.next().await.expect( "Some" ) 295 | }); 296 | 297 | assert_eq!( 5u8, res ); 298 | } 299 | 300 | 301 | // pass a &AsyncGlobal to a function that takes exec: `impl LocalSpawn` 302 | // 303 | #[ test ] 304 | // 305 | fn spawn_with_ref_local() 306 | { 307 | let (tx, mut rx) = mpsc::channel( 1 ); 308 | 309 | let res = AsyncGlobal::block_on( async 310 | { 311 | #[allow(clippy::needless_borrow)] 312 | increment_local( 4, &AsyncGlobal, tx ); 313 | 314 | rx.next().await.expect( "Some" ) 315 | }); 316 | 317 | assert_eq!( 5u8, res ); 318 | } 319 | 320 | 321 | // pass a &AsyncGlobal to a function that takes exec: `impl LocalSpawn + Clone` 322 | // 323 | #[ test ] 324 | // 325 | fn spawn_clone_with_ref_local() 326 | { 327 | let (tx, mut rx) = mpsc::channel( 1 ); 328 | 329 | let res = AsyncGlobal::block_on( async 330 | { 331 | increment_clone_local( 4, &AsyncGlobal, tx ); 332 | 333 | rx.next().await.expect( "Some" ) 334 | }); 335 | 336 | assert_eq!( 5u8, res ); 337 | } 338 | 339 | 340 | 341 | 342 | // pass a Arc to a function that takes exec: `impl LocalSpawn`. 343 | // Possible since futures 0.3.2. 344 | // 345 | #[ test ] 346 | // 347 | fn spawn_clone_with_rc_local() 348 | { 349 | let (tx, mut rx) = mpsc::channel( 1 ); 350 | 351 | let res = AsyncGlobal::block_on( async 352 | { 353 | increment_clone_local( 4, Rc::new( AsyncGlobal ), tx ); 354 | 355 | rx.next().await.expect( "Some" ) 356 | }); 357 | 358 | assert_eq!( 5u8, res ); 359 | } 360 | 361 | 362 | // pass a AsyncGlobal to a function that takes exec: `impl LocalSpawnHandle` 363 | // 364 | #[ test ] 365 | // 366 | fn spawn_handle_local() 367 | { 368 | let res = AsyncGlobal::block_on( increment_spawn_handle_local( 4, AsyncGlobal ) ); 369 | 370 | assert_eq!( 5u8, *res ); 371 | } 372 | 373 | 374 | // pass an Rc to a function that takes exec: `impl LocalSpawnHandle` 375 | // 376 | #[ test ] 377 | // 378 | fn spawn_handle_rc_local() 379 | { 380 | let res = AsyncGlobal::block_on( increment_spawn_handle_local( 4, Rc::new( AsyncGlobal ) ) ); 381 | 382 | assert_eq!( 5u8, *res ); 383 | } 384 | 385 | 386 | 387 | // pass a &AsyncGlobal to a function that takes exec: `&dyn LocalSpawnHandle` 388 | // 389 | #[ test ] 390 | // 391 | fn spawn_handle_local_os() 392 | { 393 | let result = AsyncGlobal::block_on( increment_spawn_handle_os( 4, &AsyncGlobal ) ); 394 | 395 | assert_eq!( 5u8, result ); 396 | } 397 | 398 | 399 | 400 | // pass an AsyncGlobal to a function that requires a Timer. 401 | // 402 | #[ cfg( feature = "timer" ) ] 403 | // 404 | #[ test ] 405 | // 406 | fn timer_should_wake() 407 | { 408 | AsyncGlobal::block_on( timer_should_wake_up( AsyncGlobal ) ); 409 | } 410 | 411 | 412 | 413 | // pass an AsyncGlobal to a function that requires a SpawnBlocking. 414 | // 415 | #[ test ] 416 | // 417 | fn spawn_blocking() -> DynResult<()> 418 | { 419 | AsyncGlobal::block_on( blocking( AsyncGlobal ) ) 420 | } 421 | 422 | 423 | 424 | // pass an AsyncGlobal to a function that requires a SpawnBlocking. 425 | // 426 | #[ test ] 427 | // 428 | fn spawn_blocking_void() -> DynResult<()> 429 | { 430 | AsyncGlobal::block_on( blocking_void( &AsyncGlobal ) ) 431 | } 432 | 433 | 434 | 435 | // pass an AsyncGlobal to a function that requires a Timer. 436 | // 437 | #[ cfg( feature = "timer" ) ] 438 | // 439 | #[ test ] 440 | // 441 | fn timer_should_wake_local() 442 | { 443 | AsyncGlobal::block_on( timer_should_wake_up_local( AsyncGlobal ) ); 444 | } 445 | 446 | 447 | 448 | // pass an AsyncGlobal to a function that requires a Timer. 449 | // 450 | #[ cfg( feature = "timer" ) ] 451 | // 452 | #[ test ] 453 | // 454 | fn run_timeout() 455 | { 456 | AsyncGlobal::block_on( timeout( AsyncGlobal ) ); 457 | } 458 | 459 | 460 | 461 | // pass an AsyncGlobal to a function that requires a Timer. 462 | // 463 | #[ cfg( feature = "timer" ) ] 464 | // 465 | #[ test ] 466 | // 467 | fn run_dont_timeout() 468 | { 469 | AsyncGlobal::block_on( dont_timeout( AsyncGlobal ) ); 470 | } 471 | 472 | 473 | 474 | // Verify AsyncGlobal does not implement Timer when feature is not enabled. 475 | // 476 | #[ cfg(not( feature = "timer" )) ] 477 | // 478 | #[ test ] 479 | // 480 | fn no_feature_no_timer() 481 | { 482 | static_assertions::assert_not_impl_any!( AsyncGlobal: Timer ); 483 | } 484 | 485 | 486 | 487 | // Verify tokio_io works when the async_global_tokio feature is enabled. 488 | // 489 | #[ cfg(all( not(target_arch = "wasm32"), feature = "async_global_tokio" )) ] 490 | // 491 | #[ test ] 492 | // 493 | fn tokio_io() -> DynResult<()> 494 | { 495 | AsyncGlobal::block_on( tokio_io::tcp( AsyncGlobal ) ) 496 | } 497 | 498 | 499 | 500 | // Verify tokio_io doesn't work when the async_global_tokio feature is not enabled. 501 | // 502 | #[ cfg(all( not(target_arch = "wasm32"), not(feature = "async_global_tokio") )) ] 503 | // 504 | #[ test ] #[ should_panic ] 505 | // 506 | fn no_tokio_io() 507 | { 508 | let test = async 509 | { 510 | let _ = tokio_io::socket_pair().await.expect( "socket_pair" ); 511 | }; 512 | 513 | AsyncGlobal::block_on( test ); 514 | } 515 | -------------------------------------------------------------------------------- /tests/async_global_wasm.rs: -------------------------------------------------------------------------------- 1 | #![ cfg(all( feature = "async_global", target_arch = "wasm32" )) ] 2 | 3 | // Tested: 4 | // 5 | // ✔ pass a AsyncGlobal to a function that takes exec: `impl Spawn` 6 | // ✔ pass a &AsyncGlobal to a function that takes exec: `&impl Spawn` 7 | // ✔ pass a &AsyncGlobal to a function that takes exec: `impl Spawn` 8 | // ✔ pass a &AsyncGlobal to a function that takes exec: `impl Spawn + Clone` 9 | // ✔ pass a Arc to a function that takes exec: `impl Spawn` 10 | // ✔ pass a AsyncGlobal to a function that takes exec: `impl SpawnHandle` 11 | // ✔ pass a Arc to a function that takes exec: `impl SpawnHandle` 12 | // ✔ pass a &AsyncGlobal to a function that takes exec: `&dyn SpawnHandle` 13 | // 14 | // 15 | // ✔ pass a AsyncGlobal to a function that takes exec: `impl LocalSpawn` 16 | // ✔ pass a &AsyncGlobal to a function that takes exec: `&impl LocalSpawn` 17 | // ✔ pass a &AsyncGlobal to a function that takes exec: `impl LocalSpawn` 18 | // ✔ pass a &AsyncGlobal to a function that takes exec: `impl LocalSpawn + Clone` 19 | // ✔ pass a Rc to a function that takes exec: `impl LocalSpawn` 20 | // ✔ pass a AsyncGlobal to a function that takes exec: `impl LocalSpawnHandle` 21 | // ✔ pass a Rc to a function that takes exec: `impl LocalSpawnHandle` 22 | // ✔ pass a &AsyncGlobal to a function that takes exec: `&dyn LocalSpawnHandle` 23 | // 24 | // ✔ pass an AsyncGlobal to a function that requires a YieldNow. 25 | // ✔ pass an AsyncGlobal to a function that requires a Timer. 26 | // ✔ Verify Timeout future. 27 | // 28 | mod common; 29 | 30 | use 31 | { 32 | common :: { * } , 33 | futures :: { channel::mpsc, StreamExt } , 34 | wasm_bindgen_test :: { * } , 35 | }; 36 | 37 | wasm_bindgen_test_configure!( run_in_browser ); 38 | 39 | 40 | 41 | // pass a AsyncGlobal to a function that takes exec: `impl Spawn` 42 | // 43 | #[ wasm_bindgen_test ] 44 | // 45 | fn spawn() 46 | { 47 | let (tx, mut rx) = mpsc::channel( 1 ); 48 | 49 | increment( 4, AsyncGlobal, tx ); 50 | 51 | let fut = async move 52 | { 53 | let result = rx.next().await.expect( "Some" ); 54 | 55 | assert_eq!( 5u8, result ); 56 | }; 57 | 58 | AsyncGlobal.spawn( fut ).expect( "spawn future" ); 59 | } 60 | 61 | 62 | // pass a &AsyncGlobal to a function that takes exec: `&impl Spawn` 63 | // 64 | #[ wasm_bindgen_test ] 65 | // 66 | fn spawn_ref() 67 | { 68 | let (tx, mut rx) = mpsc::channel( 1 ); 69 | 70 | increment_ref( 4, &AsyncGlobal, tx ); 71 | 72 | let fut = async move 73 | { 74 | let result = rx.next().await.expect( "Some" ); 75 | 76 | assert_eq!( 5u8, result ); 77 | }; 78 | 79 | AsyncGlobal.spawn( fut ).expect( "spawn future" ); 80 | } 81 | 82 | 83 | // pass a &AsyncGlobal to a function that takes exec: `impl Spawn` 84 | // 85 | #[ wasm_bindgen_test ] 86 | // 87 | fn spawn_with_ref() 88 | { 89 | let (tx, mut rx) = mpsc::channel( 1 ); 90 | 91 | #[allow(clippy::needless_borrow)] 92 | increment( 4, &AsyncGlobal, tx ); 93 | 94 | let fut = async move 95 | { 96 | let result = rx.next().await.expect( "Some" ); 97 | 98 | assert_eq!( 5u8, result ); 99 | }; 100 | 101 | AsyncGlobal.spawn( fut ).expect( "spawn future" ); 102 | } 103 | 104 | 105 | // pass a &AsyncGlobal to a function that takes exec: `impl Spawn + Clone` 106 | // 107 | #[ wasm_bindgen_test ] 108 | // 109 | fn spawn_clone_with_ref() 110 | { 111 | let (tx, mut rx) = mpsc::channel( 1 ); 112 | 113 | increment_clone( 4, &AsyncGlobal, tx ); 114 | 115 | let fut = async move 116 | { 117 | let result = rx.next().await.expect( "Some" ); 118 | 119 | assert_eq!( 5u8, result ); 120 | }; 121 | 122 | AsyncGlobal.spawn( fut ).expect( "spawn future" ); 123 | } 124 | 125 | 126 | // pass a Arc to a function that takes exec: `impl Spawn`. 127 | // Possible since futures 0.3.2. 128 | // 129 | #[ wasm_bindgen_test ] 130 | // 131 | fn spawn_clone_with_arc() 132 | { 133 | let (tx, mut rx) = mpsc::channel( 1 ); 134 | 135 | increment( 4, Arc::new(AsyncGlobal), tx ); 136 | 137 | let fut = async move 138 | { 139 | let result = rx.next().await.expect( "Some" ); 140 | 141 | assert_eq!( 5u8, result ); 142 | }; 143 | 144 | AsyncGlobal.spawn( fut ).expect( "spawn future" ); 145 | } 146 | 147 | 148 | // pass a AsyncGlobal to a function that takes exec: `impl SpawnHandle` 149 | // 150 | #[ wasm_bindgen_test ] 151 | // 152 | fn spawn_handle() 153 | { 154 | let fut = async move 155 | { 156 | let result = increment_spawn_handle( 4, AsyncGlobal ).await; 157 | 158 | assert_eq!( 5u8, result ); 159 | }; 160 | 161 | AsyncGlobal.spawn( fut ).expect( "spawn future" ); 162 | } 163 | 164 | 165 | // pass an Arc to a function that takes exec: `impl SpawnHandle` 166 | // 167 | #[ wasm_bindgen_test ] 168 | // 169 | fn spawn_handle_arc() 170 | { 171 | let fut = async move 172 | { 173 | let result = increment_spawn_handle( 4, Arc::new(AsyncGlobal) ).await; 174 | 175 | assert_eq!( 5u8, result ); 176 | }; 177 | 178 | AsyncGlobal.spawn( fut ).expect( "spawn future" ); 179 | } 180 | 181 | 182 | // pass a &AsyncGlobal to a function that takes exec: `&dyn SpawnHandle` 183 | // 184 | #[ wasm_bindgen_test ] 185 | // 186 | fn spawn_handle_os() 187 | { 188 | let fut = async move 189 | { 190 | let result = increment_spawn_handle_os( 4, &AsyncGlobal ).await; 191 | 192 | assert_eq!( 5u8, result ); 193 | }; 194 | 195 | AsyncGlobal.spawn_local( fut ).expect( "spawn future" ); 196 | } 197 | 198 | 199 | 200 | //----------------------Local 201 | 202 | 203 | 204 | // pass a AsyncGlobal to a function that takes exec: `impl LocalSpawn` 205 | // 206 | #[ wasm_bindgen_test ] 207 | // 208 | fn spawn_local() 209 | { 210 | let (tx, mut rx) = mpsc::channel( 1 ); 211 | increment_local( 4, AsyncGlobal, tx ); 212 | 213 | let fut = async move 214 | { 215 | let result = rx.next().await.expect( "Some" ); 216 | 217 | assert_eq!( 5u8, result ); 218 | }; 219 | 220 | AsyncGlobal.spawn_local( fut ).expect( "spawn future" ); 221 | } 222 | 223 | 224 | // pass a &AsyncGlobal to a function that takes exec: `&impl LocalSpawn` 225 | // 226 | #[ wasm_bindgen_test ] 227 | // 228 | fn spawn_ref_local() 229 | { 230 | let (tx, mut rx) = mpsc::channel( 1 ); 231 | increment_ref_local( 4, &AsyncGlobal, tx ); 232 | 233 | let fut = async move 234 | { 235 | let result = rx.next().await.expect( "Some" ); 236 | 237 | assert_eq!( 5u8, result ); 238 | }; 239 | 240 | AsyncGlobal.spawn_local( fut ).expect( "spawn future" ); 241 | } 242 | 243 | 244 | // pass a &AsyncGlobal to a function that takes exec: `impl LocalSpawn` 245 | // 246 | #[ wasm_bindgen_test ] 247 | // 248 | fn spawn_with_ref_local() 249 | { 250 | let (tx, mut rx) = mpsc::channel( 1 ); 251 | 252 | #[allow(clippy::needless_borrow)] 253 | increment_local( 4, &AsyncGlobal, tx ); 254 | 255 | let fut = async move 256 | { 257 | let result = rx.next().await.expect( "Some" ); 258 | 259 | assert_eq!( 5u8, result ); 260 | }; 261 | 262 | AsyncGlobal.spawn_local( fut ).expect( "spawn future" ); 263 | } 264 | 265 | 266 | // pass a &AsyncGlobal to a function that takes exec: `impl LocalSpawn + Clone` 267 | // 268 | #[ wasm_bindgen_test ] 269 | // 270 | fn spawn_clone_with_ref_local() 271 | { 272 | let (tx, mut rx) = mpsc::channel( 1 ); 273 | increment_clone_local( 4, &AsyncGlobal, tx ); 274 | 275 | let fut = async move 276 | { 277 | let result = rx.next().await.expect( "Some" ); 278 | 279 | assert_eq!( 5u8, result ); 280 | }; 281 | 282 | AsyncGlobal.spawn_local( fut ).expect( "spawn future" ); 283 | } 284 | 285 | 286 | // pass a Arc to a function that takes exec: `impl LocalSpawn`. 287 | // Possible since futures 0.3.2. 288 | // 289 | #[ wasm_bindgen_test ] 290 | // 291 | fn spawn_clone_with_arc_local() 292 | { 293 | let (tx, mut rx) = mpsc::channel( 1 ); 294 | increment_local( 4, Arc::new(AsyncGlobal), tx ); 295 | 296 | let fut = async move 297 | { 298 | let result = rx.next().await.expect( "Some" ); 299 | 300 | assert_eq!( 5u8, result ); 301 | }; 302 | 303 | AsyncGlobal.spawn_local( fut ).expect( "spawn future" ); 304 | } 305 | 306 | 307 | // pass a AsyncGlobal to a function that takes exec: `impl LocalSpawnHandle` 308 | // 309 | #[ wasm_bindgen_test ] 310 | // 311 | fn spawn_handle_local() 312 | { 313 | let fut = async move 314 | { 315 | let result = increment_spawn_handle_local( 4, AsyncGlobal ).await; 316 | 317 | assert_eq!( 5u8, *result ); 318 | }; 319 | 320 | AsyncGlobal.spawn_local( fut ).expect( "spawn future" ); 321 | } 322 | 323 | 324 | // pass an Arc to a function that takes exec: `impl LocalSpawnHandle` 325 | // 326 | #[ wasm_bindgen_test ] 327 | // 328 | fn spawn_handle_arc_local() 329 | { 330 | let fut = async move 331 | { 332 | let result = increment_spawn_handle_local( 4, Arc::new(AsyncGlobal) ).await; 333 | 334 | assert_eq!( 5u8, *result ); 335 | }; 336 | 337 | AsyncGlobal.spawn_local( fut ).expect( "spawn future" ); 338 | } 339 | 340 | 341 | // pass a &AsyncGlobal to a function that takes exec: `&dyn LocalSpawnHandle` 342 | // 343 | #[ wasm_bindgen_test ] 344 | // 345 | fn spawn_handle_os_local() 346 | { 347 | let fut = async move 348 | { 349 | let result = increment_spawn_handle_local_os( 4, &AsyncGlobal ).await; 350 | 351 | assert_eq!( 5u8, *result ); 352 | }; 353 | 354 | AsyncGlobal.spawn_local( fut ).expect( "spawn future" ); 355 | } 356 | 357 | 358 | 359 | // pass a AsyncGlobal to a function that requires a YieldNow. 360 | // 361 | #[ wasm_bindgen_test ] 362 | // 363 | fn yield_run_subtask_first() 364 | { 365 | let task = async{ try_yield_now( AsyncGlobal ).await.expect( "yield_now" ); }; 366 | 367 | AsyncGlobal.spawn_local( task ).expect( "spawn" ); 368 | } 369 | 370 | 371 | 372 | // pass a AsyncGlobal to a function that requires a YieldNow. 373 | // 374 | #[ wasm_bindgen_test ] 375 | // 376 | fn yield_run_subtask_last() 377 | { 378 | let task = async{ without_yield_now( AsyncGlobal ).await.expect( "yield_now" ); }; 379 | 380 | AsyncGlobal.spawn_local( task ).expect( "spawn" ); 381 | } 382 | 383 | 384 | 385 | // pass an AsyncGlobal to a function that requires a Timer. 386 | // 387 | #[ cfg( feature = "timer" ) ] 388 | // 389 | #[ wasm_bindgen_test ] 390 | // 391 | fn timer_should_wake_local() 392 | { 393 | AsyncGlobal.spawn_local( timer_should_wake_up_local( AsyncGlobal ) ).expect( "spawn" ); 394 | } 395 | 396 | 397 | 398 | // Verify timeout future. 399 | // 400 | #[ cfg( feature = "timer" ) ] 401 | // 402 | #[ wasm_bindgen_test ] 403 | // 404 | fn run_timeout() 405 | { 406 | AsyncGlobal.spawn_local( timeout( AsyncGlobal ) ).expect( "spawn" ); 407 | } 408 | 409 | 410 | 411 | // Verify timeout future. 412 | // 413 | #[ cfg( feature = "timer" ) ] 414 | // 415 | #[ wasm_bindgen_test ] 416 | // 417 | fn run_dont_timeout() 418 | { 419 | AsyncGlobal.spawn_local( dont_timeout( AsyncGlobal ) ).expect( "spawn" ); 420 | } 421 | 422 | -------------------------------------------------------------------------------- /tests/async_std.rs: -------------------------------------------------------------------------------- 1 | #![ cfg(all( feature = "async_std", not(target_os = "unknown") )) ] 2 | 3 | // Tested: 4 | // 5 | // ✔ pass a AsyncStd to a function that takes exec: `impl Spawn` 6 | // ✔ pass a &AsyncStd to a function that takes exec: `&impl Spawn` 7 | // ✔ pass a &AsyncStd to a function that takes exec: `impl Spawn` 8 | // ✔ pass a &AsyncStd to a function that takes exec: `impl Spawn + Clone` 9 | // ✔ pass a Arc to a function that takes exec: `impl Spawn` 10 | // ✔ pass a AsyncStd to a function that takes exec: `impl SpawnHandle` 11 | // ✔ pass a Arc to a function that takes exec: `impl SpawnHandle` 12 | // ✔ pass a &AsyncStd to a function that takes exec: `&dyn SpawnHandle` 13 | // 14 | // ✔ pass a AsyncStd to a function that takes exec: `impl LocalSpawn` 15 | // ✔ pass a &AsyncStd to a function that takes exec: `&impl LocalSpawn` 16 | // ✔ pass a &AsyncStd to a function that takes exec: `impl LocalSpawn` 17 | // ✔ pass a &AsyncStd to a function that takes exec: `impl LocalSpawn + Clone` 18 | // ✔ pass a Arc to a function that takes exec: `impl LocalSpawn` 19 | // ✔ pass a AsyncStd to a function that takes exec: `impl LocalSpawnHandle` 20 | // ✔ pass a Arc to a function that takes exec: `impl LocalSpawnHandle` 21 | // ✔ pass a &AsyncStd to a function that takes exec: `&dyn LocalSpawnHandle` 22 | // 23 | // ✔ pass an AsyncStd to a function that requires a SpawnBlocking. 24 | // ✔ pass an AsyncStd to a function that requires an object safe SpawnBlocking. 25 | // ✔ pass an AsyncStd to a function that requires a Timer. 26 | // ✔ Verify tokio_io works when the async_std_tokio feature is enabled. 27 | // ✔ Verify tokio_io doesn't work when the async_std_tokio feature is not enabled. 28 | // ✔ Verify Timeout future. 29 | // 30 | // ✔ Joinhandle::detach allows task to keep running. 31 | // ✔ Joinhandle::drop aborts the task. 32 | // 33 | mod common; 34 | 35 | use 36 | { 37 | common :: { * } , 38 | futures :: { channel::mpsc, StreamExt } , 39 | std :: { time::Duration } , 40 | futures_timer :: { Delay } , 41 | }; 42 | 43 | 44 | // pass a AsyncStd to a function that takes exec: `impl Spawn` 45 | // 46 | #[ test ] 47 | // 48 | fn spawn() 49 | { 50 | let (tx, mut rx) = mpsc::channel( 1 ); 51 | let exec = AsyncStd; 52 | 53 | increment( 4, exec, tx ); 54 | 55 | let result = AsyncStd::block_on( rx.next() ).expect( "Some" ); 56 | 57 | assert_eq!( 5u8, result ); 58 | } 59 | 60 | 61 | // pass a &AsyncStd to a function that takes exec: `&impl Spawn` 62 | // 63 | #[ test ] 64 | // 65 | fn spawn_ref() 66 | { 67 | let (tx, mut rx) = mpsc::channel( 1 ); 68 | let exec = AsyncStd; 69 | 70 | increment_ref( 4, &exec, tx ); 71 | 72 | let result = AsyncStd::block_on( rx.next() ).expect( "Some" ); 73 | 74 | assert_eq!( 5u8, result ); 75 | } 76 | 77 | 78 | // pass a &AsyncStd to a function that takes exec: `impl Spawn` 79 | // 80 | #[ test ] 81 | // 82 | fn spawn_with_ref() 83 | { 84 | let (tx, mut rx) = mpsc::channel( 1 ); 85 | let exec = AsyncStd; 86 | 87 | #[allow(clippy::needless_borrow)] 88 | increment( 4, &exec, tx ); 89 | 90 | let result = AsyncStd::block_on( rx.next() ).expect( "Some" ); 91 | 92 | assert_eq!( 5u8, result ); 93 | } 94 | 95 | 96 | // pass a &AsyncStd to a function that takes exec: `impl Spawn + Clone` 97 | // 98 | #[ test ] 99 | // 100 | fn spawn_clone_with_ref() 101 | { 102 | let (tx, mut rx) = mpsc::channel( 1 ); 103 | let exec = AsyncStd; 104 | 105 | increment_clone( 4, &exec, tx ); 106 | 107 | let result = AsyncStd::block_on( rx.next() ).expect( "Some" ); 108 | 109 | assert_eq!( 5u8, result ); 110 | } 111 | 112 | 113 | // pass a Arc to a function that takes exec: `impl Spawn`. 114 | // Possible since futures 0.3.2. 115 | // 116 | #[ test ] 117 | // 118 | fn spawn_clone_with_arc() 119 | { 120 | let (tx, mut rx) = mpsc::channel( 1 ); 121 | let exec = AsyncStd; 122 | 123 | increment( 4, Arc::new(exec), tx ); 124 | 125 | let result = AsyncStd::block_on( rx.next() ).expect( "Some" ); 126 | 127 | assert_eq!( 5u8, result ); 128 | } 129 | 130 | 131 | // pass a AsyncStd to a function that takes exec: `impl SpawnHandle` 132 | // 133 | #[ test ] 134 | // 135 | fn spawn_handle() 136 | { 137 | let exec = AsyncStd; 138 | let result = AsyncStd::block_on( increment_spawn_handle( 4, exec ) ); 139 | 140 | assert_eq!( 5u8, result ); 141 | } 142 | 143 | 144 | // pass an Arc to a function that takes exec: `impl SpawnHandle` 145 | // 146 | #[ test ] 147 | // 148 | fn spawn_handle_arc() 149 | { 150 | let exec = AsyncStd; 151 | let result = AsyncStd::block_on( increment_spawn_handle( 4, Arc::new(exec) ) ); 152 | 153 | assert_eq!( 5u8, result ); 154 | } 155 | 156 | 157 | // pass a AsyncStd to a function that takes exec: `&dyn SpawnHandle` 158 | // 159 | #[ test ] 160 | // 161 | fn spawn_handle_os() 162 | { 163 | let exec = AsyncStd; 164 | let result = AsyncStd::block_on( increment_spawn_handle_os( 4, &exec ) ); 165 | 166 | assert_eq!( 5u8, result ); 167 | } 168 | 169 | 170 | struct DropNotify 171 | { 172 | tx: Option>, 173 | } 174 | 175 | impl Drop for DropNotify 176 | { 177 | fn drop( &mut self ) 178 | { 179 | self.tx.take().unwrap().send(()).expect( "notify drop" ); 180 | } 181 | } 182 | 183 | 184 | // Joinhandle::drop aborts the task. 185 | // Make sure that a task that is currently waiting for it's waker to be woken up 186 | // get's dropped when JoinHandle is dropped. 187 | // 188 | #[ test ] 189 | // 190 | fn join_handle_abort() 191 | { 192 | let (tx , rx) = oneshot::channel::<()>(); 193 | 194 | let join_handle = AsyncStd.spawn_handle( async move 195 | { 196 | let _notify = DropNotify{ tx: Some(tx) }; 197 | 198 | // This will never end. 199 | // 200 | #[allow(clippy::let_unit_value)] 201 | let () = futures::future::pending().await; 202 | 203 | }).expect( "spawn task" ); 204 | 205 | 206 | AsyncStd::block_on( async 207 | { 208 | // Don't drop the handle before the task is scheduled by the executor. 209 | // 210 | Delay::new( Duration::from_millis(10) ).await; 211 | 212 | drop( join_handle ); 213 | 214 | // This should not deadlock. 215 | // 216 | assert!( rx.await.is_ok() ); 217 | }) 218 | } 219 | 220 | 221 | // Joinhandle::detach does not aborts the task. 222 | // 223 | #[ test ] 224 | // 225 | fn join_handle_detach() 226 | { 227 | let (out_tx , out_rx) = oneshot::channel::<()>(); 228 | let (in_tx , in_rx ) = oneshot::channel::<()>(); 229 | 230 | let join_handle = AsyncStd.spawn_handle( async move 231 | { 232 | in_rx.await.expect( "receive in" ); 233 | 234 | out_tx.send(()).expect( "send out" ); 235 | 236 | }).expect( "spawn task" ); 237 | 238 | 239 | // This will drop the handle. 240 | // 241 | join_handle.detach(); 242 | 243 | // When commenting out this line, the next one does hang. 244 | // 245 | in_tx.send(()).expect( "send in" ); 246 | 247 | // This should not deadlock. 248 | // 249 | assert!( AsyncStd::block_on(out_rx).is_ok() ); 250 | } 251 | 252 | 253 | // ------------------ Local 254 | // 255 | 256 | 257 | // pass a AsyncStd to a function that takes exec: `impl LocalSpawn` 258 | // 259 | #[ test ] 260 | // 261 | fn spawn_local() 262 | { 263 | let (tx, mut rx) = mpsc::channel( 1 ); 264 | 265 | let res = AsyncStd::block_on( async 266 | { 267 | increment_local( 4, AsyncStd, tx ); 268 | 269 | rx.next().await.expect( "Some" ) 270 | }); 271 | 272 | assert_eq!( 5u8, res ); 273 | } 274 | 275 | 276 | // pass a &AsyncStd to a function that takes exec: `&impl LocalSpawn` 277 | // 278 | #[ test ] 279 | // 280 | fn spawn_ref_local() 281 | { 282 | let (tx, mut rx) = mpsc::channel( 1 ); 283 | 284 | let res = AsyncStd::block_on( async 285 | { 286 | increment_ref_local( 4, &AsyncStd, tx ); 287 | 288 | rx.next().await.expect( "Some" ) 289 | }); 290 | 291 | assert_eq!( 5u8, res ); 292 | } 293 | 294 | 295 | // pass a &AsyncStd to a function that takes exec: `impl LocalSpawn` 296 | // 297 | #[ test ] 298 | // 299 | fn spawn_with_ref_local() 300 | { 301 | let (tx, mut rx) = mpsc::channel( 1 ); 302 | 303 | let res = AsyncStd::block_on( async 304 | { 305 | #[allow(clippy::needless_borrow)] 306 | increment_local( 4, &AsyncStd, tx ); 307 | 308 | rx.next().await.expect( "Some" ) 309 | }); 310 | 311 | assert_eq!( 5u8, res ); 312 | } 313 | 314 | 315 | // pass a &AsyncStd to a function that takes exec: `impl LocalSpawn + Clone` 316 | // 317 | #[ test ] 318 | // 319 | fn spawn_clone_with_ref_local() 320 | { 321 | let (tx, mut rx) = mpsc::channel( 1 ); 322 | 323 | let res = AsyncStd::block_on( async 324 | { 325 | increment_clone_local( 4, &AsyncStd, tx ); 326 | 327 | rx.next().await.expect( "Some" ) 328 | }); 329 | 330 | assert_eq!( 5u8, res ); 331 | } 332 | 333 | 334 | 335 | 336 | // pass a Arc to a function that takes exec: `impl LocalSpawn`. 337 | // Possible since futures 0.3.2. 338 | // 339 | #[ test ] 340 | // 341 | fn spawn_clone_with_rc_local() 342 | { 343 | let (tx, mut rx) = mpsc::channel( 1 ); 344 | 345 | let res = AsyncStd::block_on( async 346 | { 347 | increment_clone_local( 4, Rc::new( AsyncStd ), tx ); 348 | 349 | rx.next().await.expect( "Some" ) 350 | }); 351 | 352 | assert_eq!( 5u8, res ); 353 | } 354 | 355 | 356 | // pass a AsyncStd to a function that takes exec: `impl LocalSpawnHandle` 357 | // 358 | #[ test ] 359 | // 360 | fn spawn_handle_local() 361 | { 362 | let res = AsyncStd::block_on( increment_spawn_handle_local( 4, AsyncStd ) ); 363 | 364 | assert_eq!( 5u8, *res ); 365 | } 366 | 367 | 368 | // pass an Rc to a function that takes exec: `impl LocalSpawnHandle` 369 | // 370 | #[ test ] 371 | // 372 | fn spawn_handle_rc_local() 373 | { 374 | let res = AsyncStd::block_on( increment_spawn_handle_local( 4, Rc::new( AsyncStd ) ) ); 375 | 376 | assert_eq!( 5u8, *res ); 377 | } 378 | 379 | 380 | 381 | // pass a &AsyncStd to a function that takes exec: `&dyn LocalSpawnHandle` 382 | // 383 | #[ test ] 384 | // 385 | fn spawn_handle_local_os() 386 | { 387 | let result = AsyncStd::block_on( increment_spawn_handle_os( 4, &AsyncStd ) ); 388 | 389 | assert_eq!( 5u8, result ); 390 | } 391 | 392 | 393 | 394 | // pass an AsyncStd to a function that requires a Timer. 395 | // 396 | #[ test ] 397 | // 398 | fn timer_should_wake() 399 | { 400 | AsyncStd::block_on( timer_should_wake_up( AsyncStd ) ); 401 | } 402 | 403 | 404 | 405 | // pass an AsyncStd to a function that requires a Timer. 406 | // 407 | #[ test ] 408 | // 409 | fn no_feature_no_timer() 410 | { 411 | AsyncStd::block_on( timer_should_wake_up_local( AsyncStd ) ); 412 | } 413 | 414 | 415 | 416 | // pass an AsyncStd to a function that requires a Timer. 417 | // 418 | #[ test ] 419 | // 420 | fn run_timeout() 421 | { 422 | AsyncStd::block_on( timeout( AsyncStd ) ); 423 | } 424 | 425 | 426 | 427 | // pass an AsyncStd to a function that requires a Timer. 428 | // 429 | #[ test ] 430 | // 431 | fn run_dont_timeout() 432 | { 433 | AsyncStd::block_on( dont_timeout( AsyncStd ) ); 434 | } 435 | 436 | 437 | 438 | // pass an AsyncStd to a function that requires a Timer. 439 | // 440 | #[ test ] 441 | // 442 | fn spawn_blocking() -> DynResult<()> 443 | { 444 | AsyncStd::block_on( blocking( AsyncStd ) ) 445 | } 446 | 447 | 448 | 449 | // pass an AsyncStd to a function that requires a SpawnBlocking. 450 | // 451 | #[ test ] 452 | // 453 | fn spawn_blocking_void() -> DynResult<()> 454 | { 455 | AsyncStd::block_on( blocking_void( &AsyncStd ) ) 456 | } 457 | 458 | 459 | 460 | // Verify tokio_io works when the async_std_tokio feature is enabled. 461 | // 462 | #[ cfg(all( not(target_arch = "wasm32"), feature = "async_std_tokio" )) ] 463 | // 464 | #[ test ] 465 | // 466 | fn tokio_io() -> DynResult<()> 467 | { 468 | AsyncStd::block_on( tokio_io::tcp( AsyncStd ) ) 469 | } 470 | 471 | 472 | // Verify tokio_io doesn't work when the async_std_tokio feature is not enabled. 473 | // 474 | #[ cfg(all( not(target_arch = "wasm32"), not(feature = "async_std_tokio") )) ] 475 | // 476 | #[ test ] #[ should_panic ] 477 | // 478 | fn no_tokio_io() 479 | { 480 | let test = async 481 | { 482 | let _ = tokio_io::socket_pair().await.expect( "socket_pair" ); 483 | }; 484 | 485 | AsyncStd::block_on( test ); 486 | } 487 | -------------------------------------------------------------------------------- /tests/async_std_wasm.rs: -------------------------------------------------------------------------------- 1 | #![ cfg(all( feature = "async_std", target_arch = "wasm32" )) ] 2 | 3 | // Tested: 4 | // 5 | // ✔ pass a AsyncStd to a function that takes exec: `impl Spawn` 6 | // ✔ pass a &AsyncStd to a function that takes exec: `&impl Spawn` 7 | // ✔ pass a &AsyncStd to a function that takes exec: `impl Spawn` 8 | // ✔ pass a &AsyncStd to a function that takes exec: `impl Spawn + Clone` 9 | // ✔ pass a Arc to a function that takes exec: `impl Spawn` 10 | // ✔ pass a AsyncStd to a function that takes exec: `impl SpawnHandle` 11 | // ✔ pass a Arc to a function that takes exec: `impl SpawnHandle` 12 | // ✔ pass a &AsyncStd to a function that takes exec: `&dyn SpawnHandle` 13 | // 14 | // 15 | // ✔ pass a AsyncStd to a function that takes exec: `impl LocalSpawn` 16 | // ✔ pass a &AsyncStd to a function that takes exec: `&impl LocalSpawn` 17 | // ✔ pass a &AsyncStd to a function that takes exec: `impl LocalSpawn` 18 | // ✔ pass a &AsyncStd to a function that takes exec: `impl LocalSpawn + Clone` 19 | // ✔ pass a Rc to a function that takes exec: `impl LocalSpawn` 20 | // ✔ pass a AsyncStd to a function that takes exec: `impl LocalSpawnHandle` 21 | // ✔ pass a Rc to a function that takes exec: `impl LocalSpawnHandle` 22 | // ✔ pass a &AsyncStd to a function that takes exec: `&dyn LocalSpawnHandle` 23 | // 24 | // ✔ pass an AsyncStd to a function that requires a YieldNow. 25 | // ✔ pass an AsyncStd to a function that requires a Timer. 26 | // ✔ Verify Timeout future. 27 | // 28 | mod common; 29 | 30 | use 31 | { 32 | common :: { * } , 33 | futures :: { channel::mpsc, StreamExt } , 34 | wasm_bindgen_test :: { * } , 35 | }; 36 | 37 | wasm_bindgen_test_configure!( run_in_browser ); 38 | 39 | 40 | 41 | // pass a AsyncStd to a function that takes exec: `impl Spawn` 42 | // 43 | #[ wasm_bindgen_test ] 44 | // 45 | fn spawn() 46 | { 47 | let (tx, mut rx) = mpsc::channel( 1 ); 48 | 49 | increment( 4, AsyncStd, tx ); 50 | 51 | let fut = async move 52 | { 53 | let result = rx.next().await.expect( "Some" ); 54 | 55 | assert_eq!( 5u8, result ); 56 | }; 57 | 58 | AsyncStd.spawn( fut ).expect( "spawn future" ); 59 | } 60 | 61 | 62 | // pass a &AsyncStd to a function that takes exec: `&impl Spawn` 63 | // 64 | #[ wasm_bindgen_test ] 65 | // 66 | fn spawn_ref() 67 | { 68 | let (tx, mut rx) = mpsc::channel( 1 ); 69 | 70 | increment_ref( 4, &AsyncStd, tx ); 71 | 72 | let fut = async move 73 | { 74 | let result = rx.next().await.expect( "Some" ); 75 | 76 | assert_eq!( 5u8, result ); 77 | }; 78 | 79 | AsyncStd.spawn( fut ).expect( "spawn future" ); 80 | } 81 | 82 | 83 | // pass a &AsyncStd to a function that takes exec: `impl Spawn` 84 | // 85 | #[ wasm_bindgen_test ] 86 | // 87 | fn spawn_with_ref() 88 | { 89 | let (tx, mut rx) = mpsc::channel( 1 ); 90 | 91 | #[allow(clippy::needless_borrow)] 92 | increment( 4, &AsyncStd, tx ); 93 | 94 | let fut = async move 95 | { 96 | let result = rx.next().await.expect( "Some" ); 97 | 98 | assert_eq!( 5u8, result ); 99 | }; 100 | 101 | AsyncStd.spawn( fut ).expect( "spawn future" ); 102 | } 103 | 104 | 105 | // pass a &AsyncStd to a function that takes exec: `impl Spawn + Clone` 106 | // 107 | #[ wasm_bindgen_test ] 108 | // 109 | fn spawn_clone_with_ref() 110 | { 111 | let (tx, mut rx) = mpsc::channel( 1 ); 112 | 113 | increment_clone( 4, &AsyncStd, tx ); 114 | 115 | let fut = async move 116 | { 117 | let result = rx.next().await.expect( "Some" ); 118 | 119 | assert_eq!( 5u8, result ); 120 | }; 121 | 122 | AsyncStd.spawn( fut ).expect( "spawn future" ); 123 | } 124 | 125 | 126 | // pass a Arc to a function that takes exec: `impl Spawn`. 127 | // Possible since futures 0.3.2. 128 | // 129 | #[ wasm_bindgen_test ] 130 | // 131 | fn spawn_clone_with_arc() 132 | { 133 | let (tx, mut rx) = mpsc::channel( 1 ); 134 | 135 | increment( 4, Arc::new(AsyncStd), tx ); 136 | 137 | let fut = async move 138 | { 139 | let result = rx.next().await.expect( "Some" ); 140 | 141 | assert_eq!( 5u8, result ); 142 | }; 143 | 144 | AsyncStd.spawn( fut ).expect( "spawn future" ); 145 | } 146 | 147 | 148 | // pass a AsyncStd to a function that takes exec: `impl SpawnHandle` 149 | // 150 | #[ wasm_bindgen_test ] 151 | // 152 | fn spawn_handle() 153 | { 154 | let fut = async move 155 | { 156 | let result = increment_spawn_handle( 4, AsyncStd ).await; 157 | 158 | assert_eq!( 5u8, result ); 159 | }; 160 | 161 | AsyncStd.spawn( fut ).expect( "spawn future" ); 162 | } 163 | 164 | 165 | // pass an Arc to a function that takes exec: `impl SpawnHandle` 166 | // 167 | #[ wasm_bindgen_test ] 168 | // 169 | fn spawn_handle_arc() 170 | { 171 | let fut = async move 172 | { 173 | let result = increment_spawn_handle( 4, Arc::new(AsyncStd) ).await; 174 | 175 | assert_eq!( 5u8, result ); 176 | }; 177 | 178 | AsyncStd.spawn( fut ).expect( "spawn future" ); 179 | } 180 | 181 | 182 | // pass a &AsyncStd to a function that takes exec: `&dyn SpawnHandle` 183 | // 184 | #[ wasm_bindgen_test ] 185 | // 186 | fn spawn_handle_os() 187 | { 188 | let fut = async move 189 | { 190 | let result = increment_spawn_handle_os( 4, &AsyncStd ).await; 191 | 192 | assert_eq!( 5u8, result ); 193 | }; 194 | 195 | AsyncStd.spawn_local( fut ).expect( "spawn future" ); 196 | } 197 | 198 | 199 | 200 | //----------------------Local 201 | 202 | 203 | 204 | // pass a AsyncStd to a function that takes exec: `impl Spawn` 205 | // 206 | #[ wasm_bindgen_test ] 207 | // 208 | fn spawn_local() 209 | { 210 | let (tx, mut rx) = mpsc::channel( 1 ); 211 | increment_local( 4, AsyncStd, tx ); 212 | 213 | let fut = async move 214 | { 215 | let result = rx.next().await.expect( "Some" ); 216 | 217 | assert_eq!( 5u8, result ); 218 | }; 219 | 220 | AsyncStd.spawn_local( fut ).expect( "spawn future" ); 221 | } 222 | 223 | 224 | // pass a &AsyncStd to a function that takes exec: `&impl Spawn` 225 | // 226 | #[ wasm_bindgen_test ] 227 | // 228 | fn spawn_ref_local() 229 | { 230 | let (tx, mut rx) = mpsc::channel( 1 ); 231 | increment_ref_local( 4, &AsyncStd, tx ); 232 | 233 | let fut = async move 234 | { 235 | let result = rx.next().await.expect( "Some" ); 236 | 237 | assert_eq!( 5u8, result ); 238 | }; 239 | 240 | AsyncStd.spawn_local( fut ).expect( "spawn future" ); 241 | } 242 | 243 | 244 | // pass a &AsyncStd to a function that takes exec: `impl Spawn` 245 | // 246 | #[ wasm_bindgen_test ] 247 | // 248 | fn spawn_with_ref_local() 249 | { 250 | let (tx, mut rx) = mpsc::channel( 1 ); 251 | 252 | #[allow(clippy::needless_borrow)] 253 | increment_local( 4, &AsyncStd, tx ); 254 | 255 | let fut = async move 256 | { 257 | let result = rx.next().await.expect( "Some" ); 258 | 259 | assert_eq!( 5u8, result ); 260 | }; 261 | 262 | AsyncStd.spawn_local( fut ).expect( "spawn future" ); 263 | } 264 | 265 | 266 | // pass a &AsyncStd to a function that takes exec: `impl Spawn + Clone` 267 | // 268 | #[ wasm_bindgen_test ] 269 | // 270 | fn spawn_clone_with_ref_local() 271 | { 272 | let (tx, mut rx) = mpsc::channel( 1 ); 273 | increment_clone_local( 4, &AsyncStd, tx ); 274 | 275 | let fut = async move 276 | { 277 | let result = rx.next().await.expect( "Some" ); 278 | 279 | assert_eq!( 5u8, result ); 280 | }; 281 | 282 | AsyncStd.spawn_local( fut ).expect( "spawn future" ); 283 | } 284 | 285 | 286 | // pass a Arc to a function that takes exec: `impl Spawn`. 287 | // Possible since futures 0.3.2. 288 | // 289 | #[ wasm_bindgen_test ] 290 | // 291 | fn spawn_clone_with_arc_local() 292 | { 293 | let (tx, mut rx) = mpsc::channel( 1 ); 294 | increment_local( 4, Arc::new(AsyncStd), tx ); 295 | 296 | let fut = async move 297 | { 298 | let result = rx.next().await.expect( "Some" ); 299 | 300 | assert_eq!( 5u8, result ); 301 | }; 302 | 303 | AsyncStd.spawn_local( fut ).expect( "spawn future" ); 304 | } 305 | 306 | 307 | // pass a AsyncStd to a function that takes exec: `impl SpawnHandle` 308 | // 309 | #[ wasm_bindgen_test ] 310 | // 311 | fn spawn_handle_local() 312 | { 313 | let fut = async move 314 | { 315 | let result = increment_spawn_handle_local( 4, AsyncStd ).await; 316 | 317 | assert_eq!( 5u8, *result ); 318 | }; 319 | 320 | AsyncStd.spawn_local( fut ).expect( "spawn future" ); 321 | } 322 | 323 | 324 | // pass an Arc to a function that takes exec: `impl SpawnHandle` 325 | // 326 | #[ wasm_bindgen_test ] 327 | // 328 | fn spawn_handle_arc_local() 329 | { 330 | let fut = async move 331 | { 332 | let result = increment_spawn_handle_local( 4, Arc::new(AsyncStd) ).await; 333 | 334 | assert_eq!( 5u8, *result ); 335 | }; 336 | 337 | AsyncStd.spawn_local( fut ).expect( "spawn future" ); 338 | } 339 | 340 | 341 | // pass a &AsyncStd to a function that takes exec: `&dyn SpawnHandle` 342 | // 343 | #[ wasm_bindgen_test ] 344 | // 345 | fn spawn_handle_os_local() 346 | { 347 | let fut = async move 348 | { 349 | let result = increment_spawn_handle_local_os( 4, &AsyncStd ).await; 350 | 351 | assert_eq!( 5u8, *result ); 352 | }; 353 | 354 | AsyncStd.spawn_local( fut ).expect( "spawn future" ); 355 | } 356 | 357 | 358 | 359 | // pass a AsyncStd to a function that requires a YieldNow. 360 | // 361 | #[ wasm_bindgen_test ] 362 | // 363 | fn yield_run_subtask_first() 364 | { 365 | let task = async{ try_yield_now( AsyncStd ).await.expect( "yield_now" ); }; 366 | 367 | AsyncStd.spawn_local( task ).expect( "spawn" ); 368 | } 369 | 370 | 371 | 372 | // pass a AsyncStd to a function that requires a YieldNow. 373 | // 374 | #[ wasm_bindgen_test ] 375 | // 376 | fn yield_run_subtask_last() 377 | { 378 | let task = async{ without_yield_now( AsyncStd ).await.expect( "yield_now" ); }; 379 | 380 | AsyncStd.spawn_local( task ).expect( "spawn" ); 381 | } 382 | 383 | 384 | // pass an AsyncStd to a function that requires a Timer. 385 | // 386 | #[ cfg( feature = "timer" ) ] 387 | // 388 | #[ wasm_bindgen_test ] 389 | // 390 | fn timer_should_wake_local() 391 | { 392 | AsyncStd.spawn_local( timer_should_wake_up_local( AsyncStd ) ).expect( "spawn future" ); 393 | } 394 | 395 | 396 | 397 | // Verify timeout future. 398 | // 399 | #[ cfg( feature = "timer" ) ] 400 | // 401 | #[ wasm_bindgen_test ] 402 | // 403 | fn run_timeout() 404 | { 405 | AsyncStd.spawn_local( timeout( AsyncStd ) ).expect( "spawn" ); 406 | } 407 | 408 | 409 | 410 | // Verify timeout future. 411 | // 412 | #[ cfg( feature = "timer" ) ] 413 | // 414 | #[ wasm_bindgen_test ] 415 | // 416 | fn run_dont_timeout() 417 | { 418 | AsyncStd.spawn_local( dont_timeout( AsyncStd ) ).expect( "spawn" ); 419 | } 420 | 421 | 422 | 423 | -------------------------------------------------------------------------------- /tests/bindgen.rs: -------------------------------------------------------------------------------- 1 | #![ cfg(all( feature = "bindgen", target_arch = "wasm32" )) ] 2 | 3 | // Tested: 4 | // 5 | // ✔ pass a Bindgen to a function that takes exec: `impl Spawn` 6 | // ✔ pass a &Bindgen to a function that takes exec: `&impl Spawn` 7 | // ✔ pass a &Bindgen to a function that takes exec: `impl Spawn` 8 | // ✔ pass a &Bindgen to a function that takes exec: `impl Spawn + Clone` 9 | // ✔ pass a Arc to a function that takes exec: `impl Spawn` 10 | // ✔ pass a Bindgen to a function that takes exec: `impl SpawnHandle` 11 | // ✔ pass a Arc to a function that takes exec: `impl SpawnHandle` 12 | // ✔ pass a &Bindgen to a function that takes exec: `&dyn SpawnHandle` 13 | // 14 | // ✔ pass a Bindgen to a function that takes exec: `impl LocalSpawn` 15 | // ✔ pass a &Bindgen to a function that takes exec: `&impl LocalSpawn` 16 | // ✔ pass a &Bindgen to a function that takes exec: `impl LocalSpawn` 17 | // ✔ pass a &Bindgen to a function that takes exec: `impl LocalSpawn + Clone` 18 | // ✔ pass a Rc to a function that takes exec: `impl LocalSpawn` 19 | // ✔ pass a Bindgen to a function that takes exec: `impl LocalSpawnHandle` 20 | // ✔ pass a Rc to a function that takes exec: `impl LocalSpawnHandle` 21 | // ✔ pass a &Bindgen to a function that takes exec: `&dyn LocalSpawnHandle` 22 | // 23 | // ✔ pass a Bindgen to a function that requires a YieldNow. 24 | // ✔ pass a Bindgen to a function that requires a Timer. 25 | // ✔ Verify Bindgen does not implement Timer when feature is not enabled. 26 | // ✔ Verify Timeout future. 27 | // 28 | mod common; 29 | 30 | use 31 | { 32 | common :: { * } , 33 | futures :: { channel::mpsc, StreamExt } , 34 | wasm_bindgen_test :: { * } , 35 | }; 36 | 37 | wasm_bindgen_test_configure!( run_in_browser ); 38 | 39 | 40 | // pass a Bindgen to a function that takes exec: `impl Spawn` 41 | // 42 | #[ wasm_bindgen_test ] 43 | // 44 | fn spawn() 45 | { 46 | let (tx, mut rx) = mpsc::channel( 1 ); 47 | let exec = Bindgen; 48 | 49 | increment( 4, exec, tx ); 50 | 51 | let fut = async move 52 | { 53 | let result = rx.next().await.expect( "Some" ); 54 | 55 | assert_eq!( 5u8, result ); 56 | }; 57 | 58 | exec.spawn( fut ).expect( "spawn future" ); 59 | } 60 | 61 | 62 | // pass a &Bindgen to a function that takes exec: `&impl Spawn` 63 | // 64 | #[ wasm_bindgen_test ] 65 | // 66 | fn spawn_ref() 67 | { 68 | let (tx, mut rx) = mpsc::channel( 1 ); 69 | let exec = Bindgen; 70 | 71 | increment_ref( 4, &exec, tx ); 72 | 73 | let fut = async move 74 | { 75 | let result = rx.next().await.expect( "Some" ); 76 | 77 | assert_eq!( 5u8, result ); 78 | }; 79 | 80 | exec.spawn( fut ).expect( "spawn future" ); 81 | } 82 | 83 | 84 | // pass a &Bindgen to a function that takes exec: `impl Spawn` 85 | // 86 | #[ wasm_bindgen_test ] 87 | // 88 | fn spawn_with_ref() 89 | { 90 | let (tx, mut rx) = mpsc::channel( 1 ); 91 | let exec = Bindgen; 92 | 93 | #[allow(clippy::needless_borrow)] 94 | increment( 4, &exec, tx ); 95 | 96 | let fut = async move 97 | { 98 | let result = rx.next().await.expect( "Some" ); 99 | 100 | assert_eq!( 5u8, result ); 101 | }; 102 | 103 | exec.spawn( fut ).expect( "spawn future" ); 104 | } 105 | 106 | 107 | // pass a &Bindgen to a function that takes exec: `impl Spawn + Clone` 108 | // 109 | #[ wasm_bindgen_test ] 110 | // 111 | fn spawn_clone_with_ref() 112 | { 113 | let (tx, mut rx) = mpsc::channel( 1 ); 114 | let exec = Bindgen; 115 | 116 | increment_clone( 4, &exec, tx ); 117 | 118 | let fut = async move 119 | { 120 | let result = rx.next().await.expect( "Some" ); 121 | 122 | assert_eq!( 5u8, result ); 123 | }; 124 | 125 | exec.spawn( fut ).expect( "spawn future" ); 126 | } 127 | 128 | 129 | // pass a Arc to a function that takes exec: `impl Spawn`. 130 | // Possible since futures 0.3.2. 131 | // 132 | #[ wasm_bindgen_test ] 133 | // 134 | fn spawn_clone_with_arc() 135 | { 136 | let (tx, mut rx) = mpsc::channel( 1 ); 137 | let exec = Bindgen; 138 | 139 | increment( 4, Arc::new(exec), tx ); 140 | 141 | let fut = async move 142 | { 143 | let result = rx.next().await.expect( "Some" ); 144 | 145 | assert_eq!( 5u8, result ); 146 | }; 147 | 148 | exec.spawn( fut ).expect( "spawn future" ); 149 | } 150 | 151 | 152 | // pass a Bindgen to a function that takes exec: `impl SpawnHandle` 153 | // 154 | #[ wasm_bindgen_test ] 155 | // 156 | fn spawn_handle() 157 | { 158 | let exec = Bindgen; 159 | 160 | let fut = async move 161 | { 162 | let result = increment_spawn_handle( 4, exec ).await; 163 | 164 | assert_eq!( 5u8, result ); 165 | }; 166 | 167 | exec.spawn( fut ).expect( "spawn future" ); 168 | } 169 | 170 | 171 | // pass an Arc to a function that takes exec: `impl SpawnHandle` 172 | // 173 | #[ wasm_bindgen_test ] 174 | // 175 | fn spawn_handle_arc() 176 | { 177 | let exec = Bindgen; 178 | 179 | let fut = async move 180 | { 181 | let result = increment_spawn_handle( 4, Arc::new(exec) ).await; 182 | 183 | assert_eq!( 5u8, result ); 184 | }; 185 | 186 | exec.spawn( fut ).expect( "spawn future" ); 187 | } 188 | 189 | 190 | // pass a &Bindgen to a function that takes exec: `&dyn SpawnHandle` 191 | // 192 | #[ wasm_bindgen_test ] 193 | // 194 | fn spawn_handle_os() 195 | { 196 | let exec = Bindgen; 197 | 198 | let fut = async move 199 | { 200 | let result = increment_spawn_handle_os( 4, &exec ).await; 201 | 202 | assert_eq!( 5u8, result ); 203 | }; 204 | 205 | exec.spawn_local( fut ).expect( "spawn future" ); 206 | } 207 | 208 | 209 | //----------------------Local 210 | 211 | 212 | 213 | // pass a Bindgen to a function that takes exec: `impl Spawn` 214 | // 215 | #[ wasm_bindgen_test ] 216 | // 217 | fn spawn_local() 218 | { 219 | let (tx, mut rx) = mpsc::channel( 1 ); 220 | let exec = Bindgen; 221 | 222 | increment_local( 4, exec, tx ); 223 | 224 | let fut = async move 225 | { 226 | let result = rx.next().await.expect( "Some" ); 227 | 228 | assert_eq!( 5u8, result ); 229 | }; 230 | 231 | exec.spawn_local( fut ).expect( "spawn future" ); 232 | } 233 | 234 | 235 | // pass a &Bindgen to a function that takes exec: `&impl Spawn` 236 | // 237 | #[ wasm_bindgen_test ] 238 | // 239 | fn spawn_ref_local() 240 | { 241 | let (tx, mut rx) = mpsc::channel( 1 ); 242 | let exec = Bindgen; 243 | 244 | increment_ref_local( 4, &exec, tx ); 245 | 246 | let fut = async move 247 | { 248 | let result = rx.next().await.expect( "Some" ); 249 | 250 | assert_eq!( 5u8, result ); 251 | }; 252 | 253 | exec.spawn_local( fut ).expect( "spawn future" ); 254 | } 255 | 256 | 257 | // pass a &Bindgen to a function that takes exec: `impl Spawn` 258 | // 259 | #[ wasm_bindgen_test ] 260 | // 261 | fn spawn_with_ref_local() 262 | { 263 | let (tx, mut rx) = mpsc::channel( 1 ); 264 | let exec = Bindgen; 265 | 266 | #[allow(clippy::needless_borrow)] 267 | increment_local( 4, &exec, tx ); 268 | 269 | let fut = async move 270 | { 271 | let result = rx.next().await.expect( "Some" ); 272 | 273 | assert_eq!( 5u8, result ); 274 | }; 275 | 276 | exec.spawn_local( fut ).expect( "spawn future" ); 277 | } 278 | 279 | 280 | // pass a &Bindgen to a function that takes exec: `impl Spawn + Clone` 281 | // 282 | #[ wasm_bindgen_test ] 283 | // 284 | fn spawn_clone_with_ref_local() 285 | { 286 | let (tx, mut rx) = mpsc::channel( 1 ); 287 | let exec = Bindgen; 288 | 289 | increment_clone_local( 4, &exec, tx ); 290 | 291 | let fut = async move 292 | { 293 | let result = rx.next().await.expect( "Some" ); 294 | 295 | assert_eq!( 5u8, result ); 296 | }; 297 | 298 | exec.spawn_local( fut ).expect( "spawn future" ); 299 | } 300 | 301 | 302 | // pass a Arc to a function that takes exec: `impl Spawn`. 303 | // Possible since futures 0.3.2. 304 | // 305 | #[ wasm_bindgen_test ] 306 | // 307 | fn spawn_clone_with_arc_local() 308 | { 309 | let (tx, mut rx) = mpsc::channel( 1 ); 310 | let exec = Bindgen; 311 | 312 | increment_local( 4, Arc::new(exec), tx ); 313 | 314 | let fut = async move 315 | { 316 | let result = rx.next().await.expect( "Some" ); 317 | 318 | assert_eq!( 5u8, result ); 319 | }; 320 | 321 | exec.spawn_local( fut ).expect( "spawn future" ); 322 | } 323 | 324 | 325 | // pass a Bindgen to a function that takes exec: `impl SpawnHandle` 326 | // 327 | #[ wasm_bindgen_test ] 328 | // 329 | fn spawn_handle_local() 330 | { 331 | let exec = Bindgen; 332 | 333 | let fut = async move 334 | { 335 | let result = increment_spawn_handle_local( 4, exec ).await; 336 | 337 | assert_eq!( 5u8, *result ); 338 | }; 339 | 340 | exec.spawn_local( fut ).expect( "spawn future" ); 341 | } 342 | 343 | 344 | // pass an Arc to a function that takes exec: `impl SpawnHandle` 345 | // 346 | #[ wasm_bindgen_test ] 347 | // 348 | fn spawn_handle_arc_local() 349 | { 350 | let exec = Bindgen; 351 | 352 | 353 | let fut = async move 354 | { 355 | let result = increment_spawn_handle_local( 4, Arc::new(exec) ).await; 356 | 357 | assert_eq!( 5u8, *result ); 358 | }; 359 | 360 | exec.spawn_local( fut ).expect( "spawn future" ); 361 | } 362 | 363 | 364 | // pass a &Bindgen to a function that takes exec: `&dyn SpawnHandle` 365 | // 366 | #[ wasm_bindgen_test ] 367 | // 368 | fn spawn_handle_os_local() 369 | { 370 | let exec = Bindgen; 371 | 372 | let fut = async move 373 | { 374 | let result = increment_spawn_handle_local_os( 4, &exec ).await; 375 | 376 | assert_eq!( 5u8, *result ); 377 | }; 378 | 379 | exec.spawn_local( fut ).expect( "spawn future" ); 380 | } 381 | 382 | 383 | 384 | // pass a Bindgen to a function that requires a YieldNow. 385 | // 386 | #[ wasm_bindgen_test ] 387 | // 388 | fn yield_run_subtask_first() 389 | { 390 | let task = async{ try_yield_now( Bindgen ).await.expect( "yield_now" ); }; 391 | 392 | Bindgen.spawn_local( task ).expect( "spawn" ); 393 | } 394 | 395 | 396 | 397 | // pass a Bindgen to a function that requires a YieldNow. 398 | // 399 | #[ wasm_bindgen_test ] 400 | // 401 | fn yield_run_subtask_last() 402 | { 403 | let task = async{ without_yield_now( Bindgen ).await.expect( "yield_now" ); }; 404 | 405 | Bindgen.spawn_local( task ).expect( "spawn" ); 406 | } 407 | 408 | 409 | 410 | 411 | // pass an Bindgen to a function that requires a Timer. 412 | // 413 | #[ cfg( feature = "timer" ) ] 414 | // 415 | #[ wasm_bindgen_test ] 416 | // 417 | fn timer_should_wake_local() 418 | { 419 | Bindgen.spawn_local( timer_should_wake_up_local( Bindgen ) ).expect( "spawn" ); 420 | } 421 | 422 | 423 | 424 | // Verify timeout future. 425 | // 426 | #[ cfg( feature = "timer" ) ] 427 | // 428 | #[ wasm_bindgen_test ] 429 | // 430 | fn run_timeout() 431 | { 432 | Bindgen.spawn_local( timeout( Bindgen ) ).expect( "spawn" ); 433 | } 434 | 435 | 436 | 437 | // Verify timeout future. 438 | // 439 | #[ cfg( feature = "timer" ) ] 440 | // 441 | #[ wasm_bindgen_test ] 442 | // 443 | fn run_dont_timeout() 444 | { 445 | Bindgen.spawn_local( dont_timeout( Bindgen ) ).expect( "spawn" ); 446 | } 447 | 448 | 449 | 450 | // Verify Bindgen does not implement Timer when feature is not enabled. 451 | // 452 | #[ cfg(not( feature = "timer" )) ] 453 | // 454 | #[ test ] 455 | // 456 | fn no_feature_no_timer() 457 | { 458 | static_assertions::assert_not_impl_any!( Bindgen: Timer ); 459 | } 460 | -------------------------------------------------------------------------------- /tests/common.rs: -------------------------------------------------------------------------------- 1 | #![ allow(dead_code) ] 2 | // 3 | pub use 4 | { 5 | futures :: { FutureExt, SinkExt, channel::{ mpsc::Sender, oneshot }, executor::block_on } , 6 | futures::task :: { LocalSpawnExt, SpawnExt, LocalSpawn, Spawn } , 7 | std :: { convert::TryFrom } , 8 | std :: { sync::{ Arc, atomic::{ AtomicBool, Ordering::SeqCst } }, rc::Rc, time::Duration } , 9 | async_executors :: { * } , 10 | }; 11 | 12 | 13 | pub type DynResult = Result< T, Box >; 14 | pub type DynResultNoSend = Result< T, Box >; 15 | 16 | 17 | #[ cfg(not( target_arch = "wasm32" )) ] 18 | // 19 | pub mod tokio_io 20 | { 21 | use 22 | { 23 | tokio::net :: { TcpListener, TcpStream } , 24 | tokio::io :: { AsyncReadExt, AsyncWriteExt } , 25 | 26 | super::*, 27 | }; 28 | 29 | /// Creates a connected pair of sockets. 30 | /// Uses tokio tcp stream. This will only work if the reactor is running. 31 | // 32 | pub async fn socket_pair() -> DynResult< (TcpStream, TcpStream) > 33 | { 34 | // port 0 = let the OS choose 35 | // 36 | let listener = TcpListener::bind("127.0.0.1:0").await?; 37 | let stream1 = TcpStream::connect(listener.local_addr()?).await?; 38 | let stream2 = listener.accept().await?.0; 39 | 40 | Ok( (stream1, stream2) ) 41 | } 42 | 43 | 44 | pub async fn tcp( exec: impl SpawnHandle< DynResult<()> > + TokioIo ) -> DynResult<()> 45 | { 46 | let test = async 47 | { 48 | let (mut one, mut two) = socket_pair().await?; 49 | 50 | one.write_u8( 5 ).await?; 51 | 52 | assert_eq!( 5, two.read_u8().await? ); 53 | 54 | Ok(()) 55 | }; 56 | 57 | exec.spawn_handle( test )?.await 58 | } 59 | } 60 | 61 | 62 | 63 | async fn sum( a: u8, b: u8, mut tx: Sender ) 64 | { 65 | let res = tx.send( a + b ).await; 66 | 67 | assert!( res.is_ok() ); 68 | } 69 | 70 | 71 | async fn sum_local( a: u8, b: u8, mut tx: Sender ) 72 | { 73 | // make sure we have something !Send in here. 74 | // 75 | let a = Rc::new(a); 76 | 77 | let res = tx.send( *a + b ).await; 78 | 79 | assert!( res.is_ok() ); 80 | } 81 | 82 | 83 | async fn sum_handle( a: u8, b: u8 ) -> u8 84 | { 85 | a + b 86 | } 87 | 88 | 89 | async fn sum_handle_local( a: u8, b: u8 ) -> Rc 90 | { 91 | // Return something !Send 92 | // 93 | Rc::new( a + b ) 94 | } 95 | 96 | 97 | // A function that takes a generic executor and spawns a task. 98 | // 99 | pub fn increment( a: u8, exec: impl Spawn, tx: Sender ) 100 | { 101 | let res = exec.spawn( sum( a, 1, tx ) ); 102 | 103 | assert!( res.is_ok() ); 104 | } 105 | 106 | 107 | // A function that takes a generic executor and spawns a task. 108 | // 109 | #[ allow(dead_code) ] // gives warning when testing all executors at once. 110 | // 111 | pub fn increment_local( a: u8, exec: impl LocalSpawn, tx: Sender ) 112 | { 113 | let res = exec.spawn_local( sum( a, 1, tx ) ); 114 | 115 | assert!( res.is_ok() ); 116 | } 117 | 118 | 119 | // A function that takes a generic executor and spawns a task. 120 | // 121 | pub fn increment_ref( a: u8, exec: &impl Spawn, tx: Sender ) 122 | { 123 | let res = exec.spawn( sum( a, 1, tx ) ); 124 | 125 | assert!( res.is_ok() ); 126 | } 127 | 128 | 129 | // A function that takes a generic executor and spawns a task. 130 | // 131 | #[ allow(dead_code) ] // gives warning when testing all executors at once. 132 | // 133 | pub fn increment_ref_local( a: u8, exec: &impl LocalSpawn, tx: Sender ) 134 | { 135 | let res = exec.spawn_local( sum( a, 1, tx ) ); 136 | 137 | assert!( res.is_ok() ); 138 | } 139 | 140 | 141 | // A function that takes a generic executor by value, clones it and spawns a task. 142 | // 143 | pub fn increment_clone( a: u8, exec: impl Spawn + Clone, tx: Sender ) 144 | { 145 | let second = exec.clone(); 146 | drop( exec ); 147 | 148 | let res = second.spawn( sum( a, 1, tx ) ); 149 | 150 | assert!( res.is_ok() ); 151 | } 152 | 153 | 154 | // A function that takes a generic executor by value, clones it and spawns a task. 155 | // 156 | #[ allow(dead_code) ] // gives warning when testing all executors at once. 157 | // 158 | pub fn increment_clone_local( a: u8, exec: impl LocalSpawn + Clone, tx: Sender ) 159 | { 160 | let second = exec.clone(); 161 | drop( exec ); 162 | 163 | let res = second.spawn_local( sum( a, 1, tx ) ); 164 | 165 | assert!( res.is_ok() ); 166 | } 167 | 168 | 169 | // A function that takes a generic executor and spawns a task. 170 | // 171 | #[ allow(dead_code) ] 172 | // 173 | pub async fn increment_spawn_handle( a: u8, exec: impl SpawnHandle ) -> u8 174 | { 175 | exec.spawn_handle( sum_handle( a, 1 ) ).expect( "spawn handle" ).await 176 | } 177 | 178 | 179 | // A function that takes a trait object and spawns a task. 180 | // 181 | #[ allow(dead_code) ] 182 | // 183 | pub async fn increment_spawn_handle_os( a: u8, exec: &dyn SpawnHandle ) -> u8 184 | { 185 | exec.spawn_handle( sum_handle( a, 1 ).boxed() ).expect( "spawn handle" ).await 186 | } 187 | 188 | 189 | 190 | // A function that takes a generic executor and spawns a task. 191 | // 192 | #[ allow(dead_code) ] // gives warning when testing all executors at once. 193 | // 194 | pub async fn increment_spawn_handle_local( a: u8, exec: impl LocalSpawnHandle> ) -> Rc 195 | { 196 | exec.spawn_handle_local( sum_handle_local( a, 1 ) ).expect( "spawn handle" ).await 197 | } 198 | 199 | // A function that takes a trait object and spawns a task. 200 | // 201 | #[ allow(dead_code) ] // gives warning when testing all executors at once. 202 | // 203 | pub async fn increment_spawn_handle_local_os( a: u8, exec: &dyn LocalSpawnHandle> )-> Rc 204 | { 205 | exec.spawn_handle_local( sum_handle_local( a, 1 ).boxed() ).expect( "spawn handle" ).await 206 | } 207 | 208 | 209 | 210 | 211 | // Verify timers work by making sure a shorter timer ends before the longer one. 212 | // On top of that this shouldn't hang. 213 | // 214 | #[ cfg(not( target_arch = "wasm32" )) ] 215 | // 216 | pub async fn timer_should_wake_up( exec: impl SpawnHandle<()> + Clone + Timer + Send + Sync + 'static ) 217 | { 218 | let ex2 = exec.clone(); 219 | let handle = exec.spawn_handle( async move 220 | { 221 | let fast = ex2.sleep( Duration::from_millis( 1) ).fuse(); 222 | let slow = ex2.sleep( Duration::from_millis(80) ).fuse(); 223 | 224 | futures_util::pin_mut!( fast ); 225 | futures_util::pin_mut!( slow ); 226 | 227 | let res = futures_util::select! 228 | { 229 | _ = slow => false, 230 | _ = fast => true, 231 | }; 232 | 233 | assert!( res ); 234 | 235 | }).expect( "spawn_handle" ); 236 | 237 | handle.await; 238 | } 239 | 240 | 241 | 242 | // Verify timers work by making sure a shorter timer ends before the longer one. 243 | // On top of that this shouldn't hang. 244 | // 245 | // This one is needed for executors that aren't Send, because we pass the executor into 246 | // the spawned task and ThreadPools don't implement spawning !Send futures. 247 | // 248 | pub async fn timer_should_wake_up_local( exec: impl LocalSpawnHandle<()> + Clone + Timer + 'static ) 249 | { 250 | let ex2 = exec.clone(); 251 | let handle = exec.spawn_handle_local( async move 252 | { 253 | let fast = ex2.sleep( Duration::from_millis( 1) ).fuse(); 254 | let slow = ex2.sleep( Duration::from_millis(80) ).fuse(); 255 | 256 | futures_util::pin_mut!( fast ); 257 | futures_util::pin_mut!( slow ); 258 | 259 | let res = futures_util::select! 260 | { 261 | _ = slow => false, 262 | _ = fast => true, 263 | }; 264 | 265 | assert!( res ); 266 | 267 | }).expect( "spawn_handle" ); 268 | 269 | handle.await; 270 | } 271 | 272 | 273 | 274 | 275 | // Use timeout. 276 | // 277 | pub async fn timeout( exec: impl Timer ) 278 | { 279 | let fut = exec.sleep ( Duration::from_millis(80) ); 280 | let fut = exec.timeout( Duration::from_millis(20), fut ); 281 | 282 | assert!( fut.await.is_err() ); 283 | } 284 | 285 | 286 | // Use timeout. 287 | // 288 | pub async fn dont_timeout( exec: impl Timer ) 289 | { 290 | let fut = exec.sleep ( Duration::from_millis(20) ); 291 | let fut = exec.timeout( Duration::from_millis(80), fut ); 292 | 293 | assert!( fut.await.is_ok() ); 294 | } 295 | 296 | 297 | 298 | // Use same exec to run this function as you pass in. 299 | // 300 | pub async fn try_yield_now( exec: impl SpawnHandle<()> + YieldNow ) -> DynResult<()> 301 | { 302 | let flag = Arc::new( AtomicBool::new( false ) ); 303 | let flag2 = flag.clone(); 304 | 305 | let task = async move 306 | { 307 | flag2.store( true, SeqCst ); 308 | }; 309 | 310 | let handle = exec.spawn_handle( task )?; 311 | 312 | exec.yield_now().await; 313 | 314 | // by now task should have run because of the yield_now. 315 | // 316 | assert!( flag.load(SeqCst) ); 317 | 318 | handle.await; 319 | 320 | Ok(()) 321 | } 322 | 323 | 324 | 325 | // Use same exec to run this function as you pass in. 326 | // 327 | pub async fn without_yield_now( exec: impl SpawnHandle<()> + YieldNow ) -> DynResult<()> 328 | { 329 | let flag = Arc::new( AtomicBool::new( false ) ); 330 | let flag2 = flag.clone(); 331 | 332 | let task = async move 333 | { 334 | flag2.store( true, SeqCst ); 335 | }; 336 | 337 | let handle = exec.spawn_handle( task )?; 338 | 339 | // spawned task should not have run yet. 340 | // 341 | assert!( !flag.load(SeqCst) ); 342 | 343 | handle.await; 344 | 345 | Ok(()) 346 | } 347 | 348 | 349 | 350 | // Use same exec to run this function as you pass in. 351 | // 352 | pub async fn blocking( exec: impl SpawnBlocking<()> ) -> DynResult<()> 353 | { 354 | let flag = Arc::new( AtomicBool::new( false ) ); 355 | let flag2 = flag.clone(); 356 | 357 | let task = move || 358 | { 359 | // blocking work 360 | // 361 | std::thread::sleep( Duration::from_millis( 5 ) ); 362 | 363 | flag2.store( true, SeqCst ); 364 | }; 365 | 366 | let handle = exec.spawn_blocking( task ); 367 | 368 | handle.await; 369 | 370 | assert!( flag.load(SeqCst) ); 371 | 372 | Ok(()) 373 | } 374 | 375 | 376 | 377 | // Use same exec to run this function as you pass in. This tests for 378 | // the possibility of an object safe SpawnBlocking. 379 | // 380 | pub async fn blocking_void( exec: &dyn SpawnBlocking<()> ) -> DynResult<()> 381 | { 382 | let flag = Arc::new( AtomicBool::new( false ) ); 383 | let flag2 = flag.clone(); 384 | 385 | let task = move || 386 | { 387 | // blocking work 388 | // 389 | std::thread::sleep( Duration::from_millis( 5 ) ); 390 | 391 | flag2.store( true, SeqCst ); 392 | }; 393 | 394 | let handle = exec.spawn_blocking_dyn( Box::new(task) ); 395 | 396 | handle.await; 397 | 398 | assert!( flag.load(SeqCst) ); 399 | 400 | Ok(()) 401 | } 402 | -------------------------------------------------------------------------------- /tests/localpool.rs: -------------------------------------------------------------------------------- 1 | #![ cfg( feature = "localpool" ) ] 2 | // 3 | // Tested: 4 | // 5 | // ✔ pass a LocalSpawner to a function that takes exec: `impl SpawnHandle` 6 | // ✔ pass a Rc to a function that takes exec: `impl SpawnHandle` 7 | // ✔ pass a &LocalSpawner to a function that takes exec: `&dyn SpawnHandle` 8 | // 9 | // ✔ pass a LocalSpawner to a function that takes exec: `impl LocalSpawnHandle` 10 | // ✔ pass a Rc to a function that takes exec: `impl LocalSpawnHandle` 11 | // ✔ pass a &LocalSpawner to a function that takes exec: `&dyn LocalSpawnHandle` 12 | // 13 | // ✔ pass an LocalPool to a function that requires a Timer. 14 | // ✔ Verify LocalPool does not implement Timer when feature is not enabled. 15 | // ✔ Verify LocalSpawner does not implement Timer when feature is not enabled. 16 | // ✔ Verify Timeout future. 17 | // 18 | mod common; 19 | 20 | use 21 | { 22 | common :: { * } , 23 | futures_executor :: { LocalPool } , 24 | std :: { rc::Rc } , 25 | }; 26 | 27 | 28 | // pass a LocalSpawner to a function that takes exec: `impl SpawnHandle` 29 | // 30 | #[ test ] 31 | // 32 | fn spawn_handle() 33 | { 34 | let mut exec = LocalPool::new(); 35 | let spawner = exec.spawner(); 36 | 37 | let res = exec.run_until( increment_spawn_handle( 4, spawner ) ); 38 | 39 | assert_eq!( 5u8, res ); 40 | } 41 | 42 | 43 | // pass an Rc to a function that takes exec: `impl SpawnHandle` 44 | // 45 | #[ test ] 46 | // 47 | fn spawn_handle_rc() 48 | { 49 | let mut exec = LocalPool::new(); 50 | let spawner = exec.spawner(); 51 | 52 | let res = exec.run_until( increment_spawn_handle( 4, Rc::new(spawner) ) ); 53 | 54 | assert_eq!( 5u8, res ); 55 | } 56 | 57 | 58 | 59 | // pass a &LocalSpawner to a function that takes exec: `&dyn SpawnHandle` 60 | // 61 | #[ test ] 62 | // 63 | fn spawn_handle_os() 64 | { 65 | let mut wrap = LocalPool::new(); 66 | let exec = wrap.spawner(); 67 | 68 | let result = wrap.run_until( increment_spawn_handle_os( 4, &exec ) ); 69 | 70 | assert_eq!( 5u8, result ); 71 | } 72 | 73 | 74 | // ------------------ Local 75 | // 76 | 77 | 78 | // pass a LocalSpawner to a function that takes exec: `impl LocalSpawnHandle` 79 | // 80 | #[ test ] 81 | // 82 | fn spawn_handle_local() 83 | { 84 | let mut exec = LocalPool::new(); 85 | let spawner = exec.spawner(); 86 | 87 | let res = exec.run_until( increment_spawn_handle_local( 4, spawner ) ); 88 | 89 | assert_eq!( 5u8, *res ); 90 | } 91 | 92 | 93 | // pass an Rc to a function that takes exec: `impl LocalSpawnHandle` 94 | // 95 | #[ test ] 96 | // 97 | fn spawn_handle_rc_local() 98 | { 99 | let mut exec = LocalPool::new(); 100 | let spawner = exec.spawner(); 101 | 102 | let res = exec.run_until( increment_spawn_handle_local( 4, Rc::new(spawner) ) ); 103 | 104 | assert_eq!( 5u8, *res ); 105 | } 106 | 107 | 108 | 109 | // pass a &LocalSpawner to a function that takes exec: `&dyn LocalSpawnHandle` 110 | // 111 | #[ test ] 112 | // 113 | fn spawn_handle_local_os() 114 | { 115 | let mut wrap = LocalPool::new(); 116 | let exec = wrap.spawner(); 117 | 118 | let result = wrap.run_until( increment_spawn_handle_os( 4, &exec ) ); 119 | 120 | assert_eq!( 5u8, result ); 121 | } 122 | 123 | 124 | 125 | // pass a LocalSpawner to a function that requires a YieldNow. 126 | // 127 | #[ test ] 128 | // 129 | fn yield_run_subtask_first() -> DynResult<()> 130 | { 131 | let mut wrap = LocalPool::new(); 132 | let exec = wrap.spawner(); 133 | 134 | wrap.run_until( try_yield_now( exec ) ) 135 | } 136 | 137 | 138 | 139 | // pass a LocalSpawner to a function that requires a YieldNow. 140 | // 141 | #[ test ] 142 | // 143 | fn yield_run_subtask_last() -> DynResult<()> 144 | { 145 | let mut wrap = LocalPool::new(); 146 | let exec = wrap.spawner(); 147 | 148 | wrap.run_until( without_yield_now( exec ) ) 149 | } 150 | 151 | 152 | 153 | // pass an LocalPool to a function that requires a Timer. 154 | // 155 | #[ cfg( feature = "timer" ) ] 156 | // 157 | #[ test ] 158 | // 159 | fn timer_should_wake() 160 | { 161 | let mut wrap = LocalPool::new(); 162 | let exec = wrap.spawner(); 163 | 164 | wrap.run_until( timer_should_wake_up_local( exec ) ); 165 | } 166 | 167 | 168 | 169 | // pass an LocalSpawner to a function that requires a Timer. 170 | // 171 | #[ cfg( feature = "timer" ) ] 172 | // 173 | #[ test ] 174 | // 175 | fn run_timeout() 176 | { 177 | let mut wrap = LocalPool::new(); 178 | let exec = wrap.spawner(); 179 | 180 | wrap.run_until( timeout( exec ) ); 181 | } 182 | 183 | 184 | 185 | // pass an LocalSpawner to a function that requires a Timer. 186 | // 187 | #[ cfg( feature = "timer" ) ] 188 | // 189 | #[ test ] 190 | // 191 | fn run_dont_timeout() 192 | { 193 | let mut wrap = LocalPool::new(); 194 | let exec = wrap.spawner(); 195 | 196 | wrap.run_until( dont_timeout( exec ) ); 197 | } 198 | 199 | 200 | 201 | // Verify LocalPool does not implement Timer when feature is not enabled. 202 | // 203 | #[ cfg(not( feature = "timer" )) ] 204 | // 205 | #[ test ] 206 | // 207 | fn no_feature_no_timer() 208 | { 209 | static_assertions::assert_not_impl_any!( LocalPool : Timer ); 210 | static_assertions::assert_not_impl_any!( LocalSpawner: Timer ); 211 | } 212 | 213 | -------------------------------------------------------------------------------- /tests/threadpool.rs: -------------------------------------------------------------------------------- 1 | #![ cfg( feature = "threadpool" ) ] 2 | // 3 | // ✔ pass a ThreadPool to a function that takes exec: `impl SpawnHandle` 4 | // ✔ pass a Arc to a function that takes exec: `impl SpawnHandle` 5 | // ✔ pass a &ThreadPool to a function that takes exec: `&dyn SpawnHandle` 6 | // 7 | // ✔ Joinhandle::detach allows task to keep running. 8 | // 9 | // ✔ pass an ThreadPool to a function that requires a Timer. 10 | // ✔ Verify ThreadPool does not implement Timer when feature is not enabled. 11 | // ✔ Verify Timeout future. 12 | // 13 | mod common; 14 | 15 | use 16 | { 17 | common :: { * } , 18 | futures_executor :: { ThreadPool } , 19 | }; 20 | 21 | // pass a ThreadPool to a function that takes exec: `impl SpawnHandle` 22 | // 23 | #[ test ] 24 | // 25 | fn spawn_handle() 26 | { 27 | let exec = ThreadPool::new().expect( "create threadpool" ); 28 | let result = block_on( increment_spawn_handle( 4, exec ) ); 29 | 30 | assert_eq!( 5u8, result ); 31 | } 32 | 33 | 34 | // pass an Arc to a function that takes exec: `impl SpawnHandle` 35 | // 36 | #[ test ] 37 | // 38 | fn spawn_handle_arc() 39 | { 40 | let exec = ThreadPool::new().expect( "create threadpool" ); 41 | let result = block_on( increment_spawn_handle( 4, Arc::new(exec) ) ); 42 | 43 | assert_eq!( 5u8, result ); 44 | } 45 | 46 | 47 | // pass a ThreadPool to a function that takes exec: `&dyn SpawnHandle` 48 | // 49 | #[ test ] 50 | // 51 | fn spawn_handle_os() 52 | { 53 | let exec = ThreadPool::new().expect( "create threadpool" ); 54 | let result = block_on( increment_spawn_handle_os( 4, &exec ) ); 55 | 56 | assert_eq!( 5u8, result ); 57 | } 58 | 59 | 60 | 61 | // Joinhandle::detach allows task to keep running. 62 | // 63 | #[ test ] 64 | // 65 | fn join_handle_detach() 66 | { 67 | let exec = ThreadPool::new().expect( "create threadpool" ); 68 | 69 | let (in_tx , in_rx ) = oneshot::channel(); 70 | let (out_tx, out_rx) = oneshot::channel(); 71 | 72 | 73 | let in_join_handle = exec.spawn_handle( async move 74 | { 75 | let content = in_rx.await.expect( "receive on in" ); 76 | 77 | out_tx.send( content ).expect( "send on out" ); 78 | 79 | }).expect( "spawn task" ); 80 | 81 | 82 | in_join_handle.detach(); 83 | 84 | futures::executor::block_on( async move 85 | { 86 | in_tx.send( 5u8 ).expect( "send on in" ); 87 | 88 | assert_eq!( out_rx.await, Ok(5) ); 89 | }); 90 | } 91 | 92 | 93 | 94 | // pass an ThreadPool to a function that requires a Timer. 95 | // 96 | #[ cfg( feature = "timer" ) ] 97 | // 98 | #[ test ] 99 | // 100 | fn timer_should_wake() 101 | { 102 | let exec = ThreadPool::new().expect( "create threadpool" ); 103 | 104 | block_on( timer_should_wake_up( exec ) ); 105 | } 106 | 107 | 108 | 109 | // pass an ThreadPool to a function that requires a Timer. 110 | // 111 | #[ cfg( feature = "timer" ) ] 112 | // 113 | #[ test ] 114 | // 115 | fn run_timeout() 116 | { 117 | let exec = ThreadPool::new().expect( "create threadpool" ); 118 | 119 | block_on( timeout( exec ) ); 120 | } 121 | 122 | 123 | 124 | // pass an ThreadPool to a function that requires a Timer. 125 | // 126 | #[ cfg( feature = "timer" ) ] 127 | // 128 | #[ test ] 129 | // 130 | fn run_dont_timeout() 131 | { 132 | let exec = ThreadPool::new().expect( "create threadpool" ); 133 | 134 | block_on( dont_timeout( exec ) ); 135 | } 136 | 137 | 138 | 139 | // Verify ThreadPool does not implement Timer when feature is not enabled. 140 | // 141 | #[ cfg(not( feature = "timer" )) ] 142 | // 143 | #[ test ] 144 | // 145 | fn no_feature_no_timer() 146 | { 147 | static_assertions::assert_not_impl_any!( ThreadPool: Timer ); 148 | } 149 | -------------------------------------------------------------------------------- /tests/tokio_tp.rs: -------------------------------------------------------------------------------- 1 | #![ cfg(all( not(target_arch = "wasm32"), feature = "tokio_tp" )) ] 2 | // 3 | // Tested: 4 | // 5 | // ✔ build a TokioTp from a currently entered runtime. 6 | // ✔ build a TokioTp from a handle. 7 | // 8 | // ✔ pass a TokioTp to a function that takes exec: `impl Spawn` 9 | // ✔ pass a &TokioTp to a function that takes exec: `&impl Spawn` 10 | // ✔ pass a &TokioTp to a function that takes exec: `impl Spawn` 11 | // ✔ pass a &TokioTp to a function that takes exec: `impl Spawn + Clone` 12 | // ✔ pass a Arc to a function that takes exec: `impl Spawn` 13 | // ✔ pass a TokioTp to a function that takes exec: `impl SpawnHandle` 14 | // ✔ pass a Arc to a function that takes exec: `impl SpawnHandle` 15 | // ✔ pass a &TokioTp to a function that takes exec: `&dyn SpawnHandle` 16 | // ✔ pass a builder with some config set. 17 | // 18 | // ✔ pass a TokioTp to a function that requires a SpawnBlocking. 19 | // ✔ pass a TokioTp to a function that requires an object safe SpawnBlocking. 20 | // ✔ pass a TokioTp to a function that requires a Timer. 21 | // ✔ Verify TokioTp does not implement Timer when feature is not enabled. 22 | // ✔ Verify Timeout future. 23 | // 24 | // ✔ Verify tokio_io works when the tokio_io feature is enabled. 25 | // ✔ Verify tokio_io doesn't work when the tokio_io feature is not enabled. 26 | // 27 | // ✔ Joinhandle::detach allows task to keep running. 28 | // 29 | mod common; 30 | 31 | use 32 | { 33 | common :: { * } , 34 | futures :: { channel::{ mpsc, oneshot }, StreamExt } , 35 | std :: { convert::TryFrom } , 36 | tokio :: { runtime::{ Builder, Handle } } , 37 | }; 38 | 39 | 40 | // pass a TokioTp to a function that takes exec: `impl Spawn` 41 | // 42 | #[ test ] 43 | // 44 | fn spawn() 45 | { 46 | let (tx, mut rx) = mpsc::channel( 1 ); 47 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 48 | 49 | increment( 4, exec.clone(), tx ); 50 | 51 | let result = exec.block_on( rx.next() ).expect( "Some" ); 52 | 53 | assert_eq!( 5u8, result ); 54 | } 55 | 56 | 57 | // pass a &TokioTp to a function that takes exec: `&impl Spawn` 58 | // 59 | #[ test ] 60 | // 61 | fn spawn_ref() 62 | { 63 | let (tx, mut rx) = mpsc::channel( 1 ); 64 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 65 | 66 | increment_ref( 4, &exec, tx ); 67 | 68 | let result = exec.block_on( rx.next() ).expect( "Some" ); 69 | 70 | assert_eq!( 5u8, result ); 71 | } 72 | 73 | 74 | // pass a &TokioTp to a function that takes exec: `impl Spawn` 75 | // 76 | #[ test ] 77 | // 78 | fn spawn_with_ref() 79 | { 80 | let (tx, mut rx) = mpsc::channel( 1 ); 81 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 82 | 83 | increment( 4, &exec, tx ); 84 | 85 | let result = exec.block_on( rx.next() ).expect( "Some" ); 86 | 87 | assert_eq!( 5u8, result ); 88 | } 89 | 90 | 91 | // pass a &TokioTp to a function that takes exec: `impl Spawn + Clone` 92 | // 93 | #[ test ] 94 | // 95 | fn spawn_clone_with_ref() 96 | { 97 | let (tx, mut rx) = mpsc::channel( 1 ); 98 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 99 | 100 | increment_clone( 4, &exec, tx ); 101 | 102 | let result = exec.block_on( rx.next() ).expect( "Some" ); 103 | 104 | assert_eq!( 5u8, result ); 105 | } 106 | 107 | 108 | // pass a Arc to a function that takes exec: `impl Spawn`. 109 | // Possible since futures 0.3.2. 110 | // 111 | #[ test ] 112 | // 113 | fn spawn_clone_with_arc() 114 | { 115 | let (tx, mut rx) = mpsc::channel( 1 ); 116 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 117 | 118 | increment( 4, Arc::new( exec.clone() ), tx ); 119 | 120 | let result = exec.block_on( rx.next() ).expect( "Some" ); 121 | 122 | assert_eq!( 5u8, result ); 123 | } 124 | 125 | 126 | // pass a TokioTp to a function that takes exec: `impl SpawnHandle` 127 | // 128 | #[ test ] 129 | // 130 | fn spawn_handle() 131 | { 132 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 133 | 134 | let result = exec.block_on( increment_spawn_handle( 4, exec.clone() ) ); 135 | 136 | assert_eq!( 5u8, result ); 137 | } 138 | 139 | 140 | // pass an Arc to a function that takes exec: `impl SpawnHandle` 141 | // 142 | #[ test ] 143 | // 144 | fn spawn_handle_arc() 145 | { 146 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 147 | 148 | let result = exec.block_on( increment_spawn_handle( 4, Arc::new( exec.clone() ) ) ); 149 | 150 | assert_eq!( 5u8, result ); 151 | } 152 | 153 | 154 | // pass a AsyncStd to a function that takes exec: `&dyn SpawnHandle` 155 | // 156 | #[ test ] 157 | // 158 | fn spawn_handle_os() 159 | { 160 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 161 | 162 | let result = exec.block_on( increment_spawn_handle_os( 4, &exec ) ); 163 | 164 | assert_eq!( 5u8, result ); 165 | } 166 | 167 | 168 | // pass a builder with some config set. 169 | // 170 | #[ test ] 171 | // 172 | fn build_name_thread() 173 | { 174 | let (tx, rx) = oneshot::channel(); 175 | 176 | let rt = Builder::new_multi_thread() 177 | .thread_name( "test_thread" ) 178 | .build() 179 | .expect( "tokio builder" ) 180 | ; 181 | 182 | let exec = TokioTp::try_from( rt ).expect( "create TokioTp from builder" ); 183 | 184 | let task = async move 185 | { 186 | let name = std::thread::current().name().expect( "some name" ).to_string(); 187 | tx.send( name ).expect( "send on oneshot" ); 188 | }; 189 | 190 | exec.spawn( task ).expect( "spawn" ); 191 | 192 | exec.block_on( async 193 | { 194 | assert_eq!( rx.await.expect( "read channel" ), "test_thread" ); 195 | 196 | }); 197 | } 198 | 199 | 200 | // build from current. 201 | // 202 | #[ tokio::test(flavor = "multi_thread") ] 203 | // 204 | async fn build_from_current() 205 | { 206 | let (tx, rx) = oneshot::channel(); 207 | 208 | let exec = TokioTp::try_current().expect( "create TokioTp from current" ); 209 | 210 | let task = async move 211 | { 212 | let name = std::thread::current().name().expect( "some name" ).to_string(); 213 | tx.send( name ).expect( "send on oneshot" ); 214 | }; 215 | 216 | exec.spawn( task ).expect( "spawn" ); 217 | 218 | assert_eq!( rx.await.expect( "read channel" ), "tokio-runtime-worker" ); 219 | } 220 | 221 | 222 | 223 | // build from a `Handle`. 224 | // 225 | #[ tokio::test(flavor = "multi_thread") ] 226 | // 227 | async fn build_from_handle() 228 | { 229 | let (tx, rx) = oneshot::channel(); 230 | let handle = Handle::current(); 231 | 232 | let exec = TokioTp::try_from( handle ).expect( "create TokioTp from current" ); 233 | 234 | let task = async move 235 | { 236 | let name = std::thread::current().name().expect( "some name" ).to_string(); 237 | tx.send( name ).expect( "send on oneshot" ); 238 | }; 239 | 240 | exec.spawn( task ).expect( "spawn" ); 241 | 242 | assert_eq!( rx.await.expect( "read channel" ), "tokio-runtime-worker" ); 243 | } 244 | 245 | 246 | 247 | // Joinhandle::detach allows task to keep running. 248 | // 249 | #[ test ] 250 | // 251 | fn join_handle_detach() 252 | { 253 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 254 | 255 | let (in_tx , in_rx ) = oneshot::channel(); 256 | let (out_tx, out_rx) = oneshot::channel(); 257 | 258 | 259 | let in_join_handle = exec.spawn_handle( async move 260 | { 261 | let content = in_rx.await.expect( "receive on in" ); 262 | 263 | out_tx.send( content ).expect( "send on out" ); 264 | 265 | }).expect( "spawn task" ); 266 | 267 | 268 | in_join_handle.detach(); 269 | 270 | exec.block_on( async move 271 | { 272 | in_tx.send( 5u8 ).expect( "send on in" ); 273 | 274 | assert_eq!( out_rx.await, Ok(5) ); 275 | }); 276 | } 277 | 278 | 279 | 280 | // pass an TokioTp to a function that requires a Timer. 281 | // 282 | #[ cfg(any( feature="timer", feature="tokio_timer" )) ] 283 | // 284 | #[ test ] 285 | // 286 | fn timer_should_wake() 287 | { 288 | let exec = TokioTp::new().expect( "create tokio current thread" ); 289 | 290 | exec.block_on( timer_should_wake_up( exec.clone() ) ); 291 | } 292 | 293 | 294 | 295 | // pass an TokioTp to a function that requires a Timer. 296 | // 297 | #[ cfg(any( feature="timer", feature="tokio_timer" )) ] 298 | // 299 | #[ test ] 300 | // 301 | fn run_timeout() 302 | { 303 | let exec = &TokioTp::new().expect( "create tokio current thread" ); 304 | 305 | exec.block_on( timeout( exec ) ); 306 | } 307 | 308 | 309 | 310 | // pass an TokioTp to a function that requires a Timer. 311 | // 312 | #[ cfg(any( feature="timer", feature="tokio_timer" )) ] 313 | // 314 | #[ test ] 315 | // 316 | fn run_dont_timeout() 317 | { 318 | let exec = &TokioTp::new().expect( "create tokio current thread" ); 319 | 320 | exec.block_on( dont_timeout( exec ) ); 321 | } 322 | 323 | 324 | 325 | // Verify TokioTp does not implement Timer when feature is not enabled. 326 | // 327 | #[ cfg(not(any( feature="timer", feature="tokio_timer" ))) ] 328 | // 329 | #[ test ] 330 | // 331 | fn no_feature_no_timer() 332 | { 333 | static_assertions::assert_not_impl_any!( TokioTp: Timer ); 334 | } 335 | 336 | 337 | 338 | // pass a TokioTp to a function that requires SpawnBlocking. 339 | // 340 | #[ test ] 341 | // 342 | fn spawn_blocking() -> DynResult<()> 343 | { 344 | let exec = &TokioTp::new()?; 345 | 346 | exec.block_on( blocking( exec ) ) 347 | } 348 | 349 | 350 | 351 | // pass a TokioTp to a function that requires an object safe SpawnBlocking. 352 | // 353 | #[ test ] 354 | // 355 | fn spawn_blocking_void() -> DynResult<()> 356 | { 357 | let exec = &TokioTp::new()?; 358 | 359 | exec.block_on( blocking_void( exec ) ) 360 | } 361 | 362 | 363 | 364 | // Verify tokio_io works when the tokio_io feature is enabled. 365 | // 366 | #[ cfg( feature = "tokio_io" ) ] 367 | // 368 | #[ test ] 369 | // 370 | fn tokio_io() -> DynResult<()> 371 | { 372 | let exec = TokioTp::new()?; 373 | 374 | exec.block_on( tokio_io::tcp( exec.clone() ) ) 375 | } 376 | 377 | 378 | // Verify tokio_io doesn't work when the tokio_io feature is not enabled. 379 | // 380 | #[ cfg(not( feature = "tokio_io" )) ] 381 | // 382 | #[ test ] #[ should_panic ] 383 | // 384 | fn no_tokio_io() 385 | { 386 | let exec = TokioTp::new().expect( "create tokio threadpool" ); 387 | 388 | let test = async 389 | { 390 | let _ = tokio_io::socket_pair().await.expect( "socket_pair" ); 391 | }; 392 | 393 | exec.block_on( test ); 394 | } 395 | -------------------------------------------------------------------------------- /tests/unpin.rs: -------------------------------------------------------------------------------- 1 | #![ cfg(all( nightly, not(target_os = "unknown"), any( feature = "threadpool", feature = "async_std" ) )) ] 2 | #![ feature( negative_impls )] 3 | 4 | // Tested: 5 | // 6 | // ✔ What happens to joinhandle if T is Unpin. The handle is unpin even if T isn't, 7 | // even without a manual unpin implementation. 8 | // 9 | mod common; 10 | 11 | use common::*; 12 | 13 | 14 | #[ derive( PartialEq, Eq, Debug ) ] 15 | // 16 | struct Boon; 17 | 18 | impl !Unpin for Boon {} 19 | 20 | 21 | fn assert_unpin( _t: &T ) {} 22 | 23 | 24 | 25 | 26 | #[ cfg( feature = "threadpool" ) ] 27 | // 28 | use futures::executor::ThreadPool; 29 | 30 | // the handle is unpin even if T isn't 31 | // 32 | #[ test ] #[ cfg( feature = "threadpool" ) ] 33 | // 34 | fn unpin_handle() 35 | { 36 | let exec = ThreadPool::new().expect( "create threadpool" ); 37 | 38 | let join_handle = exec.spawn_handle( async { Boon } ).expect( "spawn" ); 39 | 40 | assert_unpin( &join_handle ); 41 | let t = block_on( join_handle ); 42 | 43 | assert_eq!( Boon, t ); 44 | } 45 | 46 | 47 | // the handle is unpin even if T isn't 48 | // 49 | #[ test ] #[ cfg( feature = "async_std" ) ] 50 | // 51 | fn unpin_handle_async_std() 52 | { 53 | let join_handle = AsyncStd.spawn_handle( async { Boon } ).expect( "spawn" ); 54 | 55 | assert_unpin( &join_handle ); 56 | let t = AsyncStd::block_on( join_handle ); 57 | 58 | assert_eq!( Boon, t ); 59 | } 60 | --------------------------------------------------------------------------------