mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-02 00:00:11 +02:00
Merge 'master' and 'tokio-1.20.1'
This commit is contained in:
@@ -34,16 +34,18 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- test
|
- test
|
||||||
- test-unstable
|
|
||||||
- test-parking_lot
|
- test-parking_lot
|
||||||
|
- valgrind
|
||||||
|
- test-unstable
|
||||||
- miri
|
- miri
|
||||||
|
- asan
|
||||||
- cross
|
- cross
|
||||||
- features
|
- features
|
||||||
- minrust
|
- minrust
|
||||||
|
- minimal-versions
|
||||||
- fmt
|
- fmt
|
||||||
- clippy
|
- clippy
|
||||||
- docs
|
- docs
|
||||||
- valgrind
|
|
||||||
- loom-compile
|
- loom-compile
|
||||||
- check-readme
|
- check-readme
|
||||||
- test-hyper
|
- test-hyper
|
||||||
@@ -195,7 +197,7 @@ jobs:
|
|||||||
- name: Install Rust ${{ env.rust_nightly }}
|
- name: Install Rust ${{ env.rust_nightly }}
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly-2022-07-10
|
toolchain: nightly
|
||||||
components: miri
|
components: miri
|
||||||
override: true
|
override: true
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v1
|
||||||
@@ -489,9 +491,23 @@ jobs:
|
|||||||
- name: Install cargo-wasi
|
- name: Install cargo-wasi
|
||||||
run: cargo install cargo-wasi
|
run: cargo install cargo-wasi
|
||||||
|
|
||||||
# TODO: Expand this when full WASI support lands.
|
- name: WASI test tokio full
|
||||||
# Currently, this is a bare bones regression test
|
run: cargo test -p tokio --target wasm32-wasi --features full
|
||||||
# for features that work today with wasi.
|
env:
|
||||||
|
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
|
||||||
|
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||||
|
|
||||||
|
- name: WASI test tokio-util full
|
||||||
|
run: cargo test -p tokio-util --target wasm32-wasi --features full
|
||||||
|
env:
|
||||||
|
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
|
||||||
|
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||||
|
|
||||||
|
- name: WASI test tokio-stream
|
||||||
|
run: cargo test -p tokio-stream --target wasm32-wasi --features time,net,io-util,sync
|
||||||
|
env:
|
||||||
|
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
|
||||||
|
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||||
|
|
||||||
- name: test tests-integration --features wasi-rt
|
- name: test tests-integration --features wasi-rt
|
||||||
# TODO: this should become: `cargo hack wasi test --each-feature`
|
# TODO: this should become: `cargo hack wasi test --each-feature`
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ env:
|
|||||||
rust_stable: stable
|
rust_stable: stable
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stess-test:
|
stress-test:
|
||||||
name: Stress Test
|
name: Stress Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
|||||||
+1
-1
@@ -562,7 +562,7 @@ Tokio ≥1.0.0 comes with LTS guarantees:
|
|||||||
|
|
||||||
The goal of these guarantees is to provide stability to the ecosystem.
|
The goal of these guarantees is to provide stability to the ecosystem.
|
||||||
|
|
||||||
## Mininum Supported Rust Version (MSRV)
|
## Minimum Supported Rust Version (MSRV)
|
||||||
|
|
||||||
* All Tokio ≥1.0.0 releases will support at least a 6-month old Rust
|
* All Tokio ≥1.0.0 releases will support at least a 6-month old Rust
|
||||||
compiler release.
|
compiler release.
|
||||||
|
|||||||
+4
-4
@@ -1,13 +1,13 @@
|
|||||||
## Report a security issue
|
## Report a security issue
|
||||||
|
|
||||||
The Tokio project team welcomes security reports and is committed to providing prompt attention to security issues. Security issues should be reported privately via [[email protected]](mailto:[email protected]). Security issues should not be reported via the public Github Issue tracker.
|
The Tokio project team welcomes security reports and is committed to providing prompt attention to security issues. Security issues should be reported privately via [[email protected]](mailto:[email protected]). Security issues should not be reported via the public GitHub Issue tracker.
|
||||||
|
|
||||||
## Vulnerability coordination
|
## Vulnerability coordination
|
||||||
|
|
||||||
Remediation of security vulnerabilities is prioritized by the project team. The project team coordinates remediation with third-party project stakeholders via [Github Security Advisories](https://help.github.com/en/github/managing-security-vulnerabilities/about-github-security-advisories). Third-party stakeholders may include the reporter of the issue, affected direct or indirect users of Tokio, and maintainers of upstream dependencies if applicable.
|
Remediation of security vulnerabilities is prioritized by the project team. The project team coordinates remediation with third-party project stakeholders via [GitHub Security Advisories](https://help.github.com/en/github/managing-security-vulnerabilities/about-github-security-advisories). Third-party stakeholders may include the reporter of the issue, affected direct or indirect users of Tokio, and maintainers of upstream dependencies if applicable.
|
||||||
|
|
||||||
Downstream project maintainers and Tokio users can request participation in coordination of applicable security issues by sending your contact email address, Github username(s) and any other salient information to [[email protected]](mailto:[email protected]). Participation in security issue coordination processes is at the discretion of the Tokio team.
|
Downstream project maintainers and Tokio users can request participation in coordination of applicable security issues by sending your contact email address, GitHub username(s) and any other salient information to [[email protected]](mailto:[email protected]). Participation in security issue coordination processes is at the discretion of the Tokio team.
|
||||||
|
|
||||||
## Security advisories
|
## Security advisories
|
||||||
|
|
||||||
The project team is committed to transparency in the security issue disclosure process. The Tokio team announces security issues via [project Github Release notes](https://github.com/tokio-rs/tokio/releases) and the [RustSec advisory database](https://github.com/RustSec/advisory-db) (i.e. `cargo-audit`).
|
The project team is committed to transparency in the security issue disclosure process. The Tokio team announces security issues via [project GitHub Release notes](https://github.com/tokio-rs/tokio/releases) and the [RustSec advisory database](https://github.com/RustSec/advisory-db) (i.e. `cargo-audit`).
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ publish = false
|
|||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "test-cat"
|
name = "test-cat"
|
||||||
required-features = ["rt-process-io-util"]
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "test-mem"
|
name = "test-mem"
|
||||||
@@ -31,7 +30,6 @@ name = "rt_yield"
|
|||||||
required-features = ["rt", "macros", "sync"]
|
required-features = ["rt", "macros", "sync"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
rt-process-io-util = ["tokio/rt", "tokio/macros", "tokio/process", "tokio/io-util", "tokio/io-std"]
|
|
||||||
# For mem check
|
# For mem check
|
||||||
rt-net = ["tokio/rt", "tokio/rt-multi-thread", "tokio/net"]
|
rt-net = ["tokio/rt", "tokio/rt-multi-thread", "tokio/net"]
|
||||||
# For test-process-signal
|
# For test-process-signal
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
//! A cat-like utility that can be used as a subprocess to test I/O
|
//! A cat-like utility that can be used as a subprocess to test I/O
|
||||||
//! stream communication.
|
//! stream communication.
|
||||||
|
|
||||||
use tokio::io::AsyncWriteExt;
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
fn main() {
|
||||||
async fn main() {
|
let stdin = io::stdin();
|
||||||
let mut stdin = tokio::io::stdin();
|
let mut stdout = io::stdout();
|
||||||
let mut stdout = tokio::io::stdout();
|
let mut line = String::new();
|
||||||
|
loop {
|
||||||
tokio::io::copy(&mut stdin, &mut stdout).await.unwrap();
|
line.clear();
|
||||||
|
stdin.read_line(&mut line).unwrap();
|
||||||
stdout.flush().await.unwrap();
|
if line.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stdout.write_all(line.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
stdout.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
#![cfg(all(feature = "macros", feature = "rt-multi-thread"))]
|
#![cfg(all(
|
||||||
|
feature = "macros",
|
||||||
|
feature = "rt-multi-thread",
|
||||||
|
not(target_os = "wasi")
|
||||||
|
))]
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn basic_main() -> usize {
|
async fn basic_main() -> usize {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use futures::channel::oneshot;
|
|||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
#[cfg_attr(target_os = "wasi", ignore = "WASI: std::thread::spawn not supported")]
|
||||||
#[test]
|
#[test]
|
||||||
fn join_with_select() {
|
fn join_with_select() {
|
||||||
block_on(async {
|
block_on(async {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))]
|
||||||
|
|
||||||
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::join;
|
use tokio::join;
|
||||||
@@ -8,22 +8,12 @@ use tokio_test::assert_ok;
|
|||||||
|
|
||||||
use futures::future::{self, FutureExt};
|
use futures::future::{self, FutureExt};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
use std::env;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::process::{ExitStatus, Stdio};
|
use std::process::{ExitStatus, Stdio};
|
||||||
|
|
||||||
// so, we need to change this back as a test, but for now this doesn't work because of:
|
|
||||||
// https://github.com/rust-lang/rust/pull/95469
|
|
||||||
//
|
|
||||||
// undo when this is closed: https://github.com/tokio-rs/tokio/issues/4802
|
|
||||||
|
|
||||||
// fn cat() -> Command {
|
|
||||||
// let mut cmd = Command::new(std::env!("CARGO_BIN_EXE_test-cat"));
|
|
||||||
// cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
|
|
||||||
// cmd
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn cat() -> Command {
|
fn cat() -> Command {
|
||||||
let mut cmd = Command::new("cat");
|
let mut cmd = Command::new(env!("CARGO_BIN_EXE_test-cat"));
|
||||||
cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
|
cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ parking_lot = "0.12.0"
|
|||||||
tokio-test = { path = "../tokio-test" }
|
tokio-test = { path = "../tokio-test" }
|
||||||
futures = { version = "0.3", default-features = false }
|
futures = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||||
proptest = "1"
|
proptest = "1"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "time")]
|
#![cfg(all(feature = "time", not(target_os = "wasi")))] // Wasi does not support panic recovery
|
||||||
|
|
||||||
use parking_lot::{const_mutex, Mutex};
|
use parking_lot::{const_mutex, Mutex};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|||||||
@@ -325,6 +325,7 @@ fn one_ready_many_none() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
proptest::proptest! {
|
proptest::proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn fuzz_pending_complete_mix(kinds: Vec<bool>) {
|
fn fuzz_pending_complete_mix(kinds: Vec<bool>) {
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ macro_rules! assert_err {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asserts that an exact duration has elapsed since since the start instant ±1ms.
|
/// Asserts that an exact duration has elapsed since the start instant ±1ms.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use tokio::time::{self, Instant};
|
/// use tokio::time::{self, Instant};
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ cfg_codec! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg_net! {
|
cfg_net! {
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub mod udp;
|
pub mod udp;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ where
|
|||||||
/// `parent` MUST have been a parent of the node when they both got locked,
|
/// `parent` MUST have been a parent of the node when they both got locked,
|
||||||
/// otherwise there is a potential for a deadlock as invariant #2 would be violated.
|
/// otherwise there is a potential for a deadlock as invariant #2 would be violated.
|
||||||
///
|
///
|
||||||
/// To aquire the locks for node and parent, use [with_locked_node_and_parent].
|
/// To acquire the locks for node and parent, use [with_locked_node_and_parent].
|
||||||
fn move_children_to_parent(node: &mut Inner, parent: &mut Inner) {
|
fn move_children_to_parent(node: &mut Inner, parent: &mut Inner) {
|
||||||
// Pre-allocate in the parent, for performance
|
// Pre-allocate in the parent, for performance
|
||||||
parent.children.reserve(node.children.len());
|
parent.children.reserve(node.children.len());
|
||||||
@@ -218,7 +218,7 @@ fn move_children_to_parent(node: &mut Inner, parent: &mut Inner) {
|
|||||||
/// Removes a child from the parent.
|
/// Removes a child from the parent.
|
||||||
///
|
///
|
||||||
/// `parent` MUST be the parent of `node`.
|
/// `parent` MUST be the parent of `node`.
|
||||||
/// To aquire the locks for node and parent, use [with_locked_node_and_parent].
|
/// To acquire the locks for node and parent, use [with_locked_node_and_parent].
|
||||||
fn remove_child(parent: &mut Inner, mut node: MutexGuard<'_, Inner>) {
|
fn remove_child(parent: &mut Inner, mut node: MutexGuard<'_, Inner>) {
|
||||||
// Query the position from where to remove a node
|
// Query the position from where to remove a node
|
||||||
let pos = node.parent_idx;
|
let pos = node.parent_idx;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ fn drop_token_no_child() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn drop_token_with_childs() {
|
fn drop_token_with_children() {
|
||||||
loom::model(|| {
|
loom::model(|| {
|
||||||
let token1 = CancellationToken::new();
|
let token1 = CancellationToken::new();
|
||||||
let child_token1 = token1.child_token();
|
let child_token1 = token1.child_token();
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
mod join_map;
|
mod join_map;
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
mod spawn_pinned;
|
mod spawn_pinned;
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
pub use spawn_pinned::LocalPoolHandle;
|
pub use spawn_pinned::LocalPoolHandle;
|
||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ impl<T> SlabStorage<T> {
|
|||||||
let key_contained = self.key_map.contains_key(&key.into());
|
let key_contained = self.key_map.contains_key(&key.into());
|
||||||
|
|
||||||
if key_contained {
|
if key_contained {
|
||||||
// It's possible that a `compact` call creates capacitiy in `self.inner` in
|
// It's possible that a `compact` call creates capacity in `self.inner` in
|
||||||
// such a way that a `self.inner.insert` call creates a `key` which was
|
// such a way that a `self.inner.insert` call creates a `key` which was
|
||||||
// previously given out during an `insert` call prior to the `compact` call.
|
// previously given out during an `insert` call prior to the `compact` call.
|
||||||
// If `key` is contained in `self.key_map`, we have encountered this exact situation,
|
// If `key` is contained in `self.key_map`, we have encountered this exact situation,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#![cfg(feature = "rt")]
|
#![cfg(feature = "rt")]
|
||||||
|
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
use tokio::runtime::Builder;
|
use tokio::runtime::Builder;
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ fn write_hits_backpressure() {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push a new new chunk
|
// Push a new chunk
|
||||||
mock.calls.push_back(Ok(b[..].to_vec()));
|
mock.calls.push_back(Ok(b[..].to_vec()));
|
||||||
}
|
}
|
||||||
// 1 'wouldblock', 4 * 2KB buffers, 1 b-byte buffer
|
// 1 'wouldblock', 4 * 2KB buffers, 1 b-byte buffer
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#![cfg(feature = "io-util")]
|
#![cfg(feature = "io-util")]
|
||||||
|
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::io::{Cursor, Read, Result as IoResult};
|
use std::io::{Cursor, Read, Result as IoResult};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support panic recovery
|
||||||
|
|
||||||
use parking_lot::{const_mutex, Mutex};
|
use parking_lot::{const_mutex, Mutex};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ fn cancel_only_all_descendants() {
|
|||||||
let child2_token = token.child_token();
|
let child2_token = token.child_token();
|
||||||
let grandchild_token = child1_token.child_token();
|
let grandchild_token = child1_token.child_token();
|
||||||
let grandchild2_token = child1_token.child_token();
|
let grandchild2_token = child1_token.child_token();
|
||||||
let grandgrandchild_token = grandchild_token.child_token();
|
let great_grandchild_token = grandchild_token.child_token();
|
||||||
|
|
||||||
assert!(!parent_token.is_cancelled());
|
assert!(!parent_token.is_cancelled());
|
||||||
assert!(!token.is_cancelled());
|
assert!(!token.is_cancelled());
|
||||||
@@ -267,7 +267,7 @@ fn cancel_only_all_descendants() {
|
|||||||
assert!(!child2_token.is_cancelled());
|
assert!(!child2_token.is_cancelled());
|
||||||
assert!(!grandchild_token.is_cancelled());
|
assert!(!grandchild_token.is_cancelled());
|
||||||
assert!(!grandchild2_token.is_cancelled());
|
assert!(!grandchild2_token.is_cancelled());
|
||||||
assert!(!grandgrandchild_token.is_cancelled());
|
assert!(!great_grandchild_token.is_cancelled());
|
||||||
|
|
||||||
let parent_fut = parent_token.cancelled();
|
let parent_fut = parent_token.cancelled();
|
||||||
let fut = token.cancelled();
|
let fut = token.cancelled();
|
||||||
@@ -276,7 +276,7 @@ fn cancel_only_all_descendants() {
|
|||||||
let child2_fut = child2_token.cancelled();
|
let child2_fut = child2_token.cancelled();
|
||||||
let grandchild_fut = grandchild_token.cancelled();
|
let grandchild_fut = grandchild_token.cancelled();
|
||||||
let grandchild2_fut = grandchild2_token.cancelled();
|
let grandchild2_fut = grandchild2_token.cancelled();
|
||||||
let grandgrandchild_fut = grandgrandchild_token.cancelled();
|
let great_grandchild_fut = great_grandchild_token.cancelled();
|
||||||
|
|
||||||
pin!(parent_fut);
|
pin!(parent_fut);
|
||||||
pin!(fut);
|
pin!(fut);
|
||||||
@@ -285,7 +285,7 @@ fn cancel_only_all_descendants() {
|
|||||||
pin!(child2_fut);
|
pin!(child2_fut);
|
||||||
pin!(grandchild_fut);
|
pin!(grandchild_fut);
|
||||||
pin!(grandchild2_fut);
|
pin!(grandchild2_fut);
|
||||||
pin!(grandgrandchild_fut);
|
pin!(great_grandchild_fut);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Poll::Pending,
|
Poll::Pending,
|
||||||
@@ -321,7 +321,7 @@ fn cancel_only_all_descendants() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Poll::Pending,
|
Poll::Pending,
|
||||||
grandgrandchild_fut
|
great_grandchild_fut
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.poll(&mut Context::from_waker(&waker))
|
.poll(&mut Context::from_waker(&waker))
|
||||||
);
|
);
|
||||||
@@ -339,7 +339,7 @@ fn cancel_only_all_descendants() {
|
|||||||
assert!(child2_token.is_cancelled());
|
assert!(child2_token.is_cancelled());
|
||||||
assert!(grandchild_token.is_cancelled());
|
assert!(grandchild_token.is_cancelled());
|
||||||
assert!(grandchild2_token.is_cancelled());
|
assert!(grandchild2_token.is_cancelled());
|
||||||
assert!(grandgrandchild_token.is_cancelled());
|
assert!(great_grandchild_token.is_cancelled());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Poll::Ready(()),
|
Poll::Ready(()),
|
||||||
@@ -367,7 +367,7 @@ fn cancel_only_all_descendants() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Poll::Ready(()),
|
Poll::Ready(()),
|
||||||
grandgrandchild_fut
|
great_grandchild_fut
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.poll(&mut Context::from_waker(&waker))
|
.poll(&mut Context::from_waker(&waker))
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -778,6 +778,7 @@ async fn compact_change_deadline() {
|
|||||||
assert!(entry.is_none());
|
assert!(entry.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_os = "wasi", ignore = "FIXME: Does not seem to work with WASI")]
|
||||||
#[tokio::test(start_paused = true)]
|
#[tokio::test(start_paused = true)]
|
||||||
async fn remove_after_compact() {
|
async fn remove_after_compact() {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
@@ -794,6 +795,7 @@ async fn remove_after_compact() {
|
|||||||
assert!(panic.is_err());
|
assert!(panic.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_os = "wasi", ignore = "FIXME: Does not seem to work with WASI")]
|
||||||
#[tokio::test(start_paused = true)]
|
#[tokio::test(start_paused = true)]
|
||||||
async fn remove_after_compact_poll() {
|
async fn remove_after_compact_poll() {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support UDP
|
||||||
|
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
|
|||||||
+11
-6
@@ -51,13 +51,14 @@ net = [
|
|||||||
"mio/os-poll",
|
"mio/os-poll",
|
||||||
"mio/os-ext",
|
"mio/os-ext",
|
||||||
"mio/net",
|
"mio/net",
|
||||||
"socket2",
|
|
||||||
"winapi/fileapi",
|
"winapi/fileapi",
|
||||||
"winapi/handleapi",
|
"winapi/handleapi",
|
||||||
"winapi/namedpipeapi",
|
"winapi/namedpipeapi",
|
||||||
"winapi/winbase",
|
"winapi/winbase",
|
||||||
"winapi/winnt",
|
"winapi/winnt",
|
||||||
"winapi/minwindef",
|
"winapi/minwindef",
|
||||||
|
"winapi/accctrl",
|
||||||
|
"winapi/aclapi"
|
||||||
]
|
]
|
||||||
process = [
|
process = [
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -68,11 +69,11 @@ process = [
|
|||||||
"mio/net",
|
"mio/net",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"winapi/handleapi",
|
"winapi/handleapi",
|
||||||
|
"winapi/minwindef",
|
||||||
"winapi/processthreadsapi",
|
"winapi/processthreadsapi",
|
||||||
"winapi/threadpoollegacyapiset",
|
"winapi/threadpoollegacyapiset",
|
||||||
"winapi/winbase",
|
"winapi/winbase",
|
||||||
"winapi/winnt",
|
"winapi/winnt",
|
||||||
"winapi/minwindef",
|
|
||||||
]
|
]
|
||||||
# Includes basic task execution capabilities
|
# Includes basic task execution capabilities
|
||||||
rt = ["once_cell"]
|
rt = ["once_cell"]
|
||||||
@@ -112,11 +113,13 @@ pin-project-lite = "0.2.0"
|
|||||||
bytes = { version = "1.0.0", optional = true }
|
bytes = { version = "1.0.0", optional = true }
|
||||||
once_cell = { version = "1.5.2", optional = true }
|
once_cell = { version = "1.5.2", optional = true }
|
||||||
memchr = { version = "2.2", optional = true }
|
memchr = { version = "2.2", optional = true }
|
||||||
mio = { version = "0.8.1", optional = true }
|
mio = { version = "0.8.4", optional = true }
|
||||||
socket2 = { version = "0.4.4", optional = true, features = [ "all" ] }
|
|
||||||
num_cpus = { version = "1.8.0", optional = true }
|
num_cpus = { version = "1.8.0", optional = true }
|
||||||
parking_lot = { version = "0.12.0", optional = true }
|
parking_lot = { version = "0.12.0", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
socket2 = { version = "0.4.4", features = [ "all" ] }
|
||||||
|
|
||||||
# Currently unstable. The API exposed by these features may be broken at any time.
|
# Currently unstable. The API exposed by these features may be broken at any time.
|
||||||
# Requires `--cfg tokio_unstable` to enable.
|
# Requires `--cfg tokio_unstable` to enable.
|
||||||
[target.'cfg(tokio_unstable)'.dependencies]
|
[target.'cfg(tokio_unstable)'.dependencies]
|
||||||
@@ -149,10 +152,12 @@ async-stream = "0.3"
|
|||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||||
proptest = "1"
|
proptest = "1"
|
||||||
rand = "0.8.0"
|
|
||||||
socket2 = "0.4"
|
socket2 = "0.4"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies]
|
||||||
|
rand = "0.8.0"
|
||||||
|
|
||||||
|
[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dev-dependencies]
|
||||||
wasm-bindgen-test = "0.3.0"
|
wasm-bindgen-test = "0.3.0"
|
||||||
|
|
||||||
[target.'cfg(target_os = "freebsd")'.dev-dependencies]
|
[target.'cfg(target_os = "freebsd")'.dev-dependencies]
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ fn main() {
|
|||||||
enable_const_thread_local = true;
|
enable_const_thread_local = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !ac.probe_rustc_version(1, 51) {
|
||||||
|
autocfg::emit("tokio_no_addr_of")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@@ -188,9 +188,9 @@ readiness, the driver's tick is packed into the atomic `usize`.
|
|||||||
The `ScheduledIo` readiness `AtomicUsize` is structured as:
|
The `ScheduledIo` readiness `AtomicUsize` is structured as:
|
||||||
|
|
||||||
```
|
```
|
||||||
| reserved | generation | driver tick | readinesss |
|
| reserved | generation | driver tick | readiness |
|
||||||
|----------+------------+--------------+------------|
|
|----------+------------+--------------+-----------|
|
||||||
| 1 bit | 7 bits + 8 bits + 16 bits |
|
| 1 bit | 7 bits + 8 bits + 16 bits |
|
||||||
```
|
```
|
||||||
|
|
||||||
The `reserved` and `generation` components exist today.
|
The `reserved` and `generation` components exist today.
|
||||||
|
|||||||
+2
-2
@@ -207,7 +207,7 @@ cfg_coop! {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||||
use wasm_bindgen_test::wasm_bindgen_test as test;
|
use wasm_bindgen_test::wasm_bindgen_test as test;
|
||||||
|
|
||||||
fn get() -> Budget {
|
fn get() -> Budget {
|
||||||
@@ -215,7 +215,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bugeting() {
|
fn budgeting() {
|
||||||
use futures::future::poll_fn;
|
use futures::future::poll_fn;
|
||||||
use tokio_test::*;
|
use tokio_test::*;
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,9 @@ enum State<T> {
|
|||||||
Busy(sys::Blocking<(io::Result<usize>, Buf, T)>),
|
Busy(sys::Blocking<(io::Result<usize>, Buf, T)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_io_std! {
|
cfg_io_blocking! {
|
||||||
impl<T> Blocking<T> {
|
impl<T> Blocking<T> {
|
||||||
|
#[cfg_attr(feature = "fs", allow(dead_code))]
|
||||||
pub(crate) fn new(inner: T) -> Blocking<T> {
|
pub(crate) fn new(inner: T) -> Blocking<T> {
|
||||||
Blocking {
|
Blocking {
|
||||||
inner: Some(inner),
|
inner: Some(inner),
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ pub(super) struct Inner {
|
|||||||
io_dispatch: RwLock<IoDispatcher>,
|
io_dispatch: RwLock<IoDispatcher>,
|
||||||
|
|
||||||
/// Used to wake up the reactor from a call to `turn`.
|
/// Used to wake up the reactor from a call to `turn`.
|
||||||
|
/// Not supported on Wasi due to lack of threading support.
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
waker: mio::Waker,
|
waker: mio::Waker,
|
||||||
|
|
||||||
metrics: IoDriverMetrics,
|
metrics: IoDriverMetrics,
|
||||||
@@ -115,6 +117,7 @@ impl Driver {
|
|||||||
/// creation.
|
/// creation.
|
||||||
pub(crate) fn new() -> io::Result<Driver> {
|
pub(crate) fn new() -> io::Result<Driver> {
|
||||||
let poll = mio::Poll::new()?;
|
let poll = mio::Poll::new()?;
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
let waker = mio::Waker::new(poll.registry(), TOKEN_WAKEUP)?;
|
let waker = mio::Waker::new(poll.registry(), TOKEN_WAKEUP)?;
|
||||||
let registry = poll.registry().try_clone()?;
|
let registry = poll.registry().try_clone()?;
|
||||||
|
|
||||||
@@ -129,6 +132,7 @@ impl Driver {
|
|||||||
inner: Arc::new(Inner {
|
inner: Arc::new(Inner {
|
||||||
registry,
|
registry,
|
||||||
io_dispatch: RwLock::new(IoDispatcher::new(allocator)),
|
io_dispatch: RwLock::new(IoDispatcher::new(allocator)),
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
waker,
|
waker,
|
||||||
metrics: IoDriverMetrics::default(),
|
metrics: IoDriverMetrics::default(),
|
||||||
}),
|
}),
|
||||||
@@ -164,6 +168,11 @@ impl Driver {
|
|||||||
match self.poll.poll(&mut events, max_wait) {
|
match self.poll.poll(&mut events, max_wait) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::InvalidInput => {
|
||||||
|
// In case of wasm32_wasi this error happens, when trying to poll without subscriptions
|
||||||
|
// just return from the park, as there would be nothing, which wakes us up.
|
||||||
|
}
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +282,7 @@ cfg_not_rt! {
|
|||||||
///
|
///
|
||||||
/// This function panics if there is no current reactor set, or if the `rt`
|
/// This function panics if there is no current reactor set, or if the `rt`
|
||||||
/// feature flag is not enabled.
|
/// feature flag is not enabled.
|
||||||
|
#[track_caller]
|
||||||
pub(super) fn current() -> Self {
|
pub(super) fn current() -> Self {
|
||||||
panic!("{}", crate::util::error::CONTEXT_MISSING_ERROR)
|
panic!("{}", crate::util::error::CONTEXT_MISSING_ERROR)
|
||||||
}
|
}
|
||||||
@@ -300,6 +310,7 @@ impl Handle {
|
|||||||
/// blocked in `turn`, then the next call to `turn` will not block and
|
/// blocked in `turn`, then the next call to `turn` will not block and
|
||||||
/// return immediately.
|
/// return immediately.
|
||||||
fn wakeup(&self) {
|
fn wakeup(&self) {
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
self.inner.waker.wake().expect("failed to wake I/O driver");
|
self.inner.waker.wake().expect("failed to wake I/O driver");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,14 @@ cfg_io_readiness! {
|
|||||||
_p: PhantomPinned,
|
_p: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_addr_of_methods! {
|
||||||
|
impl<> Waiter {
|
||||||
|
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||||
|
&self.pointers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Future returned by `readiness()`.
|
/// Future returned by `readiness()`.
|
||||||
struct Readiness<'a> {
|
struct Readiness<'a> {
|
||||||
scheduled_io: &'a ScheduledIo,
|
scheduled_io: &'a ScheduledIo,
|
||||||
@@ -399,8 +407,8 @@ cfg_io_readiness! {
|
|||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn pointers(mut target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||||
NonNull::from(&mut target.as_mut().pointers)
|
Waiter::addr_of_pointers(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -211,9 +211,11 @@ cfg_io_driver_impl! {
|
|||||||
pub use driver::{Interest, Ready};
|
pub use driver::{Interest, Ready};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_os = "wasi", allow(unused_imports))]
|
||||||
mod poll_evented;
|
mod poll_evented;
|
||||||
|
|
||||||
#[cfg(not(loom))]
|
#[cfg(not(loom))]
|
||||||
|
#[cfg_attr(target_os = "wasi", allow(unused_imports))]
|
||||||
pub(crate) use poll_evented::PollEvented;
|
pub(crate) use poll_evented::PollEvented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ impl<E: Source> PollEvented<E> {
|
|||||||
/// The runtime is usually set implicitly when this function is called
|
/// The runtime is usually set implicitly when this function is called
|
||||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||||
|
#[track_caller]
|
||||||
#[cfg_attr(feature = "signal", allow(unused))]
|
#[cfg_attr(feature = "signal", allow(unused))]
|
||||||
pub(crate) fn new(io: E) -> io::Result<Self> {
|
pub(crate) fn new(io: E) -> io::Result<Self> {
|
||||||
PollEvented::new_with_interest(io, Interest::READABLE | Interest::WRITABLE)
|
PollEvented::new_with_interest(io, Interest::READABLE | Interest::WRITABLE)
|
||||||
@@ -97,6 +98,7 @@ impl<E: Source> PollEvented<E> {
|
|||||||
/// a future driven by a tokio runtime, otherwise runtime can be set
|
/// a future driven by a tokio runtime, otherwise runtime can be set
|
||||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter)
|
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter)
|
||||||
/// function.
|
/// function.
|
||||||
|
#[track_caller]
|
||||||
#[cfg_attr(feature = "signal", allow(unused))]
|
#[cfg_attr(feature = "signal", allow(unused))]
|
||||||
pub(crate) fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> {
|
pub(crate) fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> {
|
||||||
Self::new_with_interest_and_handle(io, interest, Handle::current())
|
Self::new_with_interest_and_handle(io, interest, Handle::current())
|
||||||
@@ -134,7 +136,7 @@ impl<E: Source> PollEvented<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
feature! {
|
feature! {
|
||||||
#![any(feature = "net", feature = "process")]
|
#![any(feature = "net", all(unix, feature = "process"))]
|
||||||
|
|
||||||
use crate::io::ReadBuf;
|
use crate::io::ReadBuf;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|||||||
+20
-1
@@ -393,6 +393,20 @@ compile_error! {
|
|||||||
"Tokio requires the platform pointer width to be 32, 64, or 128 bits"
|
"Tokio requires the platform pointer width to be 32, 64, or 128 bits"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
not(tokio_unstable),
|
||||||
|
target_arch = "wasm32",
|
||||||
|
any(
|
||||||
|
feature = "fs",
|
||||||
|
feature = "io-std",
|
||||||
|
feature = "net",
|
||||||
|
feature = "process",
|
||||||
|
feature = "rt-multi-thread",
|
||||||
|
feature = "signal"
|
||||||
|
)
|
||||||
|
))]
|
||||||
|
compile_error!("Only features sync,macros,io-util,rt are supported on wasm.");
|
||||||
|
|
||||||
// Includes re-exports used by macros.
|
// Includes re-exports used by macros.
|
||||||
//
|
//
|
||||||
// This module is not intended to be part of the public API. In general, any
|
// This module is not intended to be part of the public API. In general, any
|
||||||
@@ -417,7 +431,12 @@ cfg_process! {
|
|||||||
pub mod process;
|
pub mod process;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "net", feature = "fs", feature = "io-std"))]
|
#[cfg(any(
|
||||||
|
feature = "fs",
|
||||||
|
feature = "io-std",
|
||||||
|
feature = "net",
|
||||||
|
all(windows, feature = "process"),
|
||||||
|
))]
|
||||||
mod blocking;
|
mod blocking;
|
||||||
|
|
||||||
cfg_rt! {
|
cfg_rt! {
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
//! This module defines a macro that lets you go from a raw pointer to a struct
|
||||||
|
//! to a raw pointer to a field of the struct.
|
||||||
|
|
||||||
|
#[cfg(not(tokio_no_addr_of))]
|
||||||
|
macro_rules! generate_addr_of_methods {
|
||||||
|
(
|
||||||
|
impl<$($gen:ident)*> $struct_name:ty {$(
|
||||||
|
$(#[$attrs:meta])*
|
||||||
|
$vis:vis unsafe fn $fn_name:ident(self: NonNull<Self>) -> NonNull<$field_type:ty> {
|
||||||
|
&self$(.$field_name:tt)+
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
) => {
|
||||||
|
impl<$($gen)*> $struct_name {$(
|
||||||
|
$(#[$attrs])*
|
||||||
|
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
|
||||||
|
let me = me.as_ptr();
|
||||||
|
let field = ::std::ptr::addr_of_mut!((*me) $(.$field_name)+ );
|
||||||
|
::core::ptr::NonNull::new_unchecked(field)
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `addr_of_mut!` macro is only available for MSRV at least 1.51.0. This
|
||||||
|
// version of the macro uses a workaround for older versions of rustc.
|
||||||
|
#[cfg(tokio_no_addr_of)]
|
||||||
|
macro_rules! generate_addr_of_methods {
|
||||||
|
(
|
||||||
|
impl<$($gen:ident)*> $struct_name:ty {$(
|
||||||
|
$(#[$attrs:meta])*
|
||||||
|
$vis:vis unsafe fn $fn_name:ident(self: NonNull<Self>) -> NonNull<$field_type:ty> {
|
||||||
|
&self$(.$field_name:tt)+
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
) => {
|
||||||
|
impl<$($gen)*> $struct_name {$(
|
||||||
|
$(#[$attrs])*
|
||||||
|
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
|
||||||
|
let me = me.as_ptr();
|
||||||
|
let me_u8 = me as *mut u8;
|
||||||
|
|
||||||
|
let field_offset = {
|
||||||
|
let me_ref = &*me;
|
||||||
|
let field_ref_u8 = (&me_ref $(.$field_name)+ ) as *const $field_type as *const u8;
|
||||||
|
field_ref_u8.offset_from(me_u8)
|
||||||
|
};
|
||||||
|
|
||||||
|
::core::ptr::NonNull::new_unchecked(me_u8.offset(field_offset).cast())
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
};
|
||||||
|
}
|
||||||
+35
-8
@@ -61,6 +61,7 @@ macro_rules! cfg_fs {
|
|||||||
($($item:item)*) => {
|
($($item:item)*) => {
|
||||||
$(
|
$(
|
||||||
#[cfg(feature = "fs")]
|
#[cfg(feature = "fs")]
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
|
||||||
$item
|
$item
|
||||||
)*
|
)*
|
||||||
@@ -69,7 +70,11 @@ macro_rules! cfg_fs {
|
|||||||
|
|
||||||
macro_rules! cfg_io_blocking {
|
macro_rules! cfg_io_blocking {
|
||||||
($($item:item)*) => {
|
($($item:item)*) => {
|
||||||
$( #[cfg(any(feature = "io-std", feature = "fs"))] $item )*
|
$( #[cfg(any(
|
||||||
|
feature = "io-std",
|
||||||
|
feature = "fs",
|
||||||
|
all(windows, feature = "process"),
|
||||||
|
))] $item )*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,12 +83,12 @@ macro_rules! cfg_io_driver {
|
|||||||
$(
|
$(
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "net",
|
feature = "net",
|
||||||
feature = "process",
|
all(unix, feature = "process"),
|
||||||
all(unix, feature = "signal"),
|
all(unix, feature = "signal"),
|
||||||
))]
|
))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(
|
#[cfg_attr(docsrs, doc(cfg(any(
|
||||||
feature = "net",
|
feature = "net",
|
||||||
feature = "process",
|
all(unix, feature = "process"),
|
||||||
all(unix, feature = "signal"),
|
all(unix, feature = "signal"),
|
||||||
))))]
|
))))]
|
||||||
$item
|
$item
|
||||||
@@ -96,7 +101,7 @@ macro_rules! cfg_io_driver_impl {
|
|||||||
$(
|
$(
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "net",
|
feature = "net",
|
||||||
feature = "process",
|
all(unix, feature = "process"),
|
||||||
all(unix, feature = "signal"),
|
all(unix, feature = "signal"),
|
||||||
))]
|
))]
|
||||||
$item
|
$item
|
||||||
@@ -109,7 +114,7 @@ macro_rules! cfg_not_io_driver {
|
|||||||
$(
|
$(
|
||||||
#[cfg(not(any(
|
#[cfg(not(any(
|
||||||
feature = "net",
|
feature = "net",
|
||||||
feature = "process",
|
all(unix, feature = "process"),
|
||||||
all(unix, feature = "signal"),
|
all(unix, feature = "signal"),
|
||||||
)))]
|
)))]
|
||||||
$item
|
$item
|
||||||
@@ -247,6 +252,7 @@ macro_rules! cfg_process {
|
|||||||
#[cfg(feature = "process")]
|
#[cfg(feature = "process")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
|
||||||
#[cfg(not(loom))]
|
#[cfg(not(loom))]
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
$item
|
$item
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
@@ -275,6 +281,7 @@ macro_rules! cfg_signal {
|
|||||||
#[cfg(feature = "signal")]
|
#[cfg(feature = "signal")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
|
||||||
#[cfg(not(loom))]
|
#[cfg(not(loom))]
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
$item
|
$item
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
@@ -334,7 +341,7 @@ macro_rules! cfg_not_rt {
|
|||||||
macro_rules! cfg_rt_multi_thread {
|
macro_rules! cfg_rt_multi_thread {
|
||||||
($($item:item)*) => {
|
($($item:item)*) => {
|
||||||
$(
|
$(
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
|
||||||
$item
|
$item
|
||||||
)*
|
)*
|
||||||
@@ -451,7 +458,8 @@ macro_rules! cfg_has_atomic_u64 {
|
|||||||
target_arch = "arm",
|
target_arch = "arm",
|
||||||
target_arch = "mips",
|
target_arch = "mips",
|
||||||
target_arch = "powerpc",
|
target_arch = "powerpc",
|
||||||
target_arch = "riscv32"
|
target_arch = "riscv32",
|
||||||
|
target_arch = "wasm32"
|
||||||
)))]
|
)))]
|
||||||
$item
|
$item
|
||||||
)*
|
)*
|
||||||
@@ -465,9 +473,28 @@ macro_rules! cfg_not_has_atomic_u64 {
|
|||||||
target_arch = "arm",
|
target_arch = "arm",
|
||||||
target_arch = "mips",
|
target_arch = "mips",
|
||||||
target_arch = "powerpc",
|
target_arch = "powerpc",
|
||||||
target_arch = "riscv32"
|
target_arch = "riscv32",
|
||||||
|
target_arch = "wasm32"
|
||||||
))]
|
))]
|
||||||
$item
|
$item
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! cfg_not_wasi {
|
||||||
|
($($item:item)*) => {
|
||||||
|
$(
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
|
$item
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cfg_is_wasm_not_wasi {
|
||||||
|
($($item:item)*) => {
|
||||||
|
$(
|
||||||
|
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||||
|
$item
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ mod ready;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod thread_local;
|
mod thread_local;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod addr_of;
|
||||||
|
|
||||||
cfg_trace! {
|
cfg_trace! {
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod trace;
|
mod trace;
|
||||||
|
|||||||
@@ -23,8 +23,10 @@
|
|||||||
//! [`UnixDatagram`]: UnixDatagram
|
//! [`UnixDatagram`]: UnixDatagram
|
||||||
|
|
||||||
mod addr;
|
mod addr;
|
||||||
#[cfg(feature = "net")]
|
cfg_not_wasi! {
|
||||||
pub(crate) use addr::to_socket_addrs;
|
#[cfg(feature = "net")]
|
||||||
|
pub(crate) use addr::to_socket_addrs;
|
||||||
|
}
|
||||||
pub use addr::ToSocketAddrs;
|
pub use addr::ToSocketAddrs;
|
||||||
|
|
||||||
cfg_net! {
|
cfg_net! {
|
||||||
@@ -33,11 +35,13 @@ cfg_net! {
|
|||||||
|
|
||||||
pub mod tcp;
|
pub mod tcp;
|
||||||
pub use tcp::listener::TcpListener;
|
pub use tcp::listener::TcpListener;
|
||||||
pub use tcp::socket::TcpSocket;
|
|
||||||
pub use tcp::stream::TcpStream;
|
pub use tcp::stream::TcpStream;
|
||||||
|
cfg_not_wasi! {
|
||||||
|
pub use tcp::socket::TcpSocket;
|
||||||
|
|
||||||
mod udp;
|
mod udp;
|
||||||
pub use udp::UdpSocket;
|
pub use udp::UdpSocket;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_net_unix! {
|
cfg_net_unix! {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
use crate::io::{Interest, PollEvented};
|
use crate::io::{Interest, PollEvented};
|
||||||
use crate::net::tcp::TcpStream;
|
use crate::net::tcp::TcpStream;
|
||||||
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
|
||||||
|
cfg_not_wasi! {
|
||||||
|
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
||||||
|
}
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -55,68 +58,70 @@ cfg_net! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TcpListener {
|
impl TcpListener {
|
||||||
/// Creates a new TcpListener, which will be bound to the specified address.
|
cfg_not_wasi! {
|
||||||
///
|
/// Creates a new TcpListener, which will be bound to the specified address.
|
||||||
/// The returned listener is ready for accepting connections.
|
///
|
||||||
///
|
/// The returned listener is ready for accepting connections.
|
||||||
/// Binding with a port number of 0 will request that the OS assigns a port
|
///
|
||||||
/// to this listener. The port allocated can be queried via the `local_addr`
|
/// Binding with a port number of 0 will request that the OS assigns a port
|
||||||
/// method.
|
/// to this listener. The port allocated can be queried via the `local_addr`
|
||||||
///
|
/// method.
|
||||||
/// The address type can be any implementor of the [`ToSocketAddrs`] trait.
|
///
|
||||||
/// If `addr` yields multiple addresses, bind will be attempted with each of
|
/// The address type can be any implementor of the [`ToSocketAddrs`] trait.
|
||||||
/// the addresses until one succeeds and returns the listener. If none of
|
/// If `addr` yields multiple addresses, bind will be attempted with each of
|
||||||
/// the addresses succeed in creating a listener, the error returned from
|
/// the addresses until one succeeds and returns the listener. If none of
|
||||||
/// the last attempt (the last address) is returned.
|
/// the addresses succeed in creating a listener, the error returned from
|
||||||
///
|
/// the last attempt (the last address) is returned.
|
||||||
/// This function sets the `SO_REUSEADDR` option on the socket.
|
///
|
||||||
///
|
/// This function sets the `SO_REUSEADDR` option on the socket.
|
||||||
/// To configure the socket before binding, you can use the [`TcpSocket`]
|
///
|
||||||
/// type.
|
/// To configure the socket before binding, you can use the [`TcpSocket`]
|
||||||
///
|
/// type.
|
||||||
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
|
///
|
||||||
/// [`TcpSocket`]: struct@crate::net::TcpSocket
|
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
|
||||||
///
|
/// [`TcpSocket`]: struct@crate::net::TcpSocket
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// ```no_run
|
///
|
||||||
/// use tokio::net::TcpListener;
|
/// ```no_run
|
||||||
///
|
/// use tokio::net::TcpListener;
|
||||||
/// use std::io;
|
///
|
||||||
///
|
/// use std::io;
|
||||||
/// #[tokio::main]
|
///
|
||||||
/// async fn main() -> io::Result<()> {
|
/// #[tokio::main]
|
||||||
/// let listener = TcpListener::bind("127.0.0.1:2345").await?;
|
/// async fn main() -> io::Result<()> {
|
||||||
///
|
/// let listener = TcpListener::bind("127.0.0.1:2345").await?;
|
||||||
/// // use the listener
|
///
|
||||||
///
|
/// // use the listener
|
||||||
/// # let _ = listener;
|
///
|
||||||
/// Ok(())
|
/// # let _ = listener;
|
||||||
/// }
|
/// Ok(())
|
||||||
/// ```
|
/// }
|
||||||
pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
|
/// ```
|
||||||
let addrs = to_socket_addrs(addr).await?;
|
pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
|
||||||
|
let addrs = to_socket_addrs(addr).await?;
|
||||||
|
|
||||||
let mut last_err = None;
|
let mut last_err = None;
|
||||||
|
|
||||||
for addr in addrs {
|
for addr in addrs {
|
||||||
match TcpListener::bind_addr(addr) {
|
match TcpListener::bind_addr(addr) {
|
||||||
Ok(listener) => return Ok(listener),
|
Ok(listener) => return Ok(listener),
|
||||||
Err(e) => last_err = Some(e),
|
Err(e) => last_err = Some(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Err(last_err.unwrap_or_else(|| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"could not resolve to any address",
|
||||||
|
)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(last_err.unwrap_or_else(|| {
|
fn bind_addr(addr: SocketAddr) -> io::Result<TcpListener> {
|
||||||
io::Error::new(
|
let listener = mio::net::TcpListener::bind(addr)?;
|
||||||
io::ErrorKind::InvalidInput,
|
TcpListener::new(listener)
|
||||||
"could not resolve to any address",
|
}
|
||||||
)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bind_addr(addr: SocketAddr) -> io::Result<TcpListener> {
|
|
||||||
let listener = mio::net::TcpListener::bind(addr)?;
|
|
||||||
TcpListener::new(listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accepts a new incoming connection from this listener.
|
/// Accepts a new incoming connection from this listener.
|
||||||
@@ -216,11 +221,13 @@ impl TcpListener {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This function panics if thread-local runtime is not set.
|
/// This function panics if it is not called from within a runtime with
|
||||||
|
/// IO enabled.
|
||||||
///
|
///
|
||||||
/// The runtime is usually set implicitly when this function is called
|
/// The runtime is usually set implicitly when this function is called
|
||||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||||
|
#[track_caller]
|
||||||
pub fn from_std(listener: net::TcpListener) -> io::Result<TcpListener> {
|
pub fn from_std(listener: net::TcpListener) -> io::Result<TcpListener> {
|
||||||
let io = mio::net::TcpListener::from_std(listener);
|
let io = mio::net::TcpListener::from_std(listener);
|
||||||
let io = PollEvented::new(io)?;
|
let io = PollEvented::new(io)?;
|
||||||
@@ -267,11 +274,22 @@ impl TcpListener {
|
|||||||
.map(|io| io.into_raw_socket())
|
.map(|io| io.into_raw_socket())
|
||||||
.map(|raw_socket| unsafe { std::net::TcpListener::from_raw_socket(raw_socket) })
|
.map(|raw_socket| unsafe { std::net::TcpListener::from_raw_socket(raw_socket) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
{
|
||||||
|
use std::os::wasi::io::{FromRawFd, IntoRawFd};
|
||||||
|
self.io
|
||||||
|
.into_inner()
|
||||||
|
.map(|io| io.into_raw_fd())
|
||||||
|
.map(|raw_fd| unsafe { std::net::TcpListener::from_raw_fd(raw_fd) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(listener: mio::net::TcpListener) -> io::Result<TcpListener> {
|
cfg_not_wasi! {
|
||||||
let io = PollEvented::new(listener)?;
|
pub(crate) fn new(listener: mio::net::TcpListener) -> io::Result<TcpListener> {
|
||||||
Ok(TcpListener { io })
|
let io = PollEvented::new(listener)?;
|
||||||
|
Ok(TcpListener { io })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the local address that this listener is bound to.
|
/// Returns the local address that this listener is bound to.
|
||||||
@@ -384,6 +402,20 @@ mod sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg_unstable! {
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
mod sys {
|
||||||
|
use super::TcpListener;
|
||||||
|
use std::os::wasi::prelude::*;
|
||||||
|
|
||||||
|
impl AsRawFd for TcpListener {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.io.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
mod sys {
|
mod sys {
|
||||||
use super::TcpListener;
|
use super::TcpListener;
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
pub(crate) mod listener;
|
pub(crate) mod listener;
|
||||||
|
|
||||||
pub(crate) mod socket;
|
cfg_not_wasi! {
|
||||||
|
pub(crate) mod socket;
|
||||||
|
}
|
||||||
|
|
||||||
mod split;
|
mod split;
|
||||||
pub use split::{ReadHalf, WriteHalf};
|
pub use split::{ReadHalf, WriteHalf};
|
||||||
|
|||||||
+152
-122
@@ -1,8 +1,12 @@
|
|||||||
use crate::future::poll_fn;
|
cfg_not_wasi! {
|
||||||
|
use crate::future::poll_fn;
|
||||||
|
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
||||||
|
use std::time::Duration;
|
||||||
|
}
|
||||||
|
|
||||||
use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
|
use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
|
||||||
use crate::net::tcp::split::{split, ReadHalf, WriteHalf};
|
use crate::net::tcp::split::{split, ReadHalf, WriteHalf};
|
||||||
use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
|
use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
|
||||||
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -10,7 +14,6 @@ use std::io;
|
|||||||
use std::net::{Shutdown, SocketAddr};
|
use std::net::{Shutdown, SocketAddr};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
cfg_io_util! {
|
cfg_io_util! {
|
||||||
use bytes::BufMut;
|
use bytes::BufMut;
|
||||||
@@ -70,86 +73,88 @@ cfg_net! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TcpStream {
|
impl TcpStream {
|
||||||
/// Opens a TCP connection to a remote host.
|
cfg_not_wasi! {
|
||||||
///
|
/// Opens a TCP connection to a remote host.
|
||||||
/// `addr` is an address of the remote host. Anything which implements the
|
///
|
||||||
/// [`ToSocketAddrs`] trait can be supplied as the address. If `addr`
|
/// `addr` is an address of the remote host. Anything which implements the
|
||||||
/// yields multiple addresses, connect will be attempted with each of the
|
/// [`ToSocketAddrs`] trait can be supplied as the address. If `addr`
|
||||||
/// addresses until a connection is successful. If none of the addresses
|
/// yields multiple addresses, connect will be attempted with each of the
|
||||||
/// result in a successful connection, the error returned from the last
|
/// addresses until a connection is successful. If none of the addresses
|
||||||
/// connection attempt (the last address) is returned.
|
/// result in a successful connection, the error returned from the last
|
||||||
///
|
/// connection attempt (the last address) is returned.
|
||||||
/// To configure the socket before connecting, you can use the [`TcpSocket`]
|
///
|
||||||
/// type.
|
/// To configure the socket before connecting, you can use the [`TcpSocket`]
|
||||||
///
|
/// type.
|
||||||
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
|
///
|
||||||
/// [`TcpSocket`]: struct@crate::net::TcpSocket
|
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
|
||||||
///
|
/// [`TcpSocket`]: struct@crate::net::TcpSocket
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// ```no_run
|
///
|
||||||
/// use tokio::net::TcpStream;
|
/// ```no_run
|
||||||
/// use tokio::io::AsyncWriteExt;
|
/// use tokio::net::TcpStream;
|
||||||
/// use std::error::Error;
|
/// use tokio::io::AsyncWriteExt;
|
||||||
///
|
/// use std::error::Error;
|
||||||
/// #[tokio::main]
|
///
|
||||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
/// #[tokio::main]
|
||||||
/// // Connect to a peer
|
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
/// let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
|
/// // Connect to a peer
|
||||||
///
|
/// let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||||
/// // Write some data.
|
///
|
||||||
/// stream.write_all(b"hello world!").await?;
|
/// // Write some data.
|
||||||
///
|
/// stream.write_all(b"hello world!").await?;
|
||||||
/// Ok(())
|
///
|
||||||
/// }
|
/// Ok(())
|
||||||
/// ```
|
/// }
|
||||||
///
|
/// ```
|
||||||
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
|
///
|
||||||
///
|
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
|
||||||
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
|
///
|
||||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
|
||||||
pub async fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
|
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||||
let addrs = to_socket_addrs(addr).await?;
|
pub async fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
|
||||||
|
let addrs = to_socket_addrs(addr).await?;
|
||||||
|
|
||||||
let mut last_err = None;
|
let mut last_err = None;
|
||||||
|
|
||||||
for addr in addrs {
|
for addr in addrs {
|
||||||
match TcpStream::connect_addr(addr).await {
|
match TcpStream::connect_addr(addr).await {
|
||||||
Ok(stream) => return Ok(stream),
|
Ok(stream) => return Ok(stream),
|
||||||
Err(e) => last_err = Some(e),
|
Err(e) => last_err = Some(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Err(last_err.unwrap_or_else(|| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"could not resolve to any address",
|
||||||
|
)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(last_err.unwrap_or_else(|| {
|
/// Establishes a connection to the specified `addr`.
|
||||||
io::Error::new(
|
async fn connect_addr(addr: SocketAddr) -> io::Result<TcpStream> {
|
||||||
io::ErrorKind::InvalidInput,
|
let sys = mio::net::TcpStream::connect(addr)?;
|
||||||
"could not resolve to any address",
|
TcpStream::connect_mio(sys).await
|
||||||
)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Establishes a connection to the specified `addr`.
|
|
||||||
async fn connect_addr(addr: SocketAddr) -> io::Result<TcpStream> {
|
|
||||||
let sys = mio::net::TcpStream::connect(addr)?;
|
|
||||||
TcpStream::connect_mio(sys).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn connect_mio(sys: mio::net::TcpStream) -> io::Result<TcpStream> {
|
|
||||||
let stream = TcpStream::new(sys)?;
|
|
||||||
|
|
||||||
// Once we've connected, wait for the stream to be writable as
|
|
||||||
// that's when the actual connection has been initiated. Once we're
|
|
||||||
// writable we check for `take_socket_error` to see if the connect
|
|
||||||
// actually hit an error or not.
|
|
||||||
//
|
|
||||||
// If all that succeeded then we ship everything on up.
|
|
||||||
poll_fn(|cx| stream.io.registration().poll_write_ready(cx)).await?;
|
|
||||||
|
|
||||||
if let Some(e) = stream.io.take_error()? {
|
|
||||||
return Err(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(stream)
|
pub(crate) async fn connect_mio(sys: mio::net::TcpStream) -> io::Result<TcpStream> {
|
||||||
|
let stream = TcpStream::new(sys)?;
|
||||||
|
|
||||||
|
// Once we've connected, wait for the stream to be writable as
|
||||||
|
// that's when the actual connection has been initiated. Once we're
|
||||||
|
// writable we check for `take_socket_error` to see if the connect
|
||||||
|
// actually hit an error or not.
|
||||||
|
//
|
||||||
|
// If all that succeeded then we ship everything on up.
|
||||||
|
poll_fn(|cx| stream.io.registration().poll_write_ready(cx)).await?;
|
||||||
|
|
||||||
|
if let Some(e) = stream.io.take_error()? {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(stream)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(connected: mio::net::TcpStream) -> io::Result<TcpStream> {
|
pub(crate) fn new(connected: mio::net::TcpStream) -> io::Result<TcpStream> {
|
||||||
@@ -181,11 +186,13 @@ impl TcpStream {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This function panics if thread-local runtime is not set.
|
/// This function panics if it is not called from within a runtime with
|
||||||
|
/// IO enabled.
|
||||||
///
|
///
|
||||||
/// The runtime is usually set implicitly when this function is called
|
/// The runtime is usually set implicitly when this function is called
|
||||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||||
|
#[track_caller]
|
||||||
pub fn from_std(stream: std::net::TcpStream) -> io::Result<TcpStream> {
|
pub fn from_std(stream: std::net::TcpStream) -> io::Result<TcpStream> {
|
||||||
let io = mio::net::TcpStream::from_std(stream);
|
let io = mio::net::TcpStream::from_std(stream);
|
||||||
let io = PollEvented::new(io)?;
|
let io = PollEvented::new(io)?;
|
||||||
@@ -244,6 +251,15 @@ impl TcpStream {
|
|||||||
.map(|io| io.into_raw_socket())
|
.map(|io| io.into_raw_socket())
|
||||||
.map(|raw_socket| unsafe { std::net::TcpStream::from_raw_socket(raw_socket) })
|
.map(|raw_socket| unsafe { std::net::TcpStream::from_raw_socket(raw_socket) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
{
|
||||||
|
use std::os::wasi::io::{FromRawFd, IntoRawFd};
|
||||||
|
self.io
|
||||||
|
.into_inner()
|
||||||
|
.map(|io| io.into_raw_fd())
|
||||||
|
.map(|raw_fd| unsafe { std::net::TcpStream::from_raw_fd(raw_fd) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the local address that this stream is bound to.
|
/// Returns the local address that this stream is bound to.
|
||||||
@@ -1077,52 +1093,54 @@ impl TcpStream {
|
|||||||
self.io.set_nodelay(nodelay)
|
self.io.set_nodelay(nodelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the linger duration for this socket by getting the `SO_LINGER`
|
cfg_not_wasi! {
|
||||||
/// option.
|
/// Reads the linger duration for this socket by getting the `SO_LINGER`
|
||||||
///
|
/// option.
|
||||||
/// For more information about this option, see [`set_linger`].
|
///
|
||||||
///
|
/// For more information about this option, see [`set_linger`].
|
||||||
/// [`set_linger`]: TcpStream::set_linger
|
///
|
||||||
///
|
/// [`set_linger`]: TcpStream::set_linger
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// ```no_run
|
///
|
||||||
/// use tokio::net::TcpStream;
|
/// ```no_run
|
||||||
///
|
/// use tokio::net::TcpStream;
|
||||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
///
|
||||||
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
|
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
///
|
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||||
/// println!("{:?}", stream.linger()?);
|
///
|
||||||
/// # Ok(())
|
/// println!("{:?}", stream.linger()?);
|
||||||
/// # }
|
/// # Ok(())
|
||||||
/// ```
|
/// # }
|
||||||
pub fn linger(&self) -> io::Result<Option<Duration>> {
|
/// ```
|
||||||
socket2::SockRef::from(self).linger()
|
pub fn linger(&self) -> io::Result<Option<Duration>> {
|
||||||
}
|
socket2::SockRef::from(self).linger()
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the linger duration of this socket by setting the SO_LINGER option.
|
/// Sets the linger duration of this socket by setting the SO_LINGER option.
|
||||||
///
|
///
|
||||||
/// This option controls the action taken when a stream has unsent messages and the stream is
|
/// This option controls the action taken when a stream has unsent messages and the stream is
|
||||||
/// closed. If SO_LINGER is set, the system shall block the process until it can transmit the
|
/// closed. If SO_LINGER is set, the system shall block the process until it can transmit the
|
||||||
/// data or until the time expires.
|
/// data or until the time expires.
|
||||||
///
|
///
|
||||||
/// If SO_LINGER is not specified, and the stream is closed, the system handles the call in a
|
/// If SO_LINGER is not specified, and the stream is closed, the system handles the call in a
|
||||||
/// way that allows the process to continue as quickly as possible.
|
/// way that allows the process to continue as quickly as possible.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use tokio::net::TcpStream;
|
/// use tokio::net::TcpStream;
|
||||||
///
|
///
|
||||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
|
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||||
///
|
///
|
||||||
/// stream.set_linger(None)?;
|
/// stream.set_linger(None)?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
|
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
|
||||||
socket2::SockRef::from(self).set_linger(dur)
|
socket2::SockRef::from(self).set_linger(dur)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the value of the `IP_TTL` option for this socket.
|
/// Gets the value of the `IP_TTL` option for this socket.
|
||||||
@@ -1315,3 +1333,15 @@ mod sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(tokio_unstable, target_os = "wasi"))]
|
||||||
|
mod sys {
|
||||||
|
use super::TcpStream;
|
||||||
|
use std::os::wasi::prelude::*;
|
||||||
|
|
||||||
|
impl AsRawFd for TcpStream {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.io.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ impl UdpSocket {
|
|||||||
UdpSocket::new(sys)
|
UdpSocket::new(sys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn new(socket: mio::net::UdpSocket) -> io::Result<UdpSocket> {
|
fn new(socket: mio::net::UdpSocket) -> io::Result<UdpSocket> {
|
||||||
let io = PollEvented::new(socket)?;
|
let io = PollEvented::new(socket)?;
|
||||||
Ok(UdpSocket { io })
|
Ok(UdpSocket { io })
|
||||||
@@ -210,6 +211,7 @@ impl UdpSocket {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[track_caller]
|
||||||
pub fn from_std(socket: net::UdpSocket) -> io::Result<UdpSocket> {
|
pub fn from_std(socket: net::UdpSocket) -> io::Result<UdpSocket> {
|
||||||
let io = mio::net::UdpSocket::from_std(socket);
|
let io = mio::net::UdpSocket::from_std(socket);
|
||||||
UdpSocket::new(io)
|
UdpSocket::new(io)
|
||||||
|
|||||||
@@ -430,7 +430,8 @@ impl UnixDatagram {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This function panics if thread-local runtime is not set.
|
/// This function panics if it is not called from within a runtime with
|
||||||
|
/// IO enabled.
|
||||||
///
|
///
|
||||||
/// The runtime is usually set implicitly when this function is called
|
/// The runtime is usually set implicitly when this function is called
|
||||||
/// from a future driven by a Tokio runtime, otherwise runtime can be set
|
/// from a future driven by a Tokio runtime, otherwise runtime can be set
|
||||||
@@ -457,6 +458,7 @@ impl UnixDatagram {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[track_caller]
|
||||||
pub fn from_std(datagram: net::UnixDatagram) -> io::Result<UnixDatagram> {
|
pub fn from_std(datagram: net::UnixDatagram) -> io::Result<UnixDatagram> {
|
||||||
let socket = mio::net::UnixDatagram::from_std(datagram);
|
let socket = mio::net::UnixDatagram::from_std(datagram);
|
||||||
let io = PollEvented::new(socket)?;
|
let io = PollEvented::new(socket)?;
|
||||||
|
|||||||
@@ -54,11 +54,13 @@ impl UnixListener {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This function panics if thread-local runtime is not set.
|
/// This function panics if it is not called from within a runtime with
|
||||||
|
/// IO enabled.
|
||||||
///
|
///
|
||||||
/// The runtime is usually set implicitly when this function is called
|
/// The runtime is usually set implicitly when this function is called
|
||||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||||
|
#[track_caller]
|
||||||
pub fn bind<P>(path: P) -> io::Result<UnixListener>
|
pub fn bind<P>(path: P) -> io::Result<UnixListener>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
@@ -77,11 +79,13 @@ impl UnixListener {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This function panics if thread-local runtime is not set.
|
/// This function panics if it is not called from within a runtime with
|
||||||
|
/// IO enabled.
|
||||||
///
|
///
|
||||||
/// The runtime is usually set implicitly when this function is called
|
/// The runtime is usually set implicitly when this function is called
|
||||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||||
|
#[track_caller]
|
||||||
pub fn from_std(listener: net::UnixListener) -> io::Result<UnixListener> {
|
pub fn from_std(listener: net::UnixListener) -> io::Result<UnixListener> {
|
||||||
let listener = mio::net::UnixListener::from_std(listener);
|
let listener = mio::net::UnixListener::from_std(listener);
|
||||||
let io = PollEvented::new(listener)?;
|
let io = PollEvented::new(listener)?;
|
||||||
|
|||||||
@@ -699,11 +699,13 @@ impl UnixStream {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This function panics if thread-local runtime is not set.
|
/// This function panics if it is not called from within a runtime with
|
||||||
|
/// IO enabled.
|
||||||
///
|
///
|
||||||
/// The runtime is usually set implicitly when this function is called
|
/// The runtime is usually set implicitly when this function is called
|
||||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||||
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||||
|
#[track_caller]
|
||||||
pub fn from_std(stream: net::UnixStream) -> io::Result<UnixStream> {
|
pub fn from_std(stream: net::UnixStream) -> io::Result<UnixStream> {
|
||||||
let stream = mio::net::UnixStream::from_std(stream);
|
let stream = mio::net::UnixStream::from_std(stream);
|
||||||
let io = PollEvented::new(stream)?;
|
let io = PollEvented::new(stream)?;
|
||||||
|
|||||||
@@ -1955,6 +1955,106 @@ impl ServerOptions {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Requests permission to modify the pipe's discretionary access control list.
|
||||||
|
///
|
||||||
|
/// This corresponds to setting [`WRITE_DAC`] in dwOpenMode.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::{io, os::windows::prelude::AsRawHandle, ptr};
|
||||||
|
//
|
||||||
|
/// use tokio::net::windows::named_pipe::ServerOptions;
|
||||||
|
/// use winapi::{
|
||||||
|
/// shared::winerror::ERROR_SUCCESS,
|
||||||
|
/// um::{accctrl::SE_KERNEL_OBJECT, aclapi::SetSecurityInfo, winnt::DACL_SECURITY_INFORMATION},
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// const PIPE_NAME: &str = r"\\.\pipe\write_dac_pipe";
|
||||||
|
///
|
||||||
|
/// # #[tokio::main] async fn main() -> io::Result<()> {
|
||||||
|
/// let mut pipe_template = ServerOptions::new();
|
||||||
|
/// pipe_template.write_dac(true);
|
||||||
|
/// let pipe = pipe_template.create(PIPE_NAME)?;
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// assert_eq!(
|
||||||
|
/// ERROR_SUCCESS,
|
||||||
|
/// SetSecurityInfo(
|
||||||
|
/// pipe.as_raw_handle(),
|
||||||
|
/// SE_KERNEL_OBJECT,
|
||||||
|
/// DACL_SECURITY_INFORMATION,
|
||||||
|
/// ptr::null_mut(),
|
||||||
|
/// ptr::null_mut(),
|
||||||
|
/// ptr::null_mut(),
|
||||||
|
/// ptr::null_mut(),
|
||||||
|
/// )
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # Ok(()) }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::{io, os::windows::prelude::AsRawHandle, ptr};
|
||||||
|
//
|
||||||
|
/// use tokio::net::windows::named_pipe::ServerOptions;
|
||||||
|
/// use winapi::{
|
||||||
|
/// shared::winerror::ERROR_ACCESS_DENIED,
|
||||||
|
/// um::{accctrl::SE_KERNEL_OBJECT, aclapi::SetSecurityInfo, winnt::DACL_SECURITY_INFORMATION},
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// const PIPE_NAME: &str = r"\\.\pipe\write_dac_pipe_fail";
|
||||||
|
///
|
||||||
|
/// # #[tokio::main] async fn main() -> io::Result<()> {
|
||||||
|
/// let mut pipe_template = ServerOptions::new();
|
||||||
|
/// pipe_template.write_dac(false);
|
||||||
|
/// let pipe = pipe_template.create(PIPE_NAME)?;
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// assert_eq!(
|
||||||
|
/// ERROR_ACCESS_DENIED,
|
||||||
|
/// SetSecurityInfo(
|
||||||
|
/// pipe.as_raw_handle(),
|
||||||
|
/// SE_KERNEL_OBJECT,
|
||||||
|
/// DACL_SECURITY_INFORMATION,
|
||||||
|
/// ptr::null_mut(),
|
||||||
|
/// ptr::null_mut(),
|
||||||
|
/// ptr::null_mut(),
|
||||||
|
/// ptr::null_mut(),
|
||||||
|
/// )
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # Ok(()) }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`WRITE_DAC`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
||||||
|
pub fn write_dac(&mut self, requested: bool) -> &mut Self {
|
||||||
|
bool_flag!(self.open_mode, requested, winnt::WRITE_DAC);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests permission to modify the pipe's owner.
|
||||||
|
///
|
||||||
|
/// This corresponds to setting [`WRITE_OWNER`] in dwOpenMode.
|
||||||
|
///
|
||||||
|
/// [`WRITE_OWNER`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
||||||
|
pub fn write_owner(&mut self, requested: bool) -> &mut Self {
|
||||||
|
bool_flag!(self.open_mode, requested, winnt::WRITE_OWNER);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests permission to modify the pipe's system access control list.
|
||||||
|
///
|
||||||
|
/// This corresponds to setting [`ACCESS_SYSTEM_SECURITY`] in dwOpenMode.
|
||||||
|
///
|
||||||
|
/// [`ACCESS_SYSTEM_SECURITY`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
||||||
|
pub fn access_system_security(&mut self, requested: bool) -> &mut Self {
|
||||||
|
bool_flag!(self.open_mode, requested, winnt::ACCESS_SYSTEM_SECURITY);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Indicates whether this server can accept remote clients or not. Remote
|
/// Indicates whether this server can accept remote clients or not. Remote
|
||||||
/// clients are disabled by default.
|
/// clients are disabled by default.
|
||||||
///
|
///
|
||||||
@@ -2020,6 +2120,7 @@ impl ServerOptions {
|
|||||||
/// let builder = ServerOptions::new().max_instances(255);
|
/// let builder = ServerOptions::new().max_instances(255);
|
||||||
/// # Ok(()) }
|
/// # Ok(()) }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[track_caller]
|
||||||
pub fn max_instances(&mut self, instances: usize) -> &mut Self {
|
pub fn max_instances(&mut self, instances: usize) -> &mut Self {
|
||||||
assert!(instances < 255, "cannot specify more than 254 instances");
|
assert!(instances < 255, "cannot specify more than 254 instances");
|
||||||
self.max_instances = instances as DWORD;
|
self.max_instances = instances as DWORD;
|
||||||
|
|||||||
@@ -64,7 +64,11 @@ impl Park for ParkThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
|
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
|
||||||
|
// Wasi doesn't have threads, so just sleep.
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
self.inner.park_timeout(duration);
|
self.inner.park_timeout(duration);
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
std::thread::sleep(duration);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-12
@@ -1285,41 +1285,39 @@ impl ChildStderr {
|
|||||||
|
|
||||||
impl AsyncWrite for ChildStdin {
|
impl AsyncWrite for ChildStdin {
|
||||||
fn poll_write(
|
fn poll_write(
|
||||||
self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
self.inner.poll_write(cx, buf)
|
Pin::new(&mut self.inner).poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
Poll::Ready(Ok(()))
|
Pin::new(&mut self.inner).poll_flush(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
Poll::Ready(Ok(()))
|
Pin::new(&mut self.inner).poll_shutdown(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncRead for ChildStdout {
|
impl AsyncRead for ChildStdout {
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &mut ReadBuf<'_>,
|
buf: &mut ReadBuf<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
// Safety: pipes support reading into uninitialized memory
|
Pin::new(&mut self.inner).poll_read(cx, buf)
|
||||||
unsafe { self.inner.poll_read(cx, buf) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncRead for ChildStderr {
|
impl AsyncRead for ChildStderr {
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &mut ReadBuf<'_>,
|
buf: &mut ReadBuf<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
// Safety: pipes support reading into uninitialized memory
|
Pin::new(&mut self.inner).poll_read(cx, buf)
|
||||||
unsafe { self.inner.poll_read(cx, buf) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use orphan::{OrphanQueue, OrphanQueueImpl, Wait};
|
|||||||
mod reap;
|
mod reap;
|
||||||
use reap::Reaper;
|
use reap::Reaper;
|
||||||
|
|
||||||
use crate::io::PollEvented;
|
use crate::io::{AsyncRead, AsyncWrite, PollEvented, ReadBuf};
|
||||||
use crate::process::kill::Kill;
|
use crate::process::kill::Kill;
|
||||||
use crate::process::SpawnedChild;
|
use crate::process::SpawnedChild;
|
||||||
use crate::signal::unix::driver::Handle as SignalHandle;
|
use crate::signal::unix::driver::Handle as SignalHandle;
|
||||||
@@ -177,8 +177,8 @@ impl AsRawFd for Pipe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn convert_to_stdio(io: PollEvented<Pipe>) -> io::Result<Stdio> {
|
pub(crate) fn convert_to_stdio(io: ChildStdio) -> io::Result<Stdio> {
|
||||||
let mut fd = io.into_inner()?.fd;
|
let mut fd = io.inner.into_inner()?.fd;
|
||||||
|
|
||||||
// Ensure that the fd to be inherited is set to *blocking* mode, as this
|
// Ensure that the fd to be inherited is set to *blocking* mode, as this
|
||||||
// is the default that virtually all programs expect to have. Those
|
// is the default that virtually all programs expect to have. Those
|
||||||
@@ -213,7 +213,50 @@ impl Source for Pipe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type ChildStdio = PollEvented<Pipe>;
|
pub(crate) struct ChildStdio {
|
||||||
|
inner: PollEvented<Pipe>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ChildStdio {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.inner.fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for ChildStdio {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.inner.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for ChildStdio {
|
||||||
|
fn poll_write(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
self.inner.poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRead for ChildStdio {
|
||||||
|
fn poll_read(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf<'_>,
|
||||||
|
) -> Poll<io::Result<()>> {
|
||||||
|
// Safety: pipes support reading into uninitialized memory
|
||||||
|
unsafe { self.inner.poll_read(cx, buf) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()> {
|
fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -238,7 +281,7 @@ fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn stdio<T>(io: T) -> io::Result<PollEvented<Pipe>>
|
pub(super) fn stdio<T>(io: T) -> io::Result<ChildStdio>
|
||||||
where
|
where
|
||||||
T: IntoRawFd,
|
T: IntoRawFd,
|
||||||
{
|
{
|
||||||
@@ -246,5 +289,5 @@ where
|
|||||||
let mut pipe = Pipe::from(io);
|
let mut pipe = Pipe::from(io);
|
||||||
set_nonblocking(&mut pipe, true)?;
|
set_nonblocking(&mut pipe, true)?;
|
||||||
|
|
||||||
PollEvented::new(pipe)
|
PollEvented::new(pipe).map(|inner| ChildStdio { inner })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,22 +15,22 @@
|
|||||||
//! `RegisterWaitForSingleObject` and then wait on the other end of the oneshot
|
//! `RegisterWaitForSingleObject` and then wait on the other end of the oneshot
|
||||||
//! from then on out.
|
//! from then on out.
|
||||||
|
|
||||||
use crate::io::PollEvented;
|
use crate::io::{blocking::Blocking, AsyncRead, AsyncWrite, ReadBuf};
|
||||||
use crate::process::kill::Kill;
|
use crate::process::kill::Kill;
|
||||||
use crate::process::SpawnedChild;
|
use crate::process::SpawnedChild;
|
||||||
use crate::sync::oneshot;
|
use crate::sync::oneshot;
|
||||||
|
|
||||||
use mio::windows::NamedPipe;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::fs::File as StdFile;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
|
use std::os::windows::prelude::{AsRawHandle, IntoRawHandle, RawHandle};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::process::{Child as StdChild, Command as StdCommand, ExitStatus};
|
use std::process::{Child as StdChild, Command as StdCommand, ExitStatus};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::task::Context;
|
use std::sync::Arc;
|
||||||
use std::task::Poll;
|
use std::task::{Context, Poll};
|
||||||
use winapi::shared::minwindef::{DWORD, FALSE};
|
use winapi::shared::minwindef::{DWORD, FALSE};
|
||||||
use winapi::um::handleapi::{DuplicateHandle, INVALID_HANDLE_VALUE};
|
use winapi::um::handleapi::{DuplicateHandle, INVALID_HANDLE_VALUE};
|
||||||
use winapi::um::processthreadsapi::GetCurrentProcess;
|
use winapi::um::processthreadsapi::GetCurrentProcess;
|
||||||
@@ -167,28 +167,97 @@ unsafe extern "system" fn callback(ptr: PVOID, _timer_fired: BOOLEAN) {
|
|||||||
let _ = complete.take().unwrap().send(());
|
let _ = complete.take().unwrap().send(());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type ChildStdio = PollEvented<NamedPipe>;
|
#[derive(Debug)]
|
||||||
|
struct ArcFile(Arc<StdFile>);
|
||||||
|
|
||||||
pub(super) fn stdio<T>(io: T) -> io::Result<PollEvented<NamedPipe>>
|
impl io::Read for ArcFile {
|
||||||
|
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
|
||||||
|
(&*self.0).read(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Write for ArcFile {
|
||||||
|
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
|
||||||
|
(&*self.0).write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
(&*self.0).flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ChildStdio {
|
||||||
|
// Used for accessing the raw handle, even if the io version is busy
|
||||||
|
raw: Arc<StdFile>,
|
||||||
|
// For doing I/O operations asynchronously
|
||||||
|
io: Blocking<ArcFile>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawHandle for ChildStdio {
|
||||||
|
fn as_raw_handle(&self) -> RawHandle {
|
||||||
|
self.raw.as_raw_handle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRead for ChildStdio {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf<'_>,
|
||||||
|
) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut self.io).poll_read(cx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for ChildStdio {
|
||||||
|
fn poll_write(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
Pin::new(&mut self.io).poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut self.io).poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut self.io).poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn stdio<T>(io: T) -> io::Result<ChildStdio>
|
||||||
where
|
where
|
||||||
T: IntoRawHandle,
|
T: IntoRawHandle,
|
||||||
{
|
{
|
||||||
let pipe = unsafe { NamedPipe::from_raw_handle(io.into_raw_handle()) };
|
use std::os::windows::prelude::FromRawHandle;
|
||||||
PollEvented::new(pipe)
|
|
||||||
|
let raw = Arc::new(unsafe { StdFile::from_raw_handle(io.into_raw_handle()) });
|
||||||
|
let io = Blocking::new(ArcFile(raw.clone()));
|
||||||
|
Ok(ChildStdio { raw, io })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn convert_to_stdio(io: PollEvented<NamedPipe>) -> io::Result<Stdio> {
|
pub(crate) fn convert_to_stdio(child_stdio: ChildStdio) -> io::Result<Stdio> {
|
||||||
let named_pipe = io.into_inner()?;
|
let ChildStdio { raw, io } = child_stdio;
|
||||||
|
drop(io); // Try to drop the Arc count here
|
||||||
|
|
||||||
|
Arc::try_unwrap(raw)
|
||||||
|
.or_else(|raw| duplicate_handle(&*raw))
|
||||||
|
.map(Stdio::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn duplicate_handle<T: AsRawHandle>(io: &T) -> io::Result<StdFile> {
|
||||||
|
use std::os::windows::prelude::FromRawHandle;
|
||||||
|
|
||||||
// Mio does not implement `IntoRawHandle` for `NamedPipe`, so we'll manually
|
|
||||||
// duplicate the handle here...
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut dup_handle = INVALID_HANDLE_VALUE;
|
let mut dup_handle = INVALID_HANDLE_VALUE;
|
||||||
let cur_proc = GetCurrentProcess();
|
let cur_proc = GetCurrentProcess();
|
||||||
|
|
||||||
let status = DuplicateHandle(
|
let status = DuplicateHandle(
|
||||||
cur_proc,
|
cur_proc,
|
||||||
named_pipe.as_raw_handle(),
|
io.as_raw_handle(),
|
||||||
cur_proc,
|
cur_proc,
|
||||||
&mut dup_handle,
|
&mut dup_handle,
|
||||||
0 as DWORD,
|
0 as DWORD,
|
||||||
@@ -200,6 +269,6 @@ pub(crate) fn convert_to_stdio(io: PollEvented<NamedPipe>) -> io::Result<Stdio>
|
|||||||
return Err(io::Error::last_os_error());
|
return Err(io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Stdio::from_raw_handle(dup_handle))
|
Ok(StdFile::from_raw_handle(dup_handle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
//! compilation.
|
//! compilation.
|
||||||
|
|
||||||
mod pool;
|
mod pool;
|
||||||
pub(crate) use pool::{spawn_blocking, BlockingPool, Mandatory, Spawner, Task};
|
pub(crate) use pool::{spawn_blocking, BlockingPool, Mandatory, SpawnError, Spawner, Task};
|
||||||
|
|
||||||
cfg_fs! {
|
cfg_fs! {
|
||||||
pub(crate) use pool::spawn_mandatory_blocking;
|
pub(crate) use pool::spawn_mandatory_blocking;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::runtime::{Builder, Callback, ToHandle};
|
|||||||
|
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::io;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub(crate) struct BlockingPool {
|
pub(crate) struct BlockingPool {
|
||||||
@@ -82,6 +83,25 @@ pub(crate) enum Mandatory {
|
|||||||
NonMandatory,
|
NonMandatory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) enum SpawnError {
|
||||||
|
/// Pool is shutting down and the task was not scheduled
|
||||||
|
ShuttingDown,
|
||||||
|
/// There are no worker threads available to take the task
|
||||||
|
/// and the OS failed to spawn a new one
|
||||||
|
NoThreads(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SpawnError> for io::Error {
|
||||||
|
fn from(e: SpawnError) -> Self {
|
||||||
|
match e {
|
||||||
|
SpawnError::ShuttingDown => {
|
||||||
|
io::Error::new(io::ErrorKind::Other, "blocking pool shutting down")
|
||||||
|
}
|
||||||
|
SpawnError::NoThreads(e) => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
pub(crate) fn new(task: task::UnownedTask<NoopSchedule>, mandatory: Mandatory) -> Task {
|
pub(crate) fn new(task: task::UnownedTask<NoopSchedule>, mandatory: Mandatory) -> Task {
|
||||||
Task { task, mandatory }
|
Task { task, mandatory }
|
||||||
@@ -105,6 +125,7 @@ const KEEP_ALIVE: Duration = Duration::from_secs(10);
|
|||||||
/// Tasks will be scheduled as non-mandatory, meaning they may not get executed
|
/// Tasks will be scheduled as non-mandatory, meaning they may not get executed
|
||||||
/// in case of runtime shutdown.
|
/// in case of runtime shutdown.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
#[cfg_attr(target_os = "wasi", allow(dead_code))]
|
||||||
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
|
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
|
||||||
where
|
where
|
||||||
F: FnOnce() -> R + Send + 'static,
|
F: FnOnce() -> R + Send + 'static,
|
||||||
@@ -220,7 +241,7 @@ impl fmt::Debug for BlockingPool {
|
|||||||
// ===== impl Spawner =====
|
// ===== impl Spawner =====
|
||||||
|
|
||||||
impl Spawner {
|
impl Spawner {
|
||||||
pub(crate) fn spawn(&self, task: Task, rt: &dyn ToHandle) -> Result<(), ()> {
|
pub(crate) fn spawn(&self, task: Task, rt: &dyn ToHandle) -> Result<(), SpawnError> {
|
||||||
let mut shared = self.inner.shared.lock();
|
let mut shared = self.inner.shared.lock();
|
||||||
|
|
||||||
if shared.shutdown {
|
if shared.shutdown {
|
||||||
@@ -230,7 +251,7 @@ impl Spawner {
|
|||||||
task.task.shutdown();
|
task.task.shutdown();
|
||||||
|
|
||||||
// no need to even push this task; it would never get picked up
|
// no need to even push this task; it would never get picked up
|
||||||
return Err(());
|
return Err(SpawnError::ShuttingDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
shared.queue.push_back(task);
|
shared.queue.push_back(task);
|
||||||
@@ -261,7 +282,7 @@ impl Spawner {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
// The OS refused to spawn the thread and there is no thread
|
// The OS refused to spawn the thread and there is no thread
|
||||||
// to pick up the task that has just been pushed to the queue.
|
// to pick up the task that has just been pushed to the queue.
|
||||||
panic!("OS can't spawn worker thread: {}", e)
|
return Err(SpawnError::NoThreads(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ pub(crate) type ThreadNameFn = std::sync::Arc<dyn Fn() -> String + Send + Sync +
|
|||||||
|
|
||||||
pub(crate) enum Kind {
|
pub(crate) enum Kind {
|
||||||
CurrentThread,
|
CurrentThread,
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
MultiThread,
|
MultiThread,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,14 +197,16 @@ impl Builder {
|
|||||||
Builder::new(Kind::CurrentThread, 31, EVENT_INTERVAL)
|
Builder::new(Kind::CurrentThread, 31, EVENT_INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new builder with the multi thread scheduler selected.
|
cfg_not_wasi! {
|
||||||
///
|
/// Returns a new builder with the multi thread scheduler selected.
|
||||||
/// Configuration methods can be chained on the return value.
|
///
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
/// Configuration methods can be chained on the return value.
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
|
#[cfg(feature = "rt-multi-thread")]
|
||||||
pub fn new_multi_thread() -> Builder {
|
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
|
||||||
// The number `61` is fairly arbitrary. I believe this value was copied from golang.
|
pub fn new_multi_thread() -> Builder {
|
||||||
Builder::new(Kind::MultiThread, 61, 61)
|
// The number `61` is fairly arbitrary. I believe this value was copied from golang.
|
||||||
|
Builder::new(Kind::MultiThread, 61, 61)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new runtime builder initialized with default configuration
|
/// Returns a new runtime builder initialized with default configuration
|
||||||
@@ -270,7 +272,11 @@ impl Builder {
|
|||||||
/// .unwrap();
|
/// .unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn enable_all(&mut self) -> &mut Self {
|
pub fn enable_all(&mut self) -> &mut Self {
|
||||||
#[cfg(any(feature = "net", feature = "process", all(unix, feature = "signal")))]
|
#[cfg(any(
|
||||||
|
feature = "net",
|
||||||
|
all(unix, feature = "process"),
|
||||||
|
all(unix, feature = "signal")
|
||||||
|
))]
|
||||||
self.enable_io();
|
self.enable_io();
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
self.enable_time();
|
self.enable_time();
|
||||||
@@ -287,11 +293,7 @@ impl Builder {
|
|||||||
///
|
///
|
||||||
/// The default value is the number of cores available to the system.
|
/// The default value is the number of cores available to the system.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// When using the `current_thread` runtime this method has no effect.
|
||||||
///
|
|
||||||
/// When using the `current_thread` runtime this method will panic, since
|
|
||||||
/// those variants do not allow setting worker thread counts.
|
|
||||||
///
|
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@@ -617,7 +619,7 @@ impl Builder {
|
|||||||
pub fn build(&mut self) -> io::Result<Runtime> {
|
pub fn build(&mut self) -> io::Result<Runtime> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
Kind::CurrentThread => self.build_basic_runtime(),
|
Kind::CurrentThread => self.build_basic_runtime(),
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Kind::MultiThread => self.build_threaded_runtime(),
|
Kind::MultiThread => self.build_threaded_runtime(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,7 +628,7 @@ impl Builder {
|
|||||||
driver::Cfg {
|
driver::Cfg {
|
||||||
enable_pause_time: match self.kind {
|
enable_pause_time: match self.kind {
|
||||||
Kind::CurrentThread => true,
|
Kind::CurrentThread => true,
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Kind::MultiThread => false,
|
Kind::MultiThread => false,
|
||||||
},
|
},
|
||||||
enable_io: self.enable_io,
|
enable_io: self.enable_io,
|
||||||
@@ -736,7 +738,7 @@ impl Builder {
|
|||||||
/// * `UnhandledPanic::ShutdownRuntime` will force the runtime to
|
/// * `UnhandledPanic::ShutdownRuntime` will force the runtime to
|
||||||
/// shutdown immediately when a spawned task panics even if that
|
/// shutdown immediately when a spawned task panics even if that
|
||||||
/// task's `JoinHandle` has not been dropped. All other spawned tasks
|
/// task's `JoinHandle` has not been dropped. All other spawned tasks
|
||||||
/// will immediatetly terminate and further calls to
|
/// will immediately terminate and further calls to
|
||||||
/// [`Runtime::block_on`] will panic.
|
/// [`Runtime::block_on`] will panic.
|
||||||
///
|
///
|
||||||
/// # Unstable
|
/// # Unstable
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub(crate) fn current() -> Handle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg_io_driver! {
|
cfg_io_driver! {
|
||||||
|
#[track_caller]
|
||||||
pub(crate) fn io_handle() -> crate::runtime::driver::IoHandle {
|
pub(crate) fn io_handle() -> crate::runtime::driver::IoHandle {
|
||||||
match CONTEXT.try_with(|ctx| {
|
match CONTEXT.try_with(|ctx| {
|
||||||
let ctx = ctx.borrow();
|
let ctx = ctx.borrow();
|
||||||
|
|||||||
@@ -23,7 +23,11 @@ pub struct Handle {
|
|||||||
pub(crate) struct HandleInner {
|
pub(crate) struct HandleInner {
|
||||||
/// Handles to the I/O drivers
|
/// Handles to the I/O drivers
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
not(any(feature = "net", feature = "process", all(unix, feature = "signal"))),
|
not(any(
|
||||||
|
feature = "net",
|
||||||
|
all(unix, feature = "process"),
|
||||||
|
all(unix, feature = "signal"),
|
||||||
|
)),
|
||||||
allow(dead_code)
|
allow(dead_code)
|
||||||
)]
|
)]
|
||||||
pub(super) io_handle: driver::IoHandle,
|
pub(super) io_handle: driver::IoHandle,
|
||||||
@@ -341,7 +345,7 @@ impl HandleInner {
|
|||||||
F: FnOnce() -> R + Send + 'static,
|
F: FnOnce() -> R + Send + 'static,
|
||||||
R: Send + 'static,
|
R: Send + 'static,
|
||||||
{
|
{
|
||||||
let (join_handle, _was_spawned) = if cfg!(debug_assertions)
|
let (join_handle, spawn_result) = if cfg!(debug_assertions)
|
||||||
&& std::mem::size_of::<F>() > 2048
|
&& std::mem::size_of::<F>() > 2048
|
||||||
{
|
{
|
||||||
self.spawn_blocking_inner(Box::new(func), blocking::Mandatory::NonMandatory, None, rt)
|
self.spawn_blocking_inner(Box::new(func), blocking::Mandatory::NonMandatory, None, rt)
|
||||||
@@ -349,7 +353,14 @@ impl HandleInner {
|
|||||||
self.spawn_blocking_inner(func, blocking::Mandatory::NonMandatory, None, rt)
|
self.spawn_blocking_inner(func, blocking::Mandatory::NonMandatory, None, rt)
|
||||||
};
|
};
|
||||||
|
|
||||||
join_handle
|
match spawn_result {
|
||||||
|
Ok(()) => join_handle,
|
||||||
|
// Compat: do not panic here, return the join_handle even though it will never resolve
|
||||||
|
Err(blocking::SpawnError::ShuttingDown) => join_handle,
|
||||||
|
Err(blocking::SpawnError::NoThreads(e)) => {
|
||||||
|
panic!("OS can't spawn worker thread: {}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_fs! {
|
cfg_fs! {
|
||||||
@@ -363,7 +374,7 @@ impl HandleInner {
|
|||||||
F: FnOnce() -> R + Send + 'static,
|
F: FnOnce() -> R + Send + 'static,
|
||||||
R: Send + 'static,
|
R: Send + 'static,
|
||||||
{
|
{
|
||||||
let (join_handle, was_spawned) = if cfg!(debug_assertions) && std::mem::size_of::<F>() > 2048 {
|
let (join_handle, spawn_result) = if cfg!(debug_assertions) && std::mem::size_of::<F>() > 2048 {
|
||||||
self.spawn_blocking_inner(
|
self.spawn_blocking_inner(
|
||||||
Box::new(func),
|
Box::new(func),
|
||||||
blocking::Mandatory::Mandatory,
|
blocking::Mandatory::Mandatory,
|
||||||
@@ -379,7 +390,7 @@ impl HandleInner {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if was_spawned {
|
if spawn_result.is_ok() {
|
||||||
Some(join_handle)
|
Some(join_handle)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -394,7 +405,7 @@ impl HandleInner {
|
|||||||
is_mandatory: blocking::Mandatory,
|
is_mandatory: blocking::Mandatory,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
rt: &dyn ToHandle,
|
rt: &dyn ToHandle,
|
||||||
) -> (JoinHandle<R>, bool)
|
) -> (JoinHandle<R>, Result<(), blocking::SpawnError>)
|
||||||
where
|
where
|
||||||
F: FnOnce() -> R + Send + 'static,
|
F: FnOnce() -> R + Send + 'static,
|
||||||
R: Send + 'static,
|
R: Send + 'static,
|
||||||
@@ -424,7 +435,7 @@ impl HandleInner {
|
|||||||
let spawned = self
|
let spawned = self
|
||||||
.blocking_spawner
|
.blocking_spawner
|
||||||
.spawn(blocking::Task::new(task, is_mandatory), rt);
|
.spawn(blocking::Task::new(task, is_mandatory), rt);
|
||||||
(handle, spawned.is_ok())
|
(handle, spawned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ impl RuntimeMetrics {
|
|||||||
///
|
///
|
||||||
/// `worker` is the index of the worker being queried. The given value must
|
/// `worker` is the index of the worker being queried. The given value must
|
||||||
/// be between 0 and `num_workers()`. The index uniquely identifies a single
|
/// be between 0 and `num_workers()`. The index uniquely identifies a single
|
||||||
/// worker and will continue to indentify the worker throughout the lifetime
|
/// worker and will continue to identify the worker throughout the lifetime
|
||||||
/// of the runtime instance.
|
/// of the runtime instance.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
|
|||||||
+41
-37
@@ -204,6 +204,7 @@ cfg_rt! {
|
|||||||
|
|
||||||
mod blocking;
|
mod blocking;
|
||||||
use blocking::BlockingPool;
|
use blocking::BlockingPool;
|
||||||
|
#[cfg_attr(target_os = "wasi", allow(unused_imports))]
|
||||||
pub(crate) use blocking::spawn_blocking;
|
pub(crate) use blocking::spawn_blocking;
|
||||||
|
|
||||||
cfg_trace! {
|
cfg_trace! {
|
||||||
@@ -303,7 +304,7 @@ cfg_rt! {
|
|||||||
CurrentThread(BasicScheduler),
|
CurrentThread(BasicScheduler),
|
||||||
|
|
||||||
/// Execute tasks across multiple threads.
|
/// Execute tasks across multiple threads.
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
ThreadPool(ThreadPool),
|
ThreadPool(ThreadPool),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,39 +312,41 @@ cfg_rt! {
|
|||||||
type Callback = std::sync::Arc<dyn Fn() + Send + Sync>;
|
type Callback = std::sync::Arc<dyn Fn() + Send + Sync>;
|
||||||
|
|
||||||
impl Runtime {
|
impl Runtime {
|
||||||
/// Creates a new runtime instance with default configuration values.
|
cfg_not_wasi! {
|
||||||
///
|
/// Creates a new runtime instance with default configuration values.
|
||||||
/// This results in the multi threaded scheduler, I/O driver, and time driver being
|
///
|
||||||
/// initialized.
|
/// This results in the multi threaded scheduler, I/O driver, and time driver being
|
||||||
///
|
/// initialized.
|
||||||
/// Most applications will not need to call this function directly. Instead,
|
///
|
||||||
/// they will use the [`#[tokio::main]` attribute][main]. When a more complex
|
/// Most applications will not need to call this function directly. Instead,
|
||||||
/// configuration is necessary, the [runtime builder] may be used.
|
/// they will use the [`#[tokio::main]` attribute][main]. When a more complex
|
||||||
///
|
/// configuration is necessary, the [runtime builder] may be used.
|
||||||
/// See [module level][mod] documentation for more details.
|
///
|
||||||
///
|
/// See [module level][mod] documentation for more details.
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// Creating a new `Runtime` with default configuration values.
|
///
|
||||||
///
|
/// Creating a new `Runtime` with default configuration values.
|
||||||
/// ```
|
///
|
||||||
/// use tokio::runtime::Runtime;
|
/// ```
|
||||||
///
|
/// use tokio::runtime::Runtime;
|
||||||
/// let rt = Runtime::new()
|
///
|
||||||
/// .unwrap();
|
/// let rt = Runtime::new()
|
||||||
///
|
/// .unwrap();
|
||||||
/// // Use the runtime...
|
///
|
||||||
/// ```
|
/// // Use the runtime...
|
||||||
///
|
/// ```
|
||||||
/// [mod]: index.html
|
///
|
||||||
/// [main]: ../attr.main.html
|
/// [mod]: index.html
|
||||||
/// [threaded scheduler]: index.html#threaded-scheduler
|
/// [main]: ../attr.main.html
|
||||||
/// [basic scheduler]: index.html#basic-scheduler
|
/// [threaded scheduler]: index.html#threaded-scheduler
|
||||||
/// [runtime builder]: crate::runtime::Builder
|
/// [basic scheduler]: index.html#basic-scheduler
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
/// [runtime builder]: crate::runtime::Builder
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
|
#[cfg(feature = "rt-multi-thread")]
|
||||||
pub fn new() -> std::io::Result<Runtime> {
|
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
|
||||||
Builder::new_multi_thread().enable_all().build()
|
pub fn new() -> std::io::Result<Runtime> {
|
||||||
|
Builder::new_multi_thread().enable_all().build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a handle to the runtime's spawner.
|
/// Returns a handle to the runtime's spawner.
|
||||||
@@ -472,6 +475,7 @@ cfg_rt! {
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [handle]: fn@Handle::block_on
|
/// [handle]: fn@Handle::block_on
|
||||||
|
#[track_caller]
|
||||||
pub fn block_on<F: Future>(&self, future: F) -> F::Output {
|
pub fn block_on<F: Future>(&self, future: F) -> F::Output {
|
||||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||||
let future = crate::util::trace::task(future, "block_on", None, task::Id::next().as_u64());
|
let future = crate::util::trace::task(future, "block_on", None, task::Id::next().as_u64());
|
||||||
@@ -480,7 +484,7 @@ cfg_rt! {
|
|||||||
|
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
Kind::CurrentThread(exec) => exec.block_on(future),
|
Kind::CurrentThread(exec) => exec.block_on(future),
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Kind::ThreadPool(exec) => exec.block_on(future),
|
Kind::ThreadPool(exec) => exec.block_on(future),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,7 +577,7 @@ cfg_rt! {
|
|||||||
/// may result in a resource leak (in that any blocking tasks are still running until they
|
/// may result in a resource leak (in that any blocking tasks are still running until they
|
||||||
/// return.
|
/// return.
|
||||||
///
|
///
|
||||||
/// This function is equivalent to calling `shutdown_timeout(Duration::of_nanos(0))`.
|
/// This function is equivalent to calling `shutdown_timeout(Duration::from_nanos(0))`.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use tokio::runtime::Runtime;
|
/// use tokio::runtime::Runtime;
|
||||||
@@ -610,7 +614,7 @@ cfg_rt! {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Kind::ThreadPool(_) => {
|
Kind::ThreadPool(_) => {
|
||||||
// The threaded scheduler drops its tasks on its worker threads, which is
|
// The threaded scheduler drops its tasks on its worker threads, which is
|
||||||
// already in the runtime's context.
|
// already in the runtime's context.
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ cfg_rt_multi_thread! {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum Spawner {
|
pub(crate) enum Spawner {
|
||||||
Basic(basic_scheduler::Spawner),
|
Basic(basic_scheduler::Spawner),
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
ThreadPool(thread_pool::Spawner),
|
ThreadPool(thread_pool::Spawner),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Spawner {
|
impl Spawner {
|
||||||
pub(crate) fn shutdown(&mut self) {
|
pub(crate) fn shutdown(&mut self) {
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
{
|
{
|
||||||
if let Spawner::ThreadPool(spawner) = self {
|
if let Spawner::ThreadPool(spawner) = self {
|
||||||
spawner.shutdown();
|
spawner.shutdown();
|
||||||
@@ -31,7 +31,7 @@ impl Spawner {
|
|||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
Spawner::Basic(spawner) => spawner.spawn(future, id),
|
Spawner::Basic(spawner) => spawner.spawn(future, id),
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Spawner::ThreadPool(spawner) => spawner.spawn(future, id),
|
Spawner::ThreadPool(spawner) => spawner.spawn(future, id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ impl Spawner {
|
|||||||
pub(crate) fn as_handle_inner(&self) -> &HandleInner {
|
pub(crate) fn as_handle_inner(&self) -> &HandleInner {
|
||||||
match self {
|
match self {
|
||||||
Spawner::Basic(spawner) => spawner.as_handle_inner(),
|
Spawner::Basic(spawner) => spawner.as_handle_inner(),
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Spawner::ThreadPool(spawner) => spawner.as_handle_inner(),
|
Spawner::ThreadPool(spawner) => spawner.as_handle_inner(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ cfg_metrics! {
|
|||||||
pub(crate) fn num_workers(&self) -> usize {
|
pub(crate) fn num_workers(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Spawner::Basic(_) => 1,
|
Spawner::Basic(_) => 1,
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Spawner::ThreadPool(spawner) => spawner.num_workers(),
|
Spawner::ThreadPool(spawner) => spawner.num_workers(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ cfg_metrics! {
|
|||||||
pub(crate) fn scheduler_metrics(&self) -> &SchedulerMetrics {
|
pub(crate) fn scheduler_metrics(&self) -> &SchedulerMetrics {
|
||||||
match self {
|
match self {
|
||||||
Spawner::Basic(spawner) => spawner.scheduler_metrics(),
|
Spawner::Basic(spawner) => spawner.scheduler_metrics(),
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Spawner::ThreadPool(spawner) => spawner.scheduler_metrics(),
|
Spawner::ThreadPool(spawner) => spawner.scheduler_metrics(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ cfg_metrics! {
|
|||||||
pub(crate) fn worker_metrics(&self, worker: usize) -> &WorkerMetrics {
|
pub(crate) fn worker_metrics(&self, worker: usize) -> &WorkerMetrics {
|
||||||
match self {
|
match self {
|
||||||
Spawner::Basic(spawner) => spawner.worker_metrics(worker),
|
Spawner::Basic(spawner) => spawner.worker_metrics(worker),
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Spawner::ThreadPool(spawner) => spawner.worker_metrics(worker),
|
Spawner::ThreadPool(spawner) => spawner.worker_metrics(worker),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ cfg_metrics! {
|
|||||||
pub(crate) fn injection_queue_depth(&self) -> usize {
|
pub(crate) fn injection_queue_depth(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Spawner::Basic(spawner) => spawner.injection_queue_depth(),
|
Spawner::Basic(spawner) => spawner.injection_queue_depth(),
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Spawner::ThreadPool(spawner) => spawner.injection_queue_depth(),
|
Spawner::ThreadPool(spawner) => spawner.injection_queue_depth(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ cfg_metrics! {
|
|||||||
pub(crate) fn worker_local_queue_depth(&self, worker: usize) -> usize {
|
pub(crate) fn worker_local_queue_depth(&self, worker: usize) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Spawner::Basic(spawner) => spawner.worker_metrics(worker).queue_depth(),
|
Spawner::Basic(spawner) => spawner.worker_metrics(worker).queue_depth(),
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
|
||||||
Spawner::ThreadPool(spawner) => spawner.worker_local_queue_depth(worker),
|
Spawner::ThreadPool(spawner) => spawner.worker_local_queue_depth(worker),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,6 @@ pub(crate) struct Header {
|
|||||||
/// Task state.
|
/// Task state.
|
||||||
pub(super) state: State,
|
pub(super) state: State,
|
||||||
|
|
||||||
pub(super) owned: UnsafeCell<linked_list::Pointers<Header>>,
|
|
||||||
|
|
||||||
/// Pointer to next task, used with the injection queue.
|
/// Pointer to next task, used with the injection queue.
|
||||||
pub(super) queue_next: UnsafeCell<Option<NonNull<Header>>>,
|
pub(super) queue_next: UnsafeCell<Option<NonNull<Header>>>,
|
||||||
|
|
||||||
@@ -89,12 +87,23 @@ pub(crate) struct Header {
|
|||||||
unsafe impl Send for Header {}
|
unsafe impl Send for Header {}
|
||||||
unsafe impl Sync for Header {}
|
unsafe impl Sync for Header {}
|
||||||
|
|
||||||
/// Cold data is stored after the future.
|
/// Cold data is stored after the future. Data is considered cold if it is only
|
||||||
|
/// used during creation or shutdown of the task.
|
||||||
pub(super) struct Trailer {
|
pub(super) struct Trailer {
|
||||||
|
/// Pointers for the linked list in the `OwnedTasks` that owns this task.
|
||||||
|
pub(super) owned: linked_list::Pointers<Header>,
|
||||||
/// Consumer task waiting on completion of this task.
|
/// Consumer task waiting on completion of this task.
|
||||||
pub(super) waker: UnsafeCell<Option<Waker>>,
|
pub(super) waker: UnsafeCell<Option<Waker>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_addr_of_methods! {
|
||||||
|
impl<> Trailer {
|
||||||
|
pub(super) unsafe fn addr_of_owned(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Header>> {
|
||||||
|
&self.owned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Either the future or the output.
|
/// Either the future or the output.
|
||||||
pub(super) enum Stage<T: Future> {
|
pub(super) enum Stage<T: Future> {
|
||||||
Running(T),
|
Running(T),
|
||||||
@@ -108,10 +117,9 @@ impl<T: Future, S: Schedule> Cell<T, S> {
|
|||||||
pub(super) fn new(future: T, scheduler: S, state: State, task_id: Id) -> Box<Cell<T, S>> {
|
pub(super) fn new(future: T, scheduler: S, state: State, task_id: Id) -> Box<Cell<T, S>> {
|
||||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||||
let id = future.id();
|
let id = future.id();
|
||||||
Box::new(Cell {
|
let result = Box::new(Cell {
|
||||||
header: Header {
|
header: Header {
|
||||||
state,
|
state,
|
||||||
owned: UnsafeCell::new(linked_list::Pointers::new()),
|
|
||||||
queue_next: UnsafeCell::new(None),
|
queue_next: UnsafeCell::new(None),
|
||||||
vtable: raw::vtable::<T, S>(),
|
vtable: raw::vtable::<T, S>(),
|
||||||
owner_id: UnsafeCell::new(0),
|
owner_id: UnsafeCell::new(0),
|
||||||
@@ -127,8 +135,19 @@ impl<T: Future, S: Schedule> Cell<T, S> {
|
|||||||
},
|
},
|
||||||
trailer: Trailer {
|
trailer: Trailer {
|
||||||
waker: UnsafeCell::new(None),
|
waker: UnsafeCell::new(None),
|
||||||
|
owned: linked_list::Pointers::new(),
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
let trailer_addr = (&result.trailer) as *const Trailer as usize;
|
||||||
|
let trailer_ptr = unsafe { Header::get_trailer(NonNull::from(&result.header)) };
|
||||||
|
|
||||||
|
assert_eq!(trailer_addr, trailer_ptr.as_ptr() as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +259,17 @@ impl Header {
|
|||||||
// the safety requirements on `set_owner_id`.
|
// the safety requirements on `set_owner_id`.
|
||||||
unsafe { self.owner_id.with(|ptr| *ptr) }
|
unsafe { self.owner_id.with(|ptr| *ptr) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a pointer to the `Trailer` of the task containing this `Header`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The provided raw pointer must point at the header of a task.
|
||||||
|
pub(super) unsafe fn get_trailer(me: NonNull<Header>) -> NonNull<Trailer> {
|
||||||
|
let offset = me.as_ref().vtable.trailer_offset;
|
||||||
|
let trailer = me.as_ptr().cast::<u8>().add(offset).cast::<Trailer>();
|
||||||
|
NonNull::new_unchecked(trailer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Trailer {
|
impl Trailer {
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ impl<S: 'static> OwnedTasks<S> {
|
|||||||
|
|
||||||
// safety: We just checked that the provided task is not in some other
|
// safety: We just checked that the provided task is not in some other
|
||||||
// linked list.
|
// linked list.
|
||||||
unsafe { self.inner.lock().list.remove(task.header().into()) }
|
unsafe { self.inner.lock().list.remove(task.header_ptr()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_empty(&self) -> bool {
|
pub(crate) fn is_empty(&self) -> bool {
|
||||||
|
|||||||
@@ -334,6 +334,10 @@ impl<S: 'static> Task<S> {
|
|||||||
fn header(&self) -> &Header {
|
fn header(&self) -> &Header {
|
||||||
self.raw.header()
|
self.raw.header()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn header_ptr(&self) -> NonNull<Header> {
|
||||||
|
self.raw.header_ptr()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: 'static> Notified<S> {
|
impl<S: 'static> Notified<S> {
|
||||||
@@ -365,7 +369,7 @@ cfg_rt_multi_thread! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Schedule> Task<S> {
|
impl<S: Schedule> Task<S> {
|
||||||
/// Pre-emptively cancels the task as part of the shutdown process.
|
/// Preemptively cancels the task as part of the shutdown process.
|
||||||
pub(crate) fn shutdown(self) {
|
pub(crate) fn shutdown(self) {
|
||||||
let raw = self.raw;
|
let raw = self.raw;
|
||||||
mem::forget(self);
|
mem::forget(self);
|
||||||
@@ -473,8 +477,7 @@ unsafe impl<S> linked_list::Link for Task<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn pointers(target: NonNull<Header>) -> NonNull<linked_list::Pointers<Header>> {
|
unsafe fn pointers(target: NonNull<Header>) -> NonNull<linked_list::Pointers<Header>> {
|
||||||
// Not super great as it avoids some of looms checking...
|
self::core::Trailer::addr_of_owned(Header::get_trailer(target))
|
||||||
NonNull::from(target.as_ref().owned.with_mut(|ptr| &mut *ptr))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::future::Future;
|
use crate::future::Future;
|
||||||
|
use crate::runtime::task::core::{Core, Trailer};
|
||||||
use crate::runtime::task::{Cell, Harness, Header, Id, Schedule, State};
|
use crate::runtime::task::{Cell, Harness, Header, Id, Schedule, State};
|
||||||
|
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
@@ -35,6 +36,9 @@ pub(super) struct Vtable {
|
|||||||
|
|
||||||
/// Scheduler is being shutdown.
|
/// Scheduler is being shutdown.
|
||||||
pub(super) shutdown: unsafe fn(NonNull<Header>),
|
pub(super) shutdown: unsafe fn(NonNull<Header>),
|
||||||
|
|
||||||
|
/// The number of bytes that the `trailer` field is offset from the header.
|
||||||
|
pub(super) trailer_offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the vtable for the requested `T` and `S` generics.
|
/// Get the vtable for the requested `T` and `S` generics.
|
||||||
@@ -48,9 +52,55 @@ pub(super) fn vtable<T: Future, S: Schedule>() -> &'static Vtable {
|
|||||||
drop_abort_handle: drop_abort_handle::<T, S>,
|
drop_abort_handle: drop_abort_handle::<T, S>,
|
||||||
remote_abort: remote_abort::<T, S>,
|
remote_abort: remote_abort::<T, S>,
|
||||||
shutdown: shutdown::<T, S>,
|
shutdown: shutdown::<T, S>,
|
||||||
|
trailer_offset: TrailerOffsetHelper::<T, S>::OFFSET,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calling `get_trailer_offset` directly in vtable doesn't work because it
|
||||||
|
/// prevents the vtable from being promoted to a static reference.
|
||||||
|
///
|
||||||
|
/// See this thread for more info:
|
||||||
|
/// <https://users.rust-lang.org/t/custom-vtables-with-integers/78508>
|
||||||
|
struct TrailerOffsetHelper<T, S>(T, S);
|
||||||
|
impl<T: Future, S: Schedule> TrailerOffsetHelper<T, S> {
|
||||||
|
// Pass `size_of`/`align_of` as arguments rather than calling them directly
|
||||||
|
// inside `get_trailer_offset` because trait bounds on generic parameters
|
||||||
|
// of const fn are unstable on our MSRV.
|
||||||
|
const OFFSET: usize = get_trailer_offset(
|
||||||
|
std::mem::size_of::<Header>(),
|
||||||
|
std::mem::size_of::<Core<T, S>>(),
|
||||||
|
std::mem::align_of::<Core<T, S>>(),
|
||||||
|
std::mem::align_of::<Trailer>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the offset of the `Trailer` field in `Cell<T, S>` using the
|
||||||
|
/// `#[repr(C)]` algorithm.
|
||||||
|
///
|
||||||
|
/// Pseudo-code for the `#[repr(C)]` algorithm can be found here:
|
||||||
|
/// <https://doc.rust-lang.org/reference/type-layout.html#reprc-structs>
|
||||||
|
const fn get_trailer_offset(
|
||||||
|
header_size: usize,
|
||||||
|
core_size: usize,
|
||||||
|
core_align: usize,
|
||||||
|
trailer_align: usize,
|
||||||
|
) -> usize {
|
||||||
|
let mut offset = header_size;
|
||||||
|
|
||||||
|
let core_misalign = offset % core_align;
|
||||||
|
if core_misalign > 0 {
|
||||||
|
offset += core_align - core_misalign;
|
||||||
|
}
|
||||||
|
offset += core_size;
|
||||||
|
|
||||||
|
let trailer_misalign = offset % trailer_align;
|
||||||
|
if trailer_misalign > 0 {
|
||||||
|
offset += trailer_align - trailer_misalign;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
|
||||||
impl RawTask {
|
impl RawTask {
|
||||||
pub(super) fn new<T, S>(task: T, scheduler: S, id: Id) -> RawTask
|
pub(super) fn new<T, S>(task: T, scheduler: S, id: Id) -> RawTask
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pub(super) struct WakerRef<'a, S: 'static> {
|
|||||||
_p: PhantomData<(&'a Header, S)>,
|
_p: PhantomData<(&'a Header, S)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `WakerRef` which avoids having to pre-emptively increase the
|
/// Returns a `WakerRef` which avoids having to preemptively increase the
|
||||||
/// refcount if there is no need to do so.
|
/// refcount if there is no need to do so.
|
||||||
pub(super) fn waker_ref<T, S>(header: &NonNull<Header>) -> WakerRef<'_, S>
|
pub(super) fn waker_ref<T, S>(header: &NonNull<Header>) -> WakerRef<'_, S>
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -112,6 +112,14 @@ struct Waiter {
|
|||||||
_p: PhantomPinned,
|
_p: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_addr_of_methods! {
|
||||||
|
impl<> Waiter {
|
||||||
|
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||||
|
&self.pointers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Semaphore {
|
impl Semaphore {
|
||||||
/// The maximum number of permits which a semaphore can hold.
|
/// The maximum number of permits which a semaphore can hold.
|
||||||
///
|
///
|
||||||
@@ -704,12 +712,6 @@ impl std::error::Error for TryAcquireError {}
|
|||||||
///
|
///
|
||||||
/// `Waiter` is forced to be !Unpin.
|
/// `Waiter` is forced to be !Unpin.
|
||||||
unsafe impl linked_list::Link for Waiter {
|
unsafe impl linked_list::Link for Waiter {
|
||||||
// XXX: ideally, we would be able to use `Pin` here, to enforce the
|
|
||||||
// invariant that list entries may not move while in the list. However, we
|
|
||||||
// can't do this currently, as using `Pin<&'a mut Waiter>` as the `Handle`
|
|
||||||
// type would require `Semaphore` to be generic over a lifetime. We can't
|
|
||||||
// use `Pin<*mut Waiter>`, as raw pointers are `Unpin` regardless of whether
|
|
||||||
// or not they dereference to an `!Unpin` target.
|
|
||||||
type Handle = NonNull<Waiter>;
|
type Handle = NonNull<Waiter>;
|
||||||
type Target = Waiter;
|
type Target = Waiter;
|
||||||
|
|
||||||
@@ -721,7 +723,7 @@ unsafe impl linked_list::Link for Waiter {
|
|||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn pointers(mut target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||||
NonNull::from(&mut target.as_mut().pointers)
|
Waiter::addr_of_pointers(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -361,6 +361,14 @@ struct Waiter {
|
|||||||
_p: PhantomPinned,
|
_p: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_addr_of_methods! {
|
||||||
|
impl<> Waiter {
|
||||||
|
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||||
|
&self.pointers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct RecvGuard<'a, T> {
|
struct RecvGuard<'a, T> {
|
||||||
slot: RwLockReadGuard<'a, Slot<T>>,
|
slot: RwLockReadGuard<'a, Slot<T>>,
|
||||||
}
|
}
|
||||||
@@ -1140,8 +1148,8 @@ unsafe impl linked_list::Link for Waiter {
|
|||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn pointers(mut target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||||
NonNull::from(&mut target.as_mut().pointers)
|
Waiter::addr_of_pointers(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -214,7 +214,6 @@ enum NotificationType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)] // required by `linked_list::Link` impl
|
|
||||||
struct Waiter {
|
struct Waiter {
|
||||||
/// Intrusive linked-list pointers.
|
/// Intrusive linked-list pointers.
|
||||||
pointers: linked_list::Pointers<Waiter>,
|
pointers: linked_list::Pointers<Waiter>,
|
||||||
@@ -229,6 +228,14 @@ struct Waiter {
|
|||||||
_p: PhantomPinned,
|
_p: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_addr_of_methods! {
|
||||||
|
impl<> Waiter {
|
||||||
|
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||||
|
&self.pointers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Future returned from [`Notify::notified()`].
|
/// Future returned from [`Notify::notified()`].
|
||||||
///
|
///
|
||||||
/// This future is fused, so once it has completed, any future calls to poll
|
/// This future is fused, so once it has completed, any future calls to poll
|
||||||
@@ -950,7 +957,7 @@ unsafe impl linked_list::Link for Waiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||||
target.cast()
|
Waiter::addr_of_pointers(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ impl AssertSync for AtomicWaker {}
|
|||||||
impl AssertSend for Waker {}
|
impl AssertSend for Waker {}
|
||||||
impl AssertSync for Waker {}
|
impl AssertSync for Waker {}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||||
use wasm_bindgen_test::wasm_bindgen_test as test;
|
use wasm_bindgen_test::wasm_bindgen_test as test;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::mem::ManuallyDrop;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::task::{Context, RawWaker, RawWakerVTable, Waker};
|
use std::task::{Context, RawWaker, RawWakerVTable, Waker};
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||||
use wasm_bindgen_test::wasm_bindgen_test as test;
|
use wasm_bindgen_test::wasm_bindgen_test as test;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::sync::batch_semaphore::Semaphore;
|
use crate::sync::batch_semaphore::Semaphore;
|
||||||
use tokio_test::*;
|
use tokio_test::*;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||||
use wasm_bindgen_test::wasm_bindgen_test as test;
|
use wasm_bindgen_test::wasm_bindgen_test as test;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -665,7 +665,7 @@ impl<T> Sender<T> {
|
|||||||
///
|
///
|
||||||
/// The `modify` closure must return `true` if the value has actually
|
/// The `modify` closure must return `true` if the value has actually
|
||||||
/// been modified during the mutable borrow. It should only return `false`
|
/// been modified during the mutable borrow. It should only return `false`
|
||||||
/// if the value is guaranteed to be unnmodified despite the mutable
|
/// if the value is guaranteed to be unmodified despite the mutable
|
||||||
/// borrow.
|
/// borrow.
|
||||||
///
|
///
|
||||||
/// Receivers are only notified if the closure returned `true`. If the
|
/// Receivers are only notified if the closure returned `true`. If the
|
||||||
|
|||||||
@@ -102,11 +102,21 @@ cfg_rt! {
|
|||||||
/// their own. If you want to spawn an ordinary thread, you should use
|
/// their own. If you want to spawn an ordinary thread, you should use
|
||||||
/// [`thread::spawn`] instead.
|
/// [`thread::spawn`] instead.
|
||||||
///
|
///
|
||||||
/// Closures spawned using `spawn_blocking` cannot be cancelled. When you shut
|
/// Closures spawned using `spawn_blocking` cannot be cancelled abruptly; there
|
||||||
/// down the executor, it will wait indefinitely for all blocking operations to
|
/// is no standard low level API to cause a thread to stop running. However,
|
||||||
|
/// a useful pattern is to pass some form of "cancellation token" into
|
||||||
|
/// the thread. This could be an [`AtomicBool`] that the task checks periodically.
|
||||||
|
/// Another approach is to have the thread primarily read or write from a channel,
|
||||||
|
/// and to exit when the channel closes; assuming the other side of the channel is dropped
|
||||||
|
/// when cancellation occurs, this will cause the blocking task thread to exit
|
||||||
|
/// soon after as well.
|
||||||
|
///
|
||||||
|
/// When you shut down the executor, it will wait indefinitely for all blocking operations to
|
||||||
/// finish. You can use [`shutdown_timeout`] to stop waiting for them after a
|
/// finish. You can use [`shutdown_timeout`] to stop waiting for them after a
|
||||||
/// certain timeout. Be aware that this will still not cancel the tasks — they
|
/// certain timeout. Be aware that this will still not cancel the tasks — they
|
||||||
/// are simply allowed to keep running after the method returns.
|
/// are simply allowed to keep running after the method returns. It is possible
|
||||||
|
/// for a blocking task to be cancelled if it has not yet started running, but this
|
||||||
|
/// is not guaranteed.
|
||||||
///
|
///
|
||||||
/// Note that if you are using the single threaded runtime, this function will
|
/// Note that if you are using the single threaded runtime, this function will
|
||||||
/// still spawn additional threads for blocking operations. The basic
|
/// still spawn additional threads for blocking operations. The basic
|
||||||
@@ -140,6 +150,7 @@ cfg_rt! {
|
|||||||
/// [`thread::spawn`]: fn@std::thread::spawn
|
/// [`thread::spawn`]: fn@std::thread::spawn
|
||||||
/// [`shutdown_timeout`]: fn@crate::runtime::Runtime::shutdown_timeout
|
/// [`shutdown_timeout`]: fn@crate::runtime::Runtime::shutdown_timeout
|
||||||
/// [bridgesync]: https://tokio.rs/tokio/topics/bridging
|
/// [bridgesync]: https://tokio.rs/tokio/topics/bridging
|
||||||
|
/// [`AtomicBool`]: struct@std::sync::atomic::AtomicBool
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
|||||||
+27
-14
@@ -3,7 +3,7 @@ use crate::{
|
|||||||
runtime::{context, Handle},
|
runtime::{context, Handle},
|
||||||
task::{JoinHandle, LocalSet},
|
task::{JoinHandle, LocalSet},
|
||||||
};
|
};
|
||||||
use std::future::Future;
|
use std::{future::Future, io};
|
||||||
|
|
||||||
/// Factory which is used to configure the properties of a new task.
|
/// Factory which is used to configure the properties of a new task.
|
||||||
///
|
///
|
||||||
@@ -48,7 +48,7 @@ use std::future::Future;
|
|||||||
/// .spawn(async move {
|
/// .spawn(async move {
|
||||||
/// // Process each socket concurrently.
|
/// // Process each socket concurrently.
|
||||||
/// process(socket).await
|
/// process(socket).await
|
||||||
/// });
|
/// })?;
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
@@ -83,12 +83,12 @@ impl<'a> Builder<'a> {
|
|||||||
/// See [`task::spawn`](crate::task::spawn) for
|
/// See [`task::spawn`](crate::task::spawn) for
|
||||||
/// more details.
|
/// more details.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn<Fut>(self, future: Fut) -> JoinHandle<Fut::Output>
|
pub fn spawn<Fut>(self, future: Fut) -> io::Result<JoinHandle<Fut::Output>>
|
||||||
where
|
where
|
||||||
Fut: Future + Send + 'static,
|
Fut: Future + Send + 'static,
|
||||||
Fut::Output: Send + 'static,
|
Fut::Output: Send + 'static,
|
||||||
{
|
{
|
||||||
super::spawn::spawn_inner(future, self.name)
|
Ok(super::spawn::spawn_inner(future, self.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn a task with this builder's settings on the provided [runtime
|
/// Spawn a task with this builder's settings on the provided [runtime
|
||||||
@@ -99,12 +99,16 @@ impl<'a> Builder<'a> {
|
|||||||
/// [runtime handle]: crate::runtime::Handle
|
/// [runtime handle]: crate::runtime::Handle
|
||||||
/// [`Handle::spawn`]: crate::runtime::Handle::spawn
|
/// [`Handle::spawn`]: crate::runtime::Handle::spawn
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn_on<Fut>(&mut self, future: Fut, handle: &Handle) -> JoinHandle<Fut::Output>
|
pub fn spawn_on<Fut>(
|
||||||
|
&mut self,
|
||||||
|
future: Fut,
|
||||||
|
handle: &Handle,
|
||||||
|
) -> io::Result<JoinHandle<Fut::Output>>
|
||||||
where
|
where
|
||||||
Fut: Future + Send + 'static,
|
Fut: Future + Send + 'static,
|
||||||
Fut::Output: Send + 'static,
|
Fut::Output: Send + 'static,
|
||||||
{
|
{
|
||||||
handle.spawn_named(future, self.name)
|
Ok(handle.spawn_named(future, self.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns `!Send` a task on the current [`LocalSet`] with this builder's
|
/// Spawns `!Send` a task on the current [`LocalSet`] with this builder's
|
||||||
@@ -122,12 +126,12 @@ impl<'a> Builder<'a> {
|
|||||||
/// [`task::spawn_local`]: crate::task::spawn_local
|
/// [`task::spawn_local`]: crate::task::spawn_local
|
||||||
/// [`LocalSet`]: crate::task::LocalSet
|
/// [`LocalSet`]: crate::task::LocalSet
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn_local<Fut>(self, future: Fut) -> JoinHandle<Fut::Output>
|
pub fn spawn_local<Fut>(self, future: Fut) -> io::Result<JoinHandle<Fut::Output>>
|
||||||
where
|
where
|
||||||
Fut: Future + 'static,
|
Fut: Future + 'static,
|
||||||
Fut::Output: 'static,
|
Fut::Output: 'static,
|
||||||
{
|
{
|
||||||
super::local::spawn_local_inner(future, self.name)
|
Ok(super::local::spawn_local_inner(future, self.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns `!Send` a task on the provided [`LocalSet`] with this builder's
|
/// Spawns `!Send` a task on the provided [`LocalSet`] with this builder's
|
||||||
@@ -138,12 +142,16 @@ impl<'a> Builder<'a> {
|
|||||||
/// [`LocalSet::spawn_local`]: crate::task::LocalSet::spawn_local
|
/// [`LocalSet::spawn_local`]: crate::task::LocalSet::spawn_local
|
||||||
/// [`LocalSet`]: crate::task::LocalSet
|
/// [`LocalSet`]: crate::task::LocalSet
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn_local_on<Fut>(self, future: Fut, local_set: &LocalSet) -> JoinHandle<Fut::Output>
|
pub fn spawn_local_on<Fut>(
|
||||||
|
self,
|
||||||
|
future: Fut,
|
||||||
|
local_set: &LocalSet,
|
||||||
|
) -> io::Result<JoinHandle<Fut::Output>>
|
||||||
where
|
where
|
||||||
Fut: Future + 'static,
|
Fut: Future + 'static,
|
||||||
Fut::Output: 'static,
|
Fut::Output: 'static,
|
||||||
{
|
{
|
||||||
local_set.spawn_named(future, self.name)
|
Ok(local_set.spawn_named(future, self.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns blocking code on the blocking threadpool.
|
/// Spawns blocking code on the blocking threadpool.
|
||||||
@@ -155,7 +163,10 @@ impl<'a> Builder<'a> {
|
|||||||
/// See [`task::spawn_blocking`](crate::task::spawn_blocking)
|
/// See [`task::spawn_blocking`](crate::task::spawn_blocking)
|
||||||
/// for more details.
|
/// for more details.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn_blocking<Function, Output>(self, function: Function) -> JoinHandle<Output>
|
pub fn spawn_blocking<Function, Output>(
|
||||||
|
self,
|
||||||
|
function: Function,
|
||||||
|
) -> io::Result<JoinHandle<Output>>
|
||||||
where
|
where
|
||||||
Function: FnOnce() -> Output + Send + 'static,
|
Function: FnOnce() -> Output + Send + 'static,
|
||||||
Output: Send + 'static,
|
Output: Send + 'static,
|
||||||
@@ -174,18 +185,20 @@ impl<'a> Builder<'a> {
|
|||||||
self,
|
self,
|
||||||
function: Function,
|
function: Function,
|
||||||
handle: &Handle,
|
handle: &Handle,
|
||||||
) -> JoinHandle<Output>
|
) -> io::Result<JoinHandle<Output>>
|
||||||
where
|
where
|
||||||
Function: FnOnce() -> Output + Send + 'static,
|
Function: FnOnce() -> Output + Send + 'static,
|
||||||
Output: Send + 'static,
|
Output: Send + 'static,
|
||||||
{
|
{
|
||||||
use crate::runtime::Mandatory;
|
use crate::runtime::Mandatory;
|
||||||
let (join_handle, _was_spawned) = handle.as_inner().spawn_blocking_inner(
|
let (join_handle, spawn_result) = handle.as_inner().spawn_blocking_inner(
|
||||||
function,
|
function,
|
||||||
Mandatory::NonMandatory,
|
Mandatory::NonMandatory,
|
||||||
self.name,
|
self.name,
|
||||||
handle,
|
handle,
|
||||||
);
|
);
|
||||||
join_handle
|
|
||||||
|
spawn_result?;
|
||||||
|
Ok(join_handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::task::Poll;
|
|||||||
/// runtime *if* the task's coop budget was exhausted.
|
/// runtime *if* the task's coop budget was exhausted.
|
||||||
///
|
///
|
||||||
/// The task will only yield if its entire coop budget has been exhausted.
|
/// The task will only yield if its entire coop budget has been exhausted.
|
||||||
/// This function can can be used in order to insert optional yield points into long
|
/// This function can be used in order to insert optional yield points into long
|
||||||
/// computations that do not use Tokio resources like sockets or semaphores,
|
/// computations that do not use Tokio resources like sockets or semaphores,
|
||||||
/// without redundantly yielding to the runtime each time.
|
/// without redundantly yielding to the runtime each time.
|
||||||
///
|
///
|
||||||
|
|||||||
+14
-11
@@ -101,13 +101,15 @@ impl<T: 'static> JoinSet<T> {
|
|||||||
/// use tokio::task::JoinSet;
|
/// use tokio::task::JoinSet;
|
||||||
///
|
///
|
||||||
/// #[tokio::main]
|
/// #[tokio::main]
|
||||||
/// async fn main() {
|
/// async fn main() -> std::io::Result<()> {
|
||||||
/// let mut set = JoinSet::new();
|
/// let mut set = JoinSet::new();
|
||||||
///
|
///
|
||||||
/// // Use the builder to configure a task's name before spawning it.
|
/// // Use the builder to configure a task's name before spawning it.
|
||||||
/// set.build_task()
|
/// set.build_task()
|
||||||
/// .name("my_task")
|
/// .name("my_task")
|
||||||
/// .spawn(async { /* ... */ });
|
/// .spawn(async { /* ... */ })?;
|
||||||
|
///
|
||||||
|
/// Ok(())
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||||
@@ -377,13 +379,13 @@ impl<'a, T: 'static> Builder<'a, T> {
|
|||||||
///
|
///
|
||||||
/// [`AbortHandle`]: crate::task::AbortHandle
|
/// [`AbortHandle`]: crate::task::AbortHandle
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn<F>(self, future: F) -> AbortHandle
|
pub fn spawn<F>(self, future: F) -> std::io::Result<AbortHandle>
|
||||||
where
|
where
|
||||||
F: Future<Output = T>,
|
F: Future<Output = T>,
|
||||||
F: Send + 'static,
|
F: Send + 'static,
|
||||||
T: Send,
|
T: Send,
|
||||||
{
|
{
|
||||||
self.joinset.insert(self.builder.spawn(future))
|
Ok(self.joinset.insert(self.builder.spawn(future)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn the provided task on the provided [runtime handle] with this
|
/// Spawn the provided task on the provided [runtime handle] with this
|
||||||
@@ -397,13 +399,13 @@ impl<'a, T: 'static> Builder<'a, T> {
|
|||||||
/// [`AbortHandle`]: crate::task::AbortHandle
|
/// [`AbortHandle`]: crate::task::AbortHandle
|
||||||
/// [runtime handle]: crate::runtime::Handle
|
/// [runtime handle]: crate::runtime::Handle
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn_on<F>(mut self, future: F, handle: &Handle) -> AbortHandle
|
pub fn spawn_on<F>(mut self, future: F, handle: &Handle) -> std::io::Result<AbortHandle>
|
||||||
where
|
where
|
||||||
F: Future<Output = T>,
|
F: Future<Output = T>,
|
||||||
F: Send + 'static,
|
F: Send + 'static,
|
||||||
T: Send,
|
T: Send,
|
||||||
{
|
{
|
||||||
self.joinset.insert(self.builder.spawn_on(future, handle))
|
Ok(self.joinset.insert(self.builder.spawn_on(future, handle)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn the provided task on the current [`LocalSet`] with this builder's
|
/// Spawn the provided task on the current [`LocalSet`] with this builder's
|
||||||
@@ -420,12 +422,12 @@ impl<'a, T: 'static> Builder<'a, T> {
|
|||||||
/// [`LocalSet`]: crate::task::LocalSet
|
/// [`LocalSet`]: crate::task::LocalSet
|
||||||
/// [`AbortHandle`]: crate::task::AbortHandle
|
/// [`AbortHandle`]: crate::task::AbortHandle
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn_local<F>(self, future: F) -> AbortHandle
|
pub fn spawn_local<F>(self, future: F) -> std::io::Result<AbortHandle>
|
||||||
where
|
where
|
||||||
F: Future<Output = T>,
|
F: Future<Output = T>,
|
||||||
F: 'static,
|
F: 'static,
|
||||||
{
|
{
|
||||||
self.joinset.insert(self.builder.spawn_local(future))
|
Ok(self.joinset.insert(self.builder.spawn_local(future)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn the provided task on the provided [`LocalSet`] with this builder's
|
/// Spawn the provided task on the provided [`LocalSet`] with this builder's
|
||||||
@@ -438,13 +440,14 @@ impl<'a, T: 'static> Builder<'a, T> {
|
|||||||
/// [`LocalSet`]: crate::task::LocalSet
|
/// [`LocalSet`]: crate::task::LocalSet
|
||||||
/// [`AbortHandle`]: crate::task::AbortHandle
|
/// [`AbortHandle`]: crate::task::AbortHandle
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn_local_on<F>(self, future: F, local_set: &LocalSet) -> AbortHandle
|
pub fn spawn_local_on<F>(self, future: F, local_set: &LocalSet) -> std::io::Result<AbortHandle>
|
||||||
where
|
where
|
||||||
F: Future<Output = T>,
|
F: Future<Output = T>,
|
||||||
F: 'static,
|
F: 'static,
|
||||||
{
|
{
|
||||||
self.joinset
|
Ok(self
|
||||||
.insert(self.builder.spawn_local_on(future, local_set))
|
.joinset
|
||||||
|
.insert(self.builder.spawn_local_on(future, local_set)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+100
-30
@@ -10,6 +10,7 @@ use std::fmt;
|
|||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
|
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
@@ -159,7 +160,7 @@ cfg_rt! {
|
|||||||
/// tokio::task::spawn_local(run_task(new_task));
|
/// tokio::task::spawn_local(run_task(new_task));
|
||||||
/// }
|
/// }
|
||||||
/// // If the while loop returns, then all the LocalSpawner
|
/// // If the while loop returns, then all the LocalSpawner
|
||||||
/// // objects have have been dropped.
|
/// // objects have been dropped.
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
/// // This will return once all senders are dropped and all
|
/// // This will return once all senders are dropped and all
|
||||||
@@ -215,7 +216,7 @@ cfg_rt! {
|
|||||||
tick: Cell<u8>,
|
tick: Cell<u8>,
|
||||||
|
|
||||||
/// State available from thread-local.
|
/// State available from thread-local.
|
||||||
context: Context,
|
context: Rc<Context>,
|
||||||
|
|
||||||
/// This type should not be Send.
|
/// This type should not be Send.
|
||||||
_not_send: PhantomData<*const ()>,
|
_not_send: PhantomData<*const ()>,
|
||||||
@@ -260,7 +261,7 @@ pin_project! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_thread_local!(static CURRENT: Context);
|
thread_local!(static CURRENT: Cell<Option<Rc<Context>>> = Cell::new(None));
|
||||||
|
|
||||||
cfg_rt! {
|
cfg_rt! {
|
||||||
/// Spawns a `!Send` future on the local task set.
|
/// Spawns a `!Send` future on the local task set.
|
||||||
@@ -310,10 +311,12 @@ cfg_rt! {
|
|||||||
F::Output: 'static
|
F::Output: 'static
|
||||||
{
|
{
|
||||||
CURRENT.with(|maybe_cx| {
|
CURRENT.with(|maybe_cx| {
|
||||||
let cx = maybe_cx
|
let ctx = clone_rc(maybe_cx);
|
||||||
.expect("`spawn_local` called from outside of a `task::LocalSet`");
|
match ctx {
|
||||||
|
None => panic!("`spawn_local` called from outside of a `task::LocalSet`"),
|
||||||
|
Some(cx) => cx.spawn(future, name)
|
||||||
|
}
|
||||||
|
|
||||||
cx.spawn(future, name)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,12 +330,29 @@ const MAX_TASKS_PER_TICK: usize = 61;
|
|||||||
/// How often it check the remote queue first.
|
/// How often it check the remote queue first.
|
||||||
const REMOTE_FIRST_INTERVAL: u8 = 31;
|
const REMOTE_FIRST_INTERVAL: u8 = 31;
|
||||||
|
|
||||||
|
/// Context guard for LocalSet
|
||||||
|
pub struct LocalEnterGuard(Option<Rc<Context>>);
|
||||||
|
|
||||||
|
impl Drop for LocalEnterGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
CURRENT.with(|ctx| {
|
||||||
|
ctx.replace(self.0.take());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for LocalEnterGuard {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("LocalEnterGuard").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LocalSet {
|
impl LocalSet {
|
||||||
/// Returns a new local task set.
|
/// Returns a new local task set.
|
||||||
pub fn new() -> LocalSet {
|
pub fn new() -> LocalSet {
|
||||||
LocalSet {
|
LocalSet {
|
||||||
tick: Cell::new(0),
|
tick: Cell::new(0),
|
||||||
context: Context {
|
context: Rc::new(Context {
|
||||||
owned: LocalOwnedTasks::new(),
|
owned: LocalOwnedTasks::new(),
|
||||||
queue: VecDequeCell::with_capacity(INITIAL_CAPACITY),
|
queue: VecDequeCell::with_capacity(INITIAL_CAPACITY),
|
||||||
shared: Arc::new(Shared {
|
shared: Arc::new(Shared {
|
||||||
@@ -342,11 +362,24 @@ impl LocalSet {
|
|||||||
unhandled_panic: crate::runtime::UnhandledPanic::Ignore,
|
unhandled_panic: crate::runtime::UnhandledPanic::Ignore,
|
||||||
}),
|
}),
|
||||||
unhandled_panic: Cell::new(false),
|
unhandled_panic: Cell::new(false),
|
||||||
},
|
}),
|
||||||
_not_send: PhantomData,
|
_not_send: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enters the context of this `LocalSet`.
|
||||||
|
///
|
||||||
|
/// The [`spawn_local`] method will spawn tasks on the `LocalSet` whose
|
||||||
|
/// context you are inside.
|
||||||
|
///
|
||||||
|
/// [`spawn_local`]: fn@crate::task::spawn_local
|
||||||
|
pub fn enter(&self) -> LocalEnterGuard {
|
||||||
|
CURRENT.with(|ctx| {
|
||||||
|
let old = ctx.replace(Some(self.context.clone()));
|
||||||
|
LocalEnterGuard(old)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Spawns a `!Send` task onto the local task set.
|
/// Spawns a `!Send` task onto the local task set.
|
||||||
///
|
///
|
||||||
/// This task is guaranteed to be run on the current thread.
|
/// This task is guaranteed to be run on the current thread.
|
||||||
@@ -454,6 +487,7 @@ impl LocalSet {
|
|||||||
/// [`Runtime::block_on`]: method@crate::runtime::Runtime::block_on
|
/// [`Runtime::block_on`]: method@crate::runtime::Runtime::block_on
|
||||||
/// [in-place blocking]: fn@crate::task::block_in_place
|
/// [in-place blocking]: fn@crate::task::block_in_place
|
||||||
/// [`spawn_blocking`]: fn@crate::task::spawn_blocking
|
/// [`spawn_blocking`]: fn@crate::task::spawn_blocking
|
||||||
|
#[track_caller]
|
||||||
#[cfg(feature = "rt")]
|
#[cfg(feature = "rt")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
|
||||||
pub fn block_on<F>(&self, rt: &crate::runtime::Runtime, future: F) -> F::Output
|
pub fn block_on<F>(&self, rt: &crate::runtime::Runtime, future: F) -> F::Output
|
||||||
@@ -579,7 +613,25 @@ impl LocalSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn with<T>(&self, f: impl FnOnce() -> T) -> T {
|
fn with<T>(&self, f: impl FnOnce() -> T) -> T {
|
||||||
CURRENT.set(&self.context, f)
|
CURRENT.with(|ctx| {
|
||||||
|
struct Reset<'a> {
|
||||||
|
ctx_ref: &'a Cell<Option<Rc<Context>>>,
|
||||||
|
val: Option<Rc<Context>>,
|
||||||
|
}
|
||||||
|
impl<'a> Drop for Reset<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.ctx_ref.replace(self.val.take());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let old = ctx.replace(Some(self.context.clone()));
|
||||||
|
|
||||||
|
let _reset = Reset {
|
||||||
|
ctx_ref: ctx,
|
||||||
|
val: old,
|
||||||
|
};
|
||||||
|
|
||||||
|
f()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,7 +652,7 @@ cfg_unstable! {
|
|||||||
/// * `UnhandledPanic::ShutdownRuntime` will force the `LocalSet` to
|
/// * `UnhandledPanic::ShutdownRuntime` will force the `LocalSet` to
|
||||||
/// shutdown immediately when a spawned task panics even if that
|
/// shutdown immediately when a spawned task panics even if that
|
||||||
/// task's `JoinHandle` has not been dropped. All other spawned tasks
|
/// task's `JoinHandle` has not been dropped. All other spawned tasks
|
||||||
/// will immediatetly terminate and further calls to
|
/// will immediately terminate and further calls to
|
||||||
/// [`LocalSet::block_on`] and [`LocalSet::run_until`] will panic.
|
/// [`LocalSet::block_on`] and [`LocalSet::run_until`] will panic.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@@ -645,8 +697,9 @@ cfg_unstable! {
|
|||||||
/// [`JoinHandle`]: struct@crate::task::JoinHandle
|
/// [`JoinHandle`]: struct@crate::task::JoinHandle
|
||||||
pub fn unhandled_panic(&mut self, behavior: crate::runtime::UnhandledPanic) -> &mut Self {
|
pub fn unhandled_panic(&mut self, behavior: crate::runtime::UnhandledPanic) -> &mut Self {
|
||||||
// TODO: This should be set as a builder
|
// TODO: This should be set as a builder
|
||||||
Arc::get_mut(&mut self.context.shared)
|
Rc::get_mut(&mut self.context)
|
||||||
.expect("TODO: we shouldn't panic")
|
.and_then(|ctx| Arc::get_mut(&mut ctx.shared))
|
||||||
|
.expect("Unhandled Panic behavior modified after starting LocalSet")
|
||||||
.unhandled_panic = behavior;
|
.unhandled_panic = behavior;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -769,23 +822,33 @@ impl<T: Future> Future for RunUntil<'_, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clone_rc<T>(rc: &Cell<Option<Rc<T>>>) -> Option<Rc<T>> {
|
||||||
|
let value = rc.take();
|
||||||
|
let cloned = value.clone();
|
||||||
|
rc.set(value);
|
||||||
|
cloned
|
||||||
|
}
|
||||||
|
|
||||||
impl Shared {
|
impl Shared {
|
||||||
/// Schedule the provided task on the scheduler.
|
/// Schedule the provided task on the scheduler.
|
||||||
fn schedule(&self, task: task::Notified<Arc<Self>>) {
|
fn schedule(&self, task: task::Notified<Arc<Self>>) {
|
||||||
CURRENT.with(|maybe_cx| match maybe_cx {
|
CURRENT.with(|maybe_cx| {
|
||||||
Some(cx) if cx.shared.ptr_eq(self) => {
|
let ctx = clone_rc(maybe_cx);
|
||||||
cx.queue.push_back(task);
|
match ctx {
|
||||||
}
|
Some(cx) if cx.shared.ptr_eq(self) => {
|
||||||
_ => {
|
cx.queue.push_back(task);
|
||||||
// First check whether the queue is still there (if not, the
|
}
|
||||||
// LocalSet is dropped). Then push to it if so, and if not,
|
_ => {
|
||||||
// do nothing.
|
// First check whether the queue is still there (if not, the
|
||||||
let mut lock = self.queue.lock();
|
// LocalSet is dropped). Then push to it if so, and if not,
|
||||||
|
// do nothing.
|
||||||
|
let mut lock = self.queue.lock();
|
||||||
|
|
||||||
if let Some(queue) = lock.as_mut() {
|
if let Some(queue) = lock.as_mut() {
|
||||||
queue.push_back(task);
|
queue.push_back(task);
|
||||||
drop(lock);
|
drop(lock);
|
||||||
self.waker.wake();
|
self.waker.wake();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -799,9 +862,14 @@ impl Shared {
|
|||||||
impl task::Schedule for Arc<Shared> {
|
impl task::Schedule for Arc<Shared> {
|
||||||
fn release(&self, task: &Task<Self>) -> Option<Task<Self>> {
|
fn release(&self, task: &Task<Self>) -> Option<Task<Self>> {
|
||||||
CURRENT.with(|maybe_cx| {
|
CURRENT.with(|maybe_cx| {
|
||||||
let cx = maybe_cx.expect("scheduler context missing");
|
let ctx = clone_rc(maybe_cx);
|
||||||
assert!(cx.shared.ptr_eq(self));
|
match ctx {
|
||||||
cx.owned.remove(task)
|
None => panic!("scheduler context missing"),
|
||||||
|
Some(cx) => {
|
||||||
|
assert!(cx.shared.ptr_eq(self));
|
||||||
|
cx.owned.remove(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -821,13 +889,15 @@ impl task::Schedule for Arc<Shared> {
|
|||||||
// This hook is only called from within the runtime, so
|
// This hook is only called from within the runtime, so
|
||||||
// `CURRENT` should match with `&self`, i.e. there is no
|
// `CURRENT` should match with `&self`, i.e. there is no
|
||||||
// opportunity for a nested scheduler to be called.
|
// opportunity for a nested scheduler to be called.
|
||||||
CURRENT.with(|maybe_cx| match maybe_cx {
|
CURRENT.with(|maybe_cx| {
|
||||||
|
let ctx = clone_rc(maybe_cx);
|
||||||
|
match ctx {
|
||||||
Some(cx) if Arc::ptr_eq(self, &cx.shared) => {
|
Some(cx) if Arc::ptr_eq(self, &cx.shared) => {
|
||||||
cx.unhandled_panic.set(true);
|
cx.unhandled_panic.set(true);
|
||||||
cx.owned.close_and_shutdown_all();
|
cx.owned.close_and_shutdown_all();
|
||||||
}
|
}
|
||||||
_ => unreachable!("runtime core not set in CURRENT thread-local"),
|
_ => unreachable!("runtime core not set in CURRENT thread-local"),
|
||||||
})
|
}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,8 +278,10 @@
|
|||||||
cfg_rt! {
|
cfg_rt! {
|
||||||
pub use crate::runtime::task::{JoinError, JoinHandle};
|
pub use crate::runtime::task::{JoinError, JoinHandle};
|
||||||
|
|
||||||
mod blocking;
|
cfg_not_wasi! {
|
||||||
pub use blocking::spawn_blocking;
|
mod blocking;
|
||||||
|
pub use blocking::spawn_blocking;
|
||||||
|
}
|
||||||
|
|
||||||
mod spawn;
|
mod spawn;
|
||||||
pub use spawn::spawn;
|
pub use spawn::spawn;
|
||||||
@@ -297,7 +299,7 @@ cfg_rt! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod local;
|
mod local;
|
||||||
pub use local::{spawn_local, LocalSet};
|
pub use local::{spawn_local, LocalSet, LocalEnterGuard};
|
||||||
|
|
||||||
mod task_local;
|
mod task_local;
|
||||||
pub use task_local::LocalKey;
|
pub use task_local::LocalKey;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ macro_rules! task_local {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! __task_local_inner {
|
macro_rules! __task_local_inner {
|
||||||
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty) => {
|
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty) => {
|
||||||
|
$(#[$attr])*
|
||||||
$vis static $name: $crate::task::LocalKey<$t> = {
|
$vis static $name: $crate::task::LocalKey<$t> = {
|
||||||
std::thread_local! {
|
std::thread_local! {
|
||||||
static __KEY: std::cell::RefCell<Option<$t>> = const { std::cell::RefCell::new(None) };
|
static __KEY: std::cell::RefCell<Option<$t>> = const { std::cell::RefCell::new(None) };
|
||||||
@@ -66,6 +67,7 @@ macro_rules! __task_local_inner {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! __task_local_inner {
|
macro_rules! __task_local_inner {
|
||||||
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty) => {
|
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty) => {
|
||||||
|
$(#[$attr])*
|
||||||
$vis static $name: $crate::task::LocalKey<$t> = {
|
$vis static $name: $crate::task::LocalKey<$t> = {
|
||||||
std::thread_local! {
|
std::thread_local! {
|
||||||
static __KEY: std::cell::RefCell<Option<$t>> = std::cell::RefCell::new(None);
|
static __KEY: std::cell::RefCell<Option<$t>> = std::cell::RefCell::new(None);
|
||||||
@@ -285,6 +287,7 @@ impl<T: Copy + 'static> LocalKey<T> {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This function will panic if the task local doesn't have a value set.
|
/// This function will panic if the task local doesn't have a value set.
|
||||||
|
#[track_caller]
|
||||||
pub fn get(&'static self) -> T {
|
pub fn get(&'static self) -> T {
|
||||||
self.with(|v| *v)
|
self.with(|v| *v)
|
||||||
}
|
}
|
||||||
@@ -423,6 +426,7 @@ enum ScopeInnerErr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ScopeInnerErr {
|
impl ScopeInnerErr {
|
||||||
|
#[track_caller]
|
||||||
fn panic(&self) -> ! {
|
fn panic(&self) -> ! {
|
||||||
match self {
|
match self {
|
||||||
Self::BorrowError => panic!("cannot enter a task-local scope while the task-local storage is borrowed"),
|
Self::BorrowError => panic!("cannot enter a task-local scope while the task-local storage is borrowed"),
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
//! point than it was originally scheduled for.
|
//! point than it was originally scheduled for.
|
||||||
//!
|
//!
|
||||||
//! This is accomplished by lazily rescheduling timers. That is, we update the
|
//! This is accomplished by lazily rescheduling timers. That is, we update the
|
||||||
//! state field field with the true expiration of the timer from the holder of
|
//! state field with the true expiration of the timer from the holder of
|
||||||
//! the [`TimerEntry`]. When the driver services timers (ie, whenever it's
|
//! the [`TimerEntry`]. When the driver services timers (ie, whenever it's
|
||||||
//! walking lists of timers), it checks this "true when" value, and reschedules
|
//! walking lists of timers), it checks this "true when" value, and reschedules
|
||||||
//! based on it.
|
//! based on it.
|
||||||
@@ -326,7 +326,7 @@ pub(super) type EntryList = crate::util::linked_list::LinkedList<TimerShared, Ti
|
|||||||
///
|
///
|
||||||
/// Note that this structure is located inside the `TimerEntry` structure.
|
/// Note that this structure is located inside the `TimerEntry` structure.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)] // required by `link_list::Link` impl
|
#[repr(C)]
|
||||||
pub(crate) struct TimerShared {
|
pub(crate) struct TimerShared {
|
||||||
/// Data manipulated by the driver thread itself, only.
|
/// Data manipulated by the driver thread itself, only.
|
||||||
driver_state: CachePadded<TimerSharedPadded>,
|
driver_state: CachePadded<TimerSharedPadded>,
|
||||||
@@ -339,6 +339,14 @@ pub(crate) struct TimerShared {
|
|||||||
_p: PhantomPinned,
|
_p: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_addr_of_methods! {
|
||||||
|
impl<> TimerShared {
|
||||||
|
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<TimerShared>> {
|
||||||
|
&self.driver_state.0.pointers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TimerShared {
|
impl TimerShared {
|
||||||
pub(super) fn new() -> Self {
|
pub(super) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -421,7 +429,6 @@ impl TimerShared {
|
|||||||
/// padded. This contains the information that the driver thread accesses most
|
/// padded. This contains the information that the driver thread accesses most
|
||||||
/// frequently to minimize contention. In particular, we move it away from the
|
/// frequently to minimize contention. In particular, we move it away from the
|
||||||
/// waker, as the waker is updated on every poll.
|
/// waker, as the waker is updated on every poll.
|
||||||
#[repr(C)] // required by `link_list::Link` impl
|
|
||||||
struct TimerSharedPadded {
|
struct TimerSharedPadded {
|
||||||
/// A link within the doubly-linked list of timers on a particular level and
|
/// A link within the doubly-linked list of timers on a particular level and
|
||||||
/// slot. Valid only if state is equal to Registered.
|
/// slot. Valid only if state is equal to Registered.
|
||||||
@@ -476,7 +483,7 @@ unsafe impl linked_list::Link for TimerShared {
|
|||||||
unsafe fn pointers(
|
unsafe fn pointers(
|
||||||
target: NonNull<Self::Target>,
|
target: NonNull<Self::Target>,
|
||||||
) -> NonNull<linked_list::Pointers<Self::Target>> {
|
) -> NonNull<linked_list::Pointers<Self::Target>> {
|
||||||
target.cast()
|
TimerShared::addr_of_pointers(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![cfg(not(target_os = "wasi"))]
|
||||||
|
|
||||||
use std::{task::Context, time::Duration};
|
use std::{task::Context, time::Duration};
|
||||||
|
|
||||||
#[cfg(not(loom))]
|
#[cfg(not(loom))]
|
||||||
@@ -289,11 +291,11 @@ fn balanced_incr_and_decr() {
|
|||||||
let incr_inner = inner.clone();
|
let incr_inner = inner.clone();
|
||||||
let decr_inner = inner.clone();
|
let decr_inner = inner.clone();
|
||||||
|
|
||||||
let incr_hndle = thread::spawn(move || incr(incr_inner));
|
let incr_handle = thread::spawn(move || incr(incr_inner));
|
||||||
let decr_hndle = thread::spawn(move || decr(decr_inner));
|
let decr_handle = thread::spawn(move || decr(decr_inner));
|
||||||
|
|
||||||
incr_hndle.join().expect("should never fail");
|
incr_handle.join().expect("should never fail");
|
||||||
decr_hndle.join().expect("should never fail");
|
decr_handle.join().expect("should never fail");
|
||||||
|
|
||||||
assert_eq!(inner.num(Ordering::SeqCst), 0);
|
assert_eq!(inner.num(Ordering::SeqCst), 0);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ pub enum MissedTickBehavior {
|
|||||||
/// // 50ms after the call to `tick` up above. That is, in `tick`, when we
|
/// // 50ms after the call to `tick` up above. That is, in `tick`, when we
|
||||||
/// // recognize that we missed a tick, we schedule the next tick to happen
|
/// // recognize that we missed a tick, we schedule the next tick to happen
|
||||||
/// // 50ms (or whatever the `period` is) from right then, not from when
|
/// // 50ms (or whatever the `period` is) from right then, not from when
|
||||||
/// // were were *supposed* to tick
|
/// // were *supposed* to tick
|
||||||
/// interval.tick().await;
|
/// interval.tick().await;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
|||||||
@@ -88,10 +88,6 @@ enum List {
|
|||||||
/// move it out from this entry to prevent it from getting leaked. (Since the
|
/// move it out from this entry to prevent it from getting leaked. (Since the
|
||||||
/// two linked lists are emptied in the destructor of `IdleNotifiedSet`, the
|
/// two linked lists are emptied in the destructor of `IdleNotifiedSet`, the
|
||||||
/// value should not be leaked.)
|
/// value should not be leaked.)
|
||||||
///
|
|
||||||
/// This type is `#[repr(C)]` because its `linked_list::Link` implementation
|
|
||||||
/// requires that `pointers` is the first field.
|
|
||||||
#[repr(C)]
|
|
||||||
struct ListEntry<T> {
|
struct ListEntry<T> {
|
||||||
/// The linked list pointers of the list this entry is in.
|
/// The linked list pointers of the list this entry is in.
|
||||||
pointers: linked_list::Pointers<ListEntry<T>>,
|
pointers: linked_list::Pointers<ListEntry<T>>,
|
||||||
@@ -105,6 +101,14 @@ struct ListEntry<T> {
|
|||||||
_pin: PhantomPinned,
|
_pin: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_addr_of_methods! {
|
||||||
|
impl<T> ListEntry<T> {
|
||||||
|
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<ListEntry<T>>> {
|
||||||
|
&self.pointers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// With mutable access to the `IdleNotifiedSet`, you can get mutable access to
|
// With mutable access to the `IdleNotifiedSet`, you can get mutable access to
|
||||||
// the values.
|
// the values.
|
||||||
unsafe impl<T: Send> Send for IdleNotifiedSet<T> {}
|
unsafe impl<T: Send> Send for IdleNotifiedSet<T> {}
|
||||||
@@ -453,11 +457,6 @@ unsafe impl<T> linked_list::Link for ListEntry<T> {
|
|||||||
unsafe fn pointers(
|
unsafe fn pointers(
|
||||||
target: NonNull<ListEntry<T>>,
|
target: NonNull<ListEntry<T>>,
|
||||||
) -> NonNull<linked_list::Pointers<ListEntry<T>>> {
|
) -> NonNull<linked_list::Pointers<ListEntry<T>>> {
|
||||||
// Safety: The pointers struct is the first field and ListEntry is
|
ListEntry::addr_of_pointers(target)
|
||||||
// `#[repr(C)]` so this cast is safe.
|
|
||||||
//
|
|
||||||
// We do this rather than doing a field access since `std::ptr::addr_of`
|
|
||||||
// is too new for our MSRV.
|
|
||||||
target.cast()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -631,6 +631,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn run_fuzz(ops: Vec<usize>) {
|
fn run_fuzz(ops: Vec<usize>) {
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
|||||||
@@ -127,70 +127,96 @@ macro_rules! assert_value {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_value!(tokio::fs::DirBuilder: Send & Sync & Unpin);
|
macro_rules! cfg_not_wasi {
|
||||||
assert_value!(tokio::fs::DirEntry: Send & Sync & Unpin);
|
($($item:item)*) => {
|
||||||
assert_value!(tokio::fs::File: Send & Sync & Unpin);
|
$(
|
||||||
assert_value!(tokio::fs::OpenOptions: Send & Sync & Unpin);
|
#[cfg(not(target_os = "wasi"))]
|
||||||
assert_value!(tokio::fs::ReadDir: Send & Sync & Unpin);
|
$item
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async_assert_fn!(tokio::fs::canonicalize(&str): Send & Sync & !Unpin);
|
cfg_not_wasi! {
|
||||||
async_assert_fn!(tokio::fs::copy(&str, &str): Send & Sync & !Unpin);
|
mod fs {
|
||||||
async_assert_fn!(tokio::fs::create_dir(&str): Send & Sync & !Unpin);
|
use super::*;
|
||||||
async_assert_fn!(tokio::fs::create_dir_all(&str): Send & Sync & !Unpin);
|
assert_value!(tokio::fs::DirBuilder: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::fs::hard_link(&str, &str): Send & Sync & !Unpin);
|
assert_value!(tokio::fs::DirEntry: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::fs::metadata(&str): Send & Sync & !Unpin);
|
assert_value!(tokio::fs::File: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::fs::read(&str): Send & Sync & !Unpin);
|
assert_value!(tokio::fs::OpenOptions: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::fs::read_dir(&str): Send & Sync & !Unpin);
|
assert_value!(tokio::fs::ReadDir: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::fs::read_link(&str): Send & Sync & !Unpin);
|
|
||||||
async_assert_fn!(tokio::fs::read_to_string(&str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::canonicalize(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::remove_dir(&str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::copy(&str, &str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::remove_dir_all(&str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::create_dir(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::remove_file(&str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::create_dir_all(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::rename(&str, &str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::hard_link(&str, &str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::set_permissions(&str, std::fs::Permissions): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::metadata(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::symlink_metadata(&str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::read(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::write(&str, Vec<u8>): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::read_dir(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::ReadDir::next_entry(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::read_link(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::OpenOptions::open(_, &str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::read_to_string(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::DirBuilder::create(_, &str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::remove_dir(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::DirEntry::metadata(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::remove_dir_all(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::DirEntry::file_type(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::remove_file(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::File::open(&str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::rename(&str, &str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::File::create(&str): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::set_permissions(&str, std::fs::Permissions): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::File::sync_all(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::symlink_metadata(&str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::File::sync_data(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::write(&str, Vec<u8>): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::File::set_len(_, u64): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::ReadDir::next_entry(_): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::File::metadata(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::OpenOptions::open(_, &str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::File::try_clone(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::DirBuilder::create(_, &str): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::File::into_std(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::DirEntry::metadata(_): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::fs::File::set_permissions(_, std::fs::Permissions): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::fs::DirEntry::file_type(_): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::fs::File::open(&str): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::fs::File::create(&str): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::fs::File::sync_all(_): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::fs::File::sync_data(_): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::fs::File::set_len(_, u64): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::fs::File::metadata(_): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::fs::File::try_clone(_): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::fs::File::into_std(_): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(
|
||||||
|
tokio::fs::File::set_permissions(_, std::fs::Permissions): Send & Sync & !Unpin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_not_wasi! {
|
||||||
|
assert_value!(tokio::net::TcpSocket: Send & Sync & Unpin);
|
||||||
|
async_assert_fn!(tokio::net::TcpListener::bind(SocketAddr): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::net::TcpStream::connect(SocketAddr): Send & Sync & !Unpin);
|
||||||
|
}
|
||||||
|
|
||||||
assert_value!(tokio::net::TcpListener: Send & Sync & Unpin);
|
assert_value!(tokio::net::TcpListener: Send & Sync & Unpin);
|
||||||
assert_value!(tokio::net::TcpSocket: Send & Sync & Unpin);
|
|
||||||
assert_value!(tokio::net::TcpStream: Send & Sync & Unpin);
|
assert_value!(tokio::net::TcpStream: Send & Sync & Unpin);
|
||||||
assert_value!(tokio::net::UdpSocket: Send & Sync & Unpin);
|
|
||||||
assert_value!(tokio::net::tcp::OwnedReadHalf: Send & Sync & Unpin);
|
assert_value!(tokio::net::tcp::OwnedReadHalf: Send & Sync & Unpin);
|
||||||
assert_value!(tokio::net::tcp::OwnedWriteHalf: Send & Sync & Unpin);
|
assert_value!(tokio::net::tcp::OwnedWriteHalf: Send & Sync & Unpin);
|
||||||
assert_value!(tokio::net::tcp::ReadHalf<'_>: Send & Sync & Unpin);
|
assert_value!(tokio::net::tcp::ReadHalf<'_>: Send & Sync & Unpin);
|
||||||
assert_value!(tokio::net::tcp::ReuniteError: Send & Sync & Unpin);
|
assert_value!(tokio::net::tcp::ReuniteError: Send & Sync & Unpin);
|
||||||
assert_value!(tokio::net::tcp::WriteHalf<'_>: Send & Sync & Unpin);
|
assert_value!(tokio::net::tcp::WriteHalf<'_>: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::net::TcpListener::accept(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::TcpListener::accept(_): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::net::TcpListener::bind(SocketAddr): Send & Sync & !Unpin);
|
|
||||||
async_assert_fn!(tokio::net::TcpStream::connect(SocketAddr): Send & Sync & !Unpin);
|
|
||||||
async_assert_fn!(tokio::net::TcpStream::peek(_, &mut [u8]): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::TcpStream::peek(_, &mut [u8]): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::net::TcpStream::readable(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::TcpStream::readable(_): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::net::TcpStream::ready(_, tokio::io::Interest): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::TcpStream::ready(_, tokio::io::Interest): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::net::TcpStream::writable(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::TcpStream::writable(_): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::net::UdpSocket::bind(SocketAddr): Send & Sync & !Unpin);
|
|
||||||
async_assert_fn!(tokio::net::UdpSocket::connect(_, SocketAddr): Send & Sync & !Unpin);
|
// Wasi does not support UDP
|
||||||
async_assert_fn!(tokio::net::UdpSocket::peek_from(_, &mut [u8]): Send & Sync & !Unpin);
|
cfg_not_wasi! {
|
||||||
async_assert_fn!(tokio::net::UdpSocket::readable(_): Send & Sync & !Unpin);
|
mod udp_socket {
|
||||||
async_assert_fn!(tokio::net::UdpSocket::ready(_, tokio::io::Interest): Send & Sync & !Unpin);
|
use super::*;
|
||||||
async_assert_fn!(tokio::net::UdpSocket::recv(_, &mut [u8]): Send & Sync & !Unpin);
|
assert_value!(tokio::net::UdpSocket: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::net::UdpSocket::recv_from(_, &mut [u8]): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::UdpSocket::bind(SocketAddr): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::net::UdpSocket::send(_, &[u8]): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::UdpSocket::connect(_, SocketAddr): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::net::UdpSocket::send_to(_, &[u8], SocketAddr): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::UdpSocket::peek_from(_, &mut [u8]): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::net::UdpSocket::writable(_): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::UdpSocket::readable(_): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::net::UdpSocket::ready(_, tokio::io::Interest): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::net::UdpSocket::recv(_, &mut [u8]): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::net::UdpSocket::recv_from(_, &mut [u8]): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::net::UdpSocket::send(_, &[u8]): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::net::UdpSocket::send_to(_, &[u8], SocketAddr): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::net::UdpSocket::writable(_): Send & Sync & !Unpin);
|
||||||
|
}
|
||||||
|
}
|
||||||
async_assert_fn!(tokio::net::lookup_host(SocketAddr): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::lookup_host(SocketAddr): Send & Sync & !Unpin);
|
||||||
async_assert_fn!(tokio::net::tcp::ReadHalf::peek(_, &mut [u8]): Send & Sync & !Unpin);
|
async_assert_fn!(tokio::net::tcp::ReadHalf::peek(_, &mut [u8]): Send & Sync & !Unpin);
|
||||||
|
|
||||||
@@ -242,16 +268,22 @@ mod windows_named_pipe {
|
|||||||
async_assert_fn!(NamedPipeServer::writable(_): Send & Sync & !Unpin);
|
async_assert_fn!(NamedPipeServer::writable(_): Send & Sync & !Unpin);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_value!(tokio::process::Child: Send & Sync & Unpin);
|
cfg_not_wasi! {
|
||||||
assert_value!(tokio::process::ChildStderr: Send & Sync & Unpin);
|
mod test_process {
|
||||||
assert_value!(tokio::process::ChildStdin: Send & Sync & Unpin);
|
use super::*;
|
||||||
assert_value!(tokio::process::ChildStdout: Send & Sync & Unpin);
|
assert_value!(tokio::process::Child: Send & Sync & Unpin);
|
||||||
assert_value!(tokio::process::Command: Send & Sync & Unpin);
|
assert_value!(tokio::process::ChildStderr: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::process::Child::kill(_): Send & Sync & !Unpin);
|
assert_value!(tokio::process::ChildStdin: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::process::Child::wait(_): Send & Sync & !Unpin);
|
assert_value!(tokio::process::ChildStdout: Send & Sync & Unpin);
|
||||||
async_assert_fn!(tokio::process::Child::wait_with_output(_): Send & Sync & !Unpin);
|
assert_value!(tokio::process::Command: Send & Sync & Unpin);
|
||||||
|
async_assert_fn!(tokio::process::Child::kill(_): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::process::Child::wait(_): Send & Sync & !Unpin);
|
||||||
|
async_assert_fn!(tokio::process::Child::wait_with_output(_): Send & Sync & !Unpin);
|
||||||
|
}
|
||||||
|
|
||||||
|
async_assert_fn!(tokio::signal::ctrl_c(): Send & Sync & !Unpin);
|
||||||
|
}
|
||||||
|
|
||||||
async_assert_fn!(tokio::signal::ctrl_c(): Send & Sync & !Unpin);
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod unix_signal {
|
mod unix_signal {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support bind()
|
||||||
|
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio_test::assert_ok;
|
use tokio_test::assert_ok;
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations
|
||||||
|
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tokio_test::assert_ok;
|
use tokio_test::assert_ok;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations
|
||||||
|
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support directory operations
|
||||||
|
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tokio_test::{assert_err, assert_ok};
|
use tokio_test::{assert_err, assert_ok};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations
|
||||||
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations
|
||||||
|
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support bind()
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::io::{self, copy_bidirectional, AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{self, copy_bidirectional, AsyncReadExt, AsyncWriteExt};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
// Wasi does not support panic recovery or threading
|
||||||
|
#![cfg(all(feature = "full", not(target_os = "wasi")))]
|
||||||
|
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::runtime;
|
use tokio::runtime;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support bind
|
||||||
|
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::runtime;
|
use tokio::runtime;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations
|
||||||
|
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery
|
||||||
|
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::{error::Error, pin::Pin};
|
use std::{error::Error, pin::Pin};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery
|
||||||
|
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
|
use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
|
||||||
use tokio_test::assert_ok;
|
use tokio_test::assert_ok;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery
|
||||||
|
|
||||||
use tokio::io::{split, AsyncRead, AsyncWrite, ReadBuf, ReadHalf, WriteHalf};
|
use tokio::io::{split, AsyncRead, AsyncWrite, ReadBuf, ReadHalf, WriteHalf};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery
|
||||||
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![cfg(feature = "full")]
|
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery
|
||||||
|
|
||||||
struct PanicsOnDrop;
|
struct PanicsOnDrop;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user