mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
44
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a3c803dd1 | ||
|
|
7d8de50482 | ||
|
|
ab0e60ddea | ||
|
|
ac89d8926d | ||
|
|
e184205421 | ||
|
|
44a1aad8df | ||
|
|
75c07770bf | ||
|
|
268ed5e73e | ||
|
|
0c68b89452 | ||
|
|
827694a9e3 | ||
|
|
d15e5fad16 | ||
|
|
9cb495cdb8 | ||
|
|
e7d3e0c93c | ||
|
|
e04b5be1f5 | ||
|
|
2734fa9a85 | ||
|
|
83aeae8610 | ||
|
|
cf550b2183 | ||
|
|
e1c8b5a159 | ||
|
|
eb7a615c96 | ||
|
|
44e9013f64 | ||
|
|
03969cdae7 | ||
|
|
095012b03b | ||
|
|
b5c1fb4012 | ||
|
|
d4848a9e2e | ||
|
|
bb6a292d0a | ||
|
|
eb2106f87e | ||
|
|
fadd0190da | ||
|
|
f0cb360d70 | ||
|
|
d047584e86 | ||
|
|
c5d37204dc | ||
|
|
7f26ad85c2 | ||
|
|
d39c9ed9dc | ||
|
|
44cfe10ee5 | ||
|
|
1073f6e8be | ||
|
|
dee3236c97 | ||
|
|
d9ca1517c6 | ||
|
|
6c1a1d9b07 | ||
|
|
d32acd97eb | ||
|
|
7875f26586 | ||
|
|
7ce8f05cff | ||
|
|
ea19606bc4 | ||
|
|
b9b59e4f15 | ||
|
|
cdc46a9ded | ||
|
|
b9834f6d8b |
@@ -0,0 +1,7 @@
|
||||
# See https://github.com/rustsec/rustsec/blob/59e1d2ad0b9cbc6892c26de233d4925074b4b97b/cargo-audit/audit.toml.example for example.
|
||||
|
||||
[advisories]
|
||||
ignore = [
|
||||
# https://github.com/tokio-rs/tokio/issues/4177
|
||||
"RUSTSEC-2020-0159",
|
||||
]
|
||||
+34
-9
@@ -1,15 +1,45 @@
|
||||
freebsd_instance:
|
||||
image: freebsd-12-2-release-amd64
|
||||
env:
|
||||
RUSTFLAGS: -D warnings
|
||||
|
||||
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
|
||||
# same VM. The binary will be built in 32-bit mode, but will execute on a
|
||||
# 64-bit kernel and in a 64-bit environment. Our tests don't execute any of
|
||||
# the system's binaries, so the environment shouldn't matter.
|
||||
task:
|
||||
name: FreeBSD
|
||||
name: FreeBSD 64-bit
|
||||
setup_script:
|
||||
- pkg install -y bash curl
|
||||
- curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
- sh rustup.sh -y --profile minimal --default-toolchain stable
|
||||
- . $HOME/.cargo/env
|
||||
- |
|
||||
echo "~~~~ rustc --version ~~~~"
|
||||
rustc --version
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all --all-features
|
||||
|
||||
task:
|
||||
name: FreeBSD docs
|
||||
env:
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUSTFLAGS: --cfg docsrs
|
||||
RUSTDOCFLAGS: --cfg docsrs -Dwarnings
|
||||
setup_script:
|
||||
- pkg install -y bash curl
|
||||
- curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
- sh rustup.sh -y --profile minimal --default-toolchain nightly-2021-10-25
|
||||
- . $HOME/.cargo/env
|
||||
- |
|
||||
echo "~~~~ rustc --version ~~~~"
|
||||
rustc --version
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo doc --lib --no-deps --all-features --document-private-items
|
||||
|
||||
task:
|
||||
name: FreeBSD 32-bit
|
||||
setup_script:
|
||||
- pkg install -y bash curl
|
||||
- curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
@@ -21,9 +51,4 @@ task:
|
||||
rustc --version
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all --all-features
|
||||
- cargo doc --all --no-deps
|
||||
i686_test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- |
|
||||
cargo test --all --all-features --target i686-unknown-freebsd
|
||||
- cargo test --all --all-features --target i686-unknown-freebsd
|
||||
|
||||
@@ -9,7 +9,7 @@ name: CI
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
nightly: nightly-2021-07-09
|
||||
nightly: nightly-2021-10-25
|
||||
minrust: 1.45.2
|
||||
|
||||
jobs:
|
||||
@@ -264,8 +264,8 @@ jobs:
|
||||
- name: "rustfmt --check"
|
||||
# Workaround for rust-lang/cargo#7732
|
||||
run: |
|
||||
if ! rustfmt --check --edition 2018 $(find . -name '*.rs' -print); then
|
||||
printf "Please run \`rustfmt --edition 2018 \$(find . -name '*.rs' -print)\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2
|
||||
if ! rustfmt --check --edition 2018 $(git ls-files '*.rs'); then
|
||||
printf "Please run \`rustfmt --edition 2018 \$(git ls-files '*.rs')\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ name: Loom
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
nightly: nightly-2021-07-09
|
||||
|
||||
jobs:
|
||||
loom:
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ command below instead:
|
||||
|
||||
```
|
||||
# Mac or Linux
|
||||
rustfmt --check --edition 2018 $(find . -name '*.rs' -print)
|
||||
rustfmt --check --edition 2018 $(git ls-files '*.rs')
|
||||
|
||||
# Powershell
|
||||
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2018 $_.FullName }
|
||||
|
||||
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.12.0", features = ["full"] }
|
||||
tokio = { version = "1.13.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
httparse = "1.0"
|
||||
time = "0.1"
|
||||
httpdate = "1.0"
|
||||
once_cell = "1.5.2"
|
||||
rand = "0.8.3"
|
||||
|
||||
|
||||
+14
-10
@@ -221,8 +221,9 @@ mod date {
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{self, Write};
|
||||
use std::str;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use time::{self, Duration};
|
||||
use httpdate::HttpDate;
|
||||
|
||||
pub struct Now(());
|
||||
|
||||
@@ -252,22 +253,26 @@ mod date {
|
||||
struct LastRenderedNow {
|
||||
bytes: [u8; 128],
|
||||
amt: usize,
|
||||
next_update: time::Timespec,
|
||||
unix_date: u64,
|
||||
}
|
||||
|
||||
thread_local!(static LAST: RefCell<LastRenderedNow> = RefCell::new(LastRenderedNow {
|
||||
bytes: [0; 128],
|
||||
amt: 0,
|
||||
next_update: time::Timespec::new(0, 0),
|
||||
unix_date: 0,
|
||||
}));
|
||||
|
||||
impl fmt::Display for Now {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
LAST.with(|cache| {
|
||||
let mut cache = cache.borrow_mut();
|
||||
let now = time::get_time();
|
||||
if now >= cache.next_update {
|
||||
cache.update(now);
|
||||
let now = SystemTime::now();
|
||||
let now_unix = now
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map(|since_epoch| since_epoch.as_secs())
|
||||
.unwrap_or(0);
|
||||
if cache.unix_date != now_unix {
|
||||
cache.update(now, now_unix);
|
||||
}
|
||||
f.write_str(cache.buffer())
|
||||
})
|
||||
@@ -279,11 +284,10 @@ mod date {
|
||||
str::from_utf8(&self.bytes[..self.amt]).unwrap()
|
||||
}
|
||||
|
||||
fn update(&mut self, now: time::Timespec) {
|
||||
fn update(&mut self, now: SystemTime, now_unix: u64) {
|
||||
self.amt = 0;
|
||||
write!(LocalBuffer(self), "{}", time::at(now).rfc822()).unwrap();
|
||||
self.next_update = now + Duration::seconds(1);
|
||||
self.next_update.nsec = 0;
|
||||
self.unix_date = now_unix;
|
||||
write!(LocalBuffer(self), "{}", HttpDate::from(now)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,14 @@ async fn missing_return_type() {
|
||||
|
||||
#[tokio::main]
|
||||
async fn extra_semicolon() -> Result<(), ()> {
|
||||
/* TODO(taiki-e): help message still wrong
|
||||
help: try using a variant of the expected enum
|
||||
|
|
||||
23 | Ok(Ok(());)
|
||||
|
|
||||
23 | Err(Ok(());)
|
||||
|
|
||||
*/
|
||||
Ok(());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +1,45 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/macros_type_mismatch.rs:5:7
|
||||
--> $DIR/macros_type_mismatch.rs:5:5
|
||||
|
|
||||
5 | Ok(())
|
||||
| ^^^^ expected `()`, found enum `Result`
|
||||
| ^^^^^^ expected `()`, found enum `Result`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<(), _>`
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
5 | Ok(());
|
||||
| ^
|
||||
| +
|
||||
help: try adding a return type
|
||||
|
|
||||
4 | async fn missing_semicolon_or_return_type() -> Result<(), _> {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
| ++++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/macros_type_mismatch.rs:10:18
|
||||
--> $DIR/macros_type_mismatch.rs:10:5
|
||||
|
|
||||
9 | async fn missing_return_type() {
|
||||
| - help: try adding a return type: `-> Result<(), _>`
|
||||
10 | return Ok(());
|
||||
| ^ expected `()`, found enum `Result`
|
||||
| ^^^^^^^^^^^^^^ expected `()`, found enum `Result`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<(), _>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/macros_type_mismatch.rs:14:31
|
||||
--> $DIR/macros_type_mismatch.rs:23:5
|
||||
|
|
||||
14 | async fn extra_semicolon() -> Result<(), ()> {
|
||||
| --------------- ^^^^^^^^^^^^^^ expected enum `Result`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
| -------------- expected `Result<(), ()>` because of return type
|
||||
...
|
||||
23 | Ok(());
|
||||
| ^^^^^^^ expected enum `Result`, found `()`
|
||||
|
|
||||
= note: expected enum `Result<(), ()>`
|
||||
found unit type `()`
|
||||
help: try using a variant of the expected enum
|
||||
|
|
||||
23 | Ok(Ok(());)
|
||||
|
|
||||
23 | Err(Ok(());)
|
||||
|
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
# 1.5.1 (October 29th, 2021)
|
||||
|
||||
- macros: fix type resolution error in `#[tokio::main]` ([#4176])
|
||||
|
||||
[#4176]: https://github.com/tokio-rs/tokio/pull/4176
|
||||
|
||||
# 1.5.0 (October 13th, 2021)
|
||||
|
||||
- macros: make tokio-macros attributes more IDE friendly ([#4162])
|
||||
|
||||
[#4162]: https://github.com/tokio-rs/tokio/pull/4162
|
||||
|
||||
# 1.4.1 (September 30th, 2021)
|
||||
|
||||
Reverted: run `current_thread` inside `LocalSet` ([#4027])
|
||||
|
||||
# 1.4.0 (September 29th, 2021)
|
||||
|
||||
(yanked)
|
||||
|
||||
### Changed
|
||||
|
||||
- macros: run `current_thread` inside `LocalSet` ([#4027])
|
||||
- macros: explicitly relaxed clippy lint for `.expect()` in runtime entry macro ([#4030])
|
||||
|
||||
### Fixed
|
||||
|
||||
- macros: fix invalid error messages in functions wrapped with `#[main]` or `#[test]` ([#4067])
|
||||
|
||||
[#4027]: https://github.com/tokio-rs/tokio/pull/4027
|
||||
[#4030]: https://github.com/tokio-rs/tokio/pull/4030
|
||||
[#4067]: https://github.com/tokio-rs/tokio/pull/4067
|
||||
|
||||
# 1.3.0 (July 7, 2021)
|
||||
|
||||
- macros: don't trigger `clippy::unwrap_used` ([#3926])
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-macros"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-macros-1.0.x" git tag.
|
||||
version = "1.3.0"
|
||||
version = "1.5.1"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-macros/1.3.0/tokio_macros"
|
||||
documentation = "https://docs.rs/tokio-macros/1.5.1/tokio_macros"
|
||||
description = """
|
||||
Tokio's proc macros.
|
||||
"""
|
||||
|
||||
+78
-38
@@ -1,6 +1,10 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::parse::Parser;
|
||||
|
||||
// syn::AttributeArgs does not implement syn::Parse
|
||||
type AttributeArgs = syn::punctuated::Punctuated<syn::NestedMeta, syn::Token![,]>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum RuntimeFlavor {
|
||||
@@ -27,6 +31,13 @@ struct FinalConfig {
|
||||
start_paused: Option<bool>,
|
||||
}
|
||||
|
||||
/// Config used in case of the attribute not being able to build a valid config
|
||||
const DEFAULT_ERROR_CONFIG: FinalConfig = FinalConfig {
|
||||
flavor: RuntimeFlavor::CurrentThread,
|
||||
worker_threads: None,
|
||||
start_paused: None,
|
||||
};
|
||||
|
||||
struct Configuration {
|
||||
rt_multi_thread_available: bool,
|
||||
default_flavor: RuntimeFlavor,
|
||||
@@ -184,13 +195,13 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_knobs(
|
||||
mut input: syn::ItemFn,
|
||||
args: syn::AttributeArgs,
|
||||
fn build_config(
|
||||
input: syn::ItemFn,
|
||||
args: AttributeArgs,
|
||||
is_test: bool,
|
||||
rt_multi_thread: bool,
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
if input.sig.asyncness.take().is_none() {
|
||||
) -> Result<FinalConfig, syn::Error> {
|
||||
if input.sig.asyncness.is_none() {
|
||||
let msg = "the `async` keyword is missing from the function declaration";
|
||||
return Err(syn::Error::new_spanned(input.sig.fn_token, msg));
|
||||
}
|
||||
@@ -278,7 +289,11 @@ fn parse_knobs(
|
||||
}
|
||||
}
|
||||
|
||||
let config = config.build()?;
|
||||
config.build()
|
||||
}
|
||||
|
||||
fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> TokenStream {
|
||||
input.sig.asyncness = None;
|
||||
|
||||
// If type mismatch occurs, the current rustc points to the last statement.
|
||||
let (last_stmt_start_span, last_stmt_end_span) = {
|
||||
@@ -324,25 +339,28 @@ fn parse_knobs(
|
||||
let body = &input.block;
|
||||
let brace_token = input.block.brace_token;
|
||||
let (tail_return, tail_semicolon) = match body.stmts.last() {
|
||||
Some(syn::Stmt::Semi(expr, _)) => (
|
||||
match expr {
|
||||
syn::Expr::Return(_) => quote! { return },
|
||||
_ => quote! {},
|
||||
Some(syn::Stmt::Semi(expr, _)) => match expr {
|
||||
syn::Expr::Return(_) => (quote! { return }, quote! { ; }),
|
||||
_ => match &input.sig.output {
|
||||
syn::ReturnType::Type(_, ty) if matches!(&**ty, syn::Type::Tuple(ty) if ty.elems.is_empty()) =>
|
||||
{
|
||||
(quote! {}, quote! { ; }) // unit
|
||||
}
|
||||
syn::ReturnType::Default => (quote! {}, quote! { ; }), // unit
|
||||
syn::ReturnType::Type(..) => (quote! {}, quote! {}), // ! or another
|
||||
},
|
||||
quote! {
|
||||
;
|
||||
},
|
||||
),
|
||||
},
|
||||
_ => (quote! {}, quote! {}),
|
||||
};
|
||||
input.block = syn::parse2(quote_spanned! {last_stmt_end_span=>
|
||||
{
|
||||
let body = async #body;
|
||||
#[allow(clippy::expect_used)]
|
||||
#tail_return tokio::task::LocalSet::new().block_on(
|
||||
&#rt.enable_all().build().expect("Failed building the Runtime"),
|
||||
body,
|
||||
)#tail_semicolon
|
||||
#tail_return #rt
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("Failed building the Runtime")
|
||||
.block_on(body)#tail_semicolon
|
||||
}
|
||||
})
|
||||
.expect("Parsing failure");
|
||||
@@ -353,36 +371,58 @@ fn parse_knobs(
|
||||
#input
|
||||
};
|
||||
|
||||
Ok(result.into())
|
||||
result.into()
|
||||
}
|
||||
|
||||
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
|
||||
tokens.extend(TokenStream::from(error.into_compile_error()));
|
||||
tokens
|
||||
}
|
||||
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||
// If any of the steps for this macro fail, we still want to expand to an item that is as close
|
||||
// to the expected output as possible. This helps out IDEs such that completions and other
|
||||
// related features keep working.
|
||||
let input: syn::ItemFn = match syn::parse(item.clone()) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return token_stream_with_error(item, e),
|
||||
};
|
||||
|
||||
if input.sig.ident == "main" && !input.sig.inputs.is_empty() {
|
||||
let config = if input.sig.ident == "main" && !input.sig.inputs.is_empty() {
|
||||
let msg = "the main function cannot accept arguments";
|
||||
return syn::Error::new_spanned(&input.sig.ident, msg)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
Err(syn::Error::new_spanned(&input.sig.ident, msg))
|
||||
} else {
|
||||
AttributeArgs::parse_terminated
|
||||
.parse(args)
|
||||
.and_then(|args| build_config(input.clone(), args, false, rt_multi_thread))
|
||||
};
|
||||
|
||||
parse_knobs(input, args, false, rt_multi_thread).unwrap_or_else(|e| e.to_compile_error().into())
|
||||
match config {
|
||||
Ok(config) => parse_knobs(input, false, config),
|
||||
Err(e) => token_stream_with_error(parse_knobs(input, false, DEFAULT_ERROR_CONFIG), e),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||
// If any of the steps for this macro fail, we still want to expand to an item that is as close
|
||||
// to the expected output as possible. This helps out IDEs such that completions and other
|
||||
// related features keep working.
|
||||
let input: syn::ItemFn = match syn::parse(item.clone()) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return token_stream_with_error(item, e),
|
||||
};
|
||||
let config = if let Some(attr) = input.attrs.iter().find(|attr| attr.path.is_ident("test")) {
|
||||
let msg = "second test attribute is supplied";
|
||||
Err(syn::Error::new_spanned(&attr, msg))
|
||||
} else {
|
||||
AttributeArgs::parse_terminated
|
||||
.parse(args)
|
||||
.and_then(|args| build_config(input.clone(), args, true, rt_multi_thread))
|
||||
};
|
||||
|
||||
for attr in &input.attrs {
|
||||
if attr.path.is_ident("test") {
|
||||
let msg = "second test attribute is supplied";
|
||||
return syn::Error::new_spanned(&attr, msg)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
match config {
|
||||
Ok(config) => parse_knobs(input, true, config),
|
||||
Err(e) => token_stream_with_error(parse_knobs(input, true, DEFAULT_ERROR_CONFIG), e),
|
||||
}
|
||||
|
||||
parse_knobs(input, args, true, rt_multi_thread).unwrap_or_else(|e| e.to_compile_error().into())
|
||||
}
|
||||
|
||||
+25
-33
@@ -27,9 +27,6 @@ use proc_macro::TokenStream;
|
||||
/// helps set up a `Runtime` without requiring the user to use
|
||||
/// [Runtime](../tokio/runtime/struct.Runtime.html) or
|
||||
/// [Builder](../tokio/runtime/struct.Builder.html) directly.
|
||||
/// The function executes in the context of a
|
||||
/// [LocalSet](../tokio/task/struct.LocalSet.html), allowing calls to
|
||||
/// [spawn_local](../tokio/task/fn.spawn_local.html) without further setup.
|
||||
///
|
||||
/// Note: This macro is designed to be simplistic and targets applications that
|
||||
/// do not require a complex setup. If the provided functionality is not
|
||||
@@ -87,14 +84,13 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
/// tokio::runtime::Builder::new_multi_thread()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -113,14 +109,13 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_current_thread()
|
||||
/// tokio::runtime::Builder::new_current_thread()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -137,15 +132,14 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
/// tokio::runtime::Builder::new_multi_thread()
|
||||
/// .worker_threads(2)
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -162,15 +156,14 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_current_thread()
|
||||
/// tokio::runtime::Builder::new_current_thread()
|
||||
/// .enable_all()
|
||||
/// .start_paused(true)
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -211,14 +204,13 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_current_thread()
|
||||
/// tokio::runtime::Builder::new_current_thread()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![cfg(feature = "sync")]
|
||||
|
||||
use tokio::sync::watch;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
@@ -23,13 +23,14 @@ categories = ["asynchronous"]
|
||||
default = []
|
||||
|
||||
# Shorthand for enabling everything
|
||||
full = ["codec", "compat", "io", "time", "net", "rt"]
|
||||
full = ["codec", "compat", "io-util", "time", "net", "rt"]
|
||||
|
||||
net = ["tokio/net"]
|
||||
compat = ["futures-io",]
|
||||
codec = []
|
||||
time = ["tokio/time","slab"]
|
||||
io = []
|
||||
io-util = ["io", "tokio/rt", "tokio/io-util"]
|
||||
rt = ["tokio/rt"]
|
||||
|
||||
__docs_rs = ["futures-util"]
|
||||
|
||||
@@ -38,6 +38,18 @@ macro_rules! cfg_io {
|
||||
}
|
||||
}
|
||||
|
||||
cfg_io! {
|
||||
macro_rules! cfg_io_util {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(feature = "io-util")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_rt {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
|
||||
@@ -106,6 +106,7 @@ where
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(capacity),
|
||||
has_errored: false,
|
||||
},
|
||||
write: WriteFrame::default(),
|
||||
},
|
||||
|
||||
@@ -27,10 +27,12 @@ pin_project! {
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ReadFrame {
|
||||
pub(crate) eof: bool,
|
||||
pub(crate) is_readable: bool,
|
||||
pub(crate) buffer: BytesMut,
|
||||
pub(crate) has_errored: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct WriteFrame {
|
||||
@@ -49,6 +51,7 @@ impl Default for ReadFrame {
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
has_errored: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +75,7 @@ impl From<BytesMut> for ReadFrame {
|
||||
buffer,
|
||||
is_readable: size > 0,
|
||||
eof: false,
|
||||
has_errored: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,30 +130,42 @@ where
|
||||
//
|
||||
// The initial state is `reading`.
|
||||
//
|
||||
// | state | eof | is_readable |
|
||||
// |---------|-------|-------------|
|
||||
// | reading | false | false |
|
||||
// | framing | false | true |
|
||||
// | pausing | true | true |
|
||||
// | paused | true | false |
|
||||
//
|
||||
// `decode_eof`
|
||||
// returns `Some` read 0 bytes
|
||||
// │ │ │ │
|
||||
// │ ▼ │ ▼
|
||||
// ┌───────┐ `decode_eof` ┌──────┐
|
||||
// ┌──read 0 bytes──▶│pausing│─returns `None`─▶│paused│──┐
|
||||
// │ └───────┘ └──────┘ │
|
||||
// pending read┐ │ ┌──────┐ │ ▲ │
|
||||
// │ │ │ │ │ │ │ │
|
||||
// │ ▼ │ │ `decode` returns `Some`│ pending read
|
||||
// │ ╔═══════╗ ┌───────┐◀─┘ │
|
||||
// └──║reading║─read n>0 bytes─▶│framing│ │
|
||||
// ╚═══════╝ └───────┘◀──────read n>0 bytes┘
|
||||
// ▲ │
|
||||
// │ │
|
||||
// └─`decode` returns `None`─┘
|
||||
// | state | eof | is_readable | has_errored |
|
||||
// |---------|-------|-------------|-------------|
|
||||
// | reading | false | false | false |
|
||||
// | framing | false | true | false |
|
||||
// | pausing | true | true | false |
|
||||
// | paused | true | false | false |
|
||||
// | errored | <any> | <any> | true |
|
||||
// `decode_eof` returns Err
|
||||
// ┌────────────────────────────────────────────────────────┐
|
||||
// `decode_eof` returns │ │
|
||||
// `Ok(Some)` │ │
|
||||
// ┌─────┐ │ `decode_eof` returns After returning │
|
||||
// Read 0 bytes ├─────▼──┴┐ `Ok(None)` ┌────────┐ ◄───┐ `None` ┌───▼─────┐
|
||||
// ┌────────────────►│ Pausing ├───────────────────────►│ Paused ├─┐ └───────────┤ Errored │
|
||||
// │ └─────────┘ └─┬──▲───┘ │ └───▲───▲─┘
|
||||
// Pending read │ │ │ │ │ │
|
||||
// ┌──────┐ │ `decode` returns `Some` │ └─────┘ │ │
|
||||
// │ │ │ ┌──────┐ │ Pending │ │
|
||||
// │ ┌────▼──┴─┐ Read n>0 bytes ┌┴──────▼─┐ read n>0 bytes │ read │ │
|
||||
// └─┤ Reading ├───────────────►│ Framing │◄────────────────────────┘ │ │
|
||||
// └──┬─▲────┘ └─────┬──┬┘ │ │
|
||||
// │ │ │ │ `decode` returns Err │ │
|
||||
// │ └───decode` returns `None`──┘ └───────────────────────────────────────────────────────┘ │
|
||||
// │ read returns Err │
|
||||
// └────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
loop {
|
||||
// Return `None` if we have encountered an error from the underlying decoder
|
||||
// See: https://github.com/tokio-rs/tokio/issues/3976
|
||||
if state.has_errored {
|
||||
// preparing has_errored -> paused
|
||||
trace!("Returning None and setting paused");
|
||||
state.is_readable = false;
|
||||
state.has_errored = false;
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
// Repeatedly call `decode` or `decode_eof` while the buffer is "readable",
|
||||
// i.e. it _might_ contain data consumable as a frame or closing frame.
|
||||
// Both signal that there is no such data by returning `None`.
|
||||
@@ -165,7 +181,11 @@ where
|
||||
// pausing or framing
|
||||
if state.eof {
|
||||
// pausing
|
||||
let frame = pinned.codec.decode_eof(&mut state.buffer)?;
|
||||
let frame = pinned.codec.decode_eof(&mut state.buffer).map_err(|err| {
|
||||
trace!("Got an error, going to errored state");
|
||||
state.has_errored = true;
|
||||
err
|
||||
})?;
|
||||
if frame.is_none() {
|
||||
state.is_readable = false; // prepare pausing -> paused
|
||||
}
|
||||
@@ -176,7 +196,11 @@ where
|
||||
// framing
|
||||
trace!("attempting to decode a frame");
|
||||
|
||||
if let Some(frame) = pinned.codec.decode(&mut state.buffer)? {
|
||||
if let Some(frame) = pinned.codec.decode(&mut state.buffer).map_err(|op| {
|
||||
trace!("Got an error, going to errored state");
|
||||
state.has_errored = true;
|
||||
op
|
||||
})? {
|
||||
trace!("frame decoded from buffer");
|
||||
// implicit framing -> framing
|
||||
return Poll::Ready(Some(Ok(frame)));
|
||||
@@ -190,7 +214,13 @@ where
|
||||
// Make sure we've got room for at least one byte to read to ensure
|
||||
// that we don't get a spurious 0 that looks like EOF.
|
||||
state.buffer.reserve(1);
|
||||
let bytect = match poll_read_buf(pinned.inner.as_mut(), cx, &mut state.buffer)? {
|
||||
let bytect = match poll_read_buf(pinned.inner.as_mut(), cx, &mut state.buffer).map_err(
|
||||
|err| {
|
||||
trace!("Got an error, going to errored state");
|
||||
state.has_errored = true;
|
||||
err
|
||||
},
|
||||
)? {
|
||||
Poll::Ready(ct) => ct,
|
||||
// implicit reading -> reading or implicit paused -> paused
|
||||
Poll::Pending => return Poll::Pending,
|
||||
|
||||
@@ -51,6 +51,7 @@ where
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(capacity),
|
||||
has_errored: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
//! Helpers for IO related tasks.
|
||||
//!
|
||||
//! These types are often used in combination with hyper or reqwest, as they
|
||||
//! The stream types are often used in combination with hyper or reqwest, as they
|
||||
//! allow converting between a hyper [`Body`] and [`AsyncRead`].
|
||||
//!
|
||||
//! The [`SyncIoBridge`] type converts from the world of async I/O
|
||||
//! to synchronous I/O; this may often come up when using synchronous APIs
|
||||
//! inside [`tokio::task::spawn_blocking`].
|
||||
//!
|
||||
//! [`Body`]: https://docs.rs/hyper/0.13/hyper/struct.Body.html
|
||||
//! [`AsyncRead`]: tokio::io::AsyncRead
|
||||
|
||||
mod read_buf;
|
||||
mod reader_stream;
|
||||
mod stream_reader;
|
||||
cfg_io_util! {
|
||||
mod sync_bridge;
|
||||
pub use self::sync_bridge::SyncIoBridge;
|
||||
}
|
||||
|
||||
pub use self::read_buf::read_buf;
|
||||
pub use self::reader_stream::ReaderStream;
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
use std::io::{Read, Write};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
|
||||
/// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
|
||||
/// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].
|
||||
#[derive(Debug)]
|
||||
pub struct SyncIoBridge<T> {
|
||||
src: T,
|
||||
rt: tokio::runtime::Handle,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + Unpin> Read for SyncIoBridge<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(AsyncReadExt::read(src, buf))
|
||||
}
|
||||
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.read_to_end(buf))
|
||||
}
|
||||
|
||||
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.read_to_string(buf))
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
|
||||
let src = &mut self.src;
|
||||
// The AsyncRead trait returns the count, synchronous doesn't.
|
||||
let _n = self.rt.block_on(src.read_exact(buf))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite + Unpin> Write for SyncIoBridge<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.write(buf))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.flush())
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.write_all(buf))
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.write_vectored(bufs))
|
||||
}
|
||||
}
|
||||
|
||||
// Because https://doc.rust-lang.org/std/io/trait.Write.html#method.is_write_vectored is at the time
|
||||
// of this writing still unstable, we expose this as part of a standalone method.
|
||||
impl<T: AsyncWrite> SyncIoBridge<T> {
|
||||
/// Determines if the underlying [`tokio::io::AsyncWrite`] target supports efficient vectored writes.
|
||||
///
|
||||
/// See [`tokio::io::AsyncWrite::is_write_vectored`].
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
self.src.is_write_vectored()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Unpin> SyncIoBridge<T> {
|
||||
/// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
|
||||
/// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].
|
||||
///
|
||||
/// When this struct is created, it captures a handle to the current thread's runtime with [`tokio::runtime::Handle::current`].
|
||||
/// It is hence OK to move this struct into a separate thread outside the runtime, as created
|
||||
/// by e.g. [`tokio::task::spawn_blocking`].
|
||||
///
|
||||
/// Stated even more strongly: to make use of this bridge, you *must* move
|
||||
/// it into a separate thread outside the runtime. The synchronous I/O will use the
|
||||
/// underlying handle to block on the backing asynchronous source, via
|
||||
/// [`tokio::runtime::Handle::block_on`]. As noted in the documentation for that
|
||||
/// function, an attempt to `block_on` from an asynchronous execution context
|
||||
/// will panic.
|
||||
///
|
||||
/// # Wrapping `!Unpin` types
|
||||
///
|
||||
/// Use e.g. `SyncIoBridge::new(Box::pin(src))`.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// This will panic if called outside the context of a Tokio runtime.
|
||||
pub fn new(src: T) -> Self {
|
||||
Self::new_with_handle(src, tokio::runtime::Handle::current())
|
||||
}
|
||||
|
||||
/// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
|
||||
/// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].
|
||||
///
|
||||
/// This is the same as [`SyncIoBridge::new`], but allows passing an arbitrary handle and hence may
|
||||
/// be initially invoked outside of an asynchronous context.
|
||||
pub fn new_with_handle(src: T, rt: tokio::runtime::Handle) -> Self {
|
||||
Self { src, rt }
|
||||
}
|
||||
}
|
||||
@@ -498,9 +498,20 @@ impl<T> DelayQueue<T> {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn remove(&mut self, key: &Key) -> Expired<T> {
|
||||
let prev_deadline = self.next_deadline();
|
||||
|
||||
self.remove_key(key);
|
||||
let data = self.slab.remove(key.index);
|
||||
|
||||
let next_deadline = self.next_deadline();
|
||||
if prev_deadline != next_deadline {
|
||||
match (next_deadline, &mut self.delay) {
|
||||
(None, _) => self.delay = None,
|
||||
(Some(deadline), Some(delay)) => delay.as_mut().reset(deadline),
|
||||
(Some(deadline), None) => self.delay = Some(Box::pin(sleep_until(deadline))),
|
||||
}
|
||||
}
|
||||
|
||||
Expired {
|
||||
key: Key::new(key.index),
|
||||
data: data.inner,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
use futures_core::stream::Stream;
|
||||
use std::{io, pin::Pin};
|
||||
use tokio_test::{assert_ready, io::Builder, task};
|
||||
use tokio_util::codec::{BytesCodec, FramedRead};
|
||||
|
||||
macro_rules! pin {
|
||||
($id:ident) => {
|
||||
Pin::new(&mut $id)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_read {
|
||||
($e:expr, $n:expr) => {{
|
||||
let val = assert_ready!($e);
|
||||
assert_eq!(val.unwrap().unwrap(), $n);
|
||||
}};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn return_none_after_error() {
|
||||
let mut io = FramedRead::new(
|
||||
Builder::new()
|
||||
.read(b"abcdef")
|
||||
.read_error(io::Error::new(io::ErrorKind::Other, "Resource errored out"))
|
||||
.read(b"more data")
|
||||
.build(),
|
||||
BytesCodec::new(),
|
||||
);
|
||||
|
||||
let mut task = task::spawn(());
|
||||
|
||||
task.enter(|cx, _| {
|
||||
assert_read!(pin!(io).poll_next(cx), b"abcdef".to_vec());
|
||||
assert!(assert_ready!(pin!(io).poll_next(cx)).unwrap().is_err());
|
||||
assert!(assert_ready!(pin!(io).poll_next(cx)).is_none());
|
||||
assert_read!(pin!(io).poll_next(cx), b"more data".to_vec());
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#![cfg(feature = "io-util")]
|
||||
|
||||
use std::error::Error;
|
||||
use std::io::{Cursor, Read, Result as IoResult};
|
||||
use tokio::io::AsyncRead;
|
||||
use tokio_util::io::SyncIoBridge;
|
||||
|
||||
async fn test_reader_len(
|
||||
r: impl AsyncRead + Unpin + Send + 'static,
|
||||
expected_len: usize,
|
||||
) -> IoResult<()> {
|
||||
let mut r = SyncIoBridge::new(r);
|
||||
let res = tokio::task::spawn_blocking(move || {
|
||||
let mut buf = Vec::new();
|
||||
r.read_to_end(&mut buf)?;
|
||||
Ok::<_, std::io::Error>(buf)
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(res?.len(), expected_len);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_read_to_sync() -> Result<(), Box<dyn Error>> {
|
||||
test_reader_len(tokio::io::empty(), 0).await?;
|
||||
let buf = b"hello world";
|
||||
test_reader_len(Cursor::new(buf), buf.len()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_write_to_sync() -> Result<(), Box<dyn Error>> {
|
||||
let mut dest = Vec::new();
|
||||
let src = b"hello world";
|
||||
let dest = tokio::task::spawn_blocking(move || -> Result<_, String> {
|
||||
let mut w = SyncIoBridge::new(Cursor::new(&mut dest));
|
||||
std::io::copy(&mut Cursor::new(src), &mut w).map_err(|e| e.to_string())?;
|
||||
Ok(dest)
|
||||
})
|
||||
.await??;
|
||||
assert_eq!(dest.as_slice(), src);
|
||||
Ok(())
|
||||
}
|
||||
@@ -630,6 +630,16 @@ async fn insert_in_past_after_poll_fires_immediately() {
|
||||
assert_eq!(entry, "bar");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn delay_queue_poll_expired_when_empty() {
|
||||
let mut delay_queue = task::spawn(DelayQueue::new());
|
||||
let key = delay_queue.insert(0, std::time::Duration::from_secs(10));
|
||||
assert_pending!(poll!(delay_queue));
|
||||
|
||||
delay_queue.remove(&key);
|
||||
assert!(assert_ready!(poll!(delay_queue)).is_none());
|
||||
}
|
||||
|
||||
fn ms(n: u64) -> Duration {
|
||||
Duration::from_millis(n)
|
||||
}
|
||||
|
||||
+65
-4
@@ -1,3 +1,68 @@
|
||||
# 1.13.1 (November 15, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- sync: fix a data race between `oneshot::Sender::send` and awaiting a
|
||||
`oneshot::Receiver` when the oneshot has been closed ([#4226])
|
||||
|
||||
[#4226]: https://github.com/tokio-rs/tokio/pull/4226
|
||||
|
||||
# 1.13.0 (October 29, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- sync: fix `Notify` to clone the waker before locking its waiter list ([#4129])
|
||||
- tokio: add riscv32 to non atomic64 architectures ([#4185])
|
||||
|
||||
### Added
|
||||
|
||||
- net: add `poll_{recv,send}_ready` methods to `udp` and `uds_datagram` ([#4131])
|
||||
- net: add `try_*`, `readable`, `writable`, `ready`, and `peer_addr` methods to split halves ([#4120])
|
||||
- sync: add `blocking_lock` to `Mutex` ([#4130])
|
||||
- sync: add `watch::Sender::send_replace` ([#3962], [#4195])
|
||||
- sync: expand `Debug` for `Mutex<T>` impl to unsized `T` ([#4134])
|
||||
- tracing: instrument time::Sleep ([#4072])
|
||||
- tracing: use structured location fields for spawned tasks ([#4128])
|
||||
|
||||
### Changed
|
||||
|
||||
- io: add assert in `copy_bidirectional` that `poll_write` is sensible ([#4125])
|
||||
- macros: use qualified syntax when polling in `select!` ([#4192])
|
||||
- runtime: handle `block_on` wakeups better ([#4157])
|
||||
- task: allocate callback on heap immediately in debug mode ([#4203])
|
||||
- tokio: assert platform-minimum requirements at build time ([#3797])
|
||||
|
||||
### Documented
|
||||
|
||||
- docs: conversion of doc comments to indicative mood ([#4174])
|
||||
- docs: add returning on the first error example for `try_join!` ([#4133])
|
||||
- docs: fixing broken links in `tokio/src/lib.rs` ([#4132])
|
||||
- signal: add example with background listener ([#4171])
|
||||
- sync: add more oneshot examples ([#4153])
|
||||
- time: document `Interval::tick` cancel safety ([#4152])
|
||||
|
||||
[#3797]: https://github.com/tokio-rs/tokio/pull/3797
|
||||
[#3962]: https://github.com/tokio-rs/tokio/pull/3962
|
||||
[#4072]: https://github.com/tokio-rs/tokio/pull/4072
|
||||
[#4120]: https://github.com/tokio-rs/tokio/pull/4120
|
||||
[#4125]: https://github.com/tokio-rs/tokio/pull/4125
|
||||
[#4128]: https://github.com/tokio-rs/tokio/pull/4128
|
||||
[#4129]: https://github.com/tokio-rs/tokio/pull/4129
|
||||
[#4130]: https://github.com/tokio-rs/tokio/pull/4130
|
||||
[#4131]: https://github.com/tokio-rs/tokio/pull/4131
|
||||
[#4132]: https://github.com/tokio-rs/tokio/pull/4132
|
||||
[#4133]: https://github.com/tokio-rs/tokio/pull/4133
|
||||
[#4134]: https://github.com/tokio-rs/tokio/pull/4134
|
||||
[#4152]: https://github.com/tokio-rs/tokio/pull/4152
|
||||
[#4153]: https://github.com/tokio-rs/tokio/pull/4153
|
||||
[#4157]: https://github.com/tokio-rs/tokio/pull/4157
|
||||
[#4171]: https://github.com/tokio-rs/tokio/pull/4171
|
||||
[#4174]: https://github.com/tokio-rs/tokio/pull/4174
|
||||
[#4185]: https://github.com/tokio-rs/tokio/pull/4185
|
||||
[#4192]: https://github.com/tokio-rs/tokio/pull/4192
|
||||
[#4195]: https://github.com/tokio-rs/tokio/pull/4195
|
||||
[#4203]: https://github.com/tokio-rs/tokio/pull/4203
|
||||
|
||||
# 1.12.0 (September 21, 2021)
|
||||
|
||||
### Fixed
|
||||
@@ -14,10 +79,6 @@
|
||||
- runtime: callback when a worker parks and unparks ([#4070])
|
||||
- sync: implement `try_recv` for mpsc channels ([#4113])
|
||||
|
||||
### Changed
|
||||
|
||||
- macros: run runtime inside `LocalSet` when using macro ([#4027])
|
||||
|
||||
### Documented
|
||||
|
||||
- docs: clarify CPU-bound tasks on Tokio ([#4105])
|
||||
|
||||
+2
-2
@@ -7,12 +7,12 @@ name = "tokio"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v1.0.x" git tag.
|
||||
version = "1.12.0"
|
||||
version = "1.13.1"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/1.12.0/tokio/"
|
||||
documentation = "https://docs.rs/tokio/1.13.1/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.12.0", features = ["full"] }
|
||||
tokio = { version = "1.13.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
+5
-5
@@ -69,14 +69,14 @@ cfg_rt_multi_thread! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the given closure with a cooperative task budget. When the function
|
||||
/// Runs the given closure with a cooperative task budget. When the function
|
||||
/// returns, the budget is reset to the value prior to calling the function.
|
||||
#[inline(always)]
|
||||
pub(crate) fn budget<R>(f: impl FnOnce() -> R) -> R {
|
||||
with_budget(Budget::initial(), f)
|
||||
}
|
||||
|
||||
/// Run the given closure with an unconstrained task budget. When the function returns, the budget
|
||||
/// Runs the given closure with an unconstrained task budget. When the function returns, the budget
|
||||
/// is reset to the value prior to calling the function.
|
||||
#[inline(always)]
|
||||
pub(crate) fn with_unconstrained<R>(f: impl FnOnce() -> R) -> R {
|
||||
@@ -108,7 +108,7 @@ fn with_budget<R>(budget: Budget, f: impl FnOnce() -> R) -> R {
|
||||
}
|
||||
|
||||
cfg_rt_multi_thread! {
|
||||
/// Set the current task's budget
|
||||
/// Sets the current task's budget.
|
||||
pub(crate) fn set(budget: Budget) {
|
||||
CURRENT.with(|cell| cell.set(budget))
|
||||
}
|
||||
@@ -120,7 +120,7 @@ cfg_rt_multi_thread! {
|
||||
}
|
||||
|
||||
cfg_rt! {
|
||||
/// Forcibly remove the budgeting constraints early.
|
||||
/// Forcibly removes the budgeting constraints early.
|
||||
///
|
||||
/// Returns the remaining budget
|
||||
pub(crate) fn stop() -> Budget {
|
||||
@@ -186,7 +186,7 @@ cfg_coop! {
|
||||
}
|
||||
|
||||
impl Budget {
|
||||
/// Decrement the budget. Returns `true` if successful. Decrementing fails
|
||||
/// Decrements the budget. Returns `true` if successful. Decrementing fails
|
||||
/// when there is not enough remaining budget.
|
||||
fn decrement(&mut self) -> bool {
|
||||
if let Some(num) = &mut self.0 {
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::fs::asyncify;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Creates a new, empty directory at the provided path
|
||||
/// Creates a new, empty directory at the provided path.
|
||||
///
|
||||
/// This is an async version of [`std::fs::create_dir`][std]
|
||||
///
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct DirBuilder {
|
||||
/// Indicates whether to create parent directories if they are missing.
|
||||
recursive: bool,
|
||||
|
||||
/// Set the Unix mode for newly created directories.
|
||||
/// Sets the Unix mode for newly created directories.
|
||||
#[cfg(unix)]
|
||||
pub(super) mode: Option<u32>,
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ use std::fs::File as StdFile;
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Read the contents of a file into a buffer
|
||||
/// Read the contents of a file into a buffer:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::File;
|
||||
@@ -383,7 +383,7 @@ impl File {
|
||||
asyncify(move || std.metadata()).await
|
||||
}
|
||||
|
||||
/// Create a new `File` instance that shares the same underlying file handle
|
||||
/// Creates a new `File` instance that shares the same underlying file handle
|
||||
/// as the existing `File` instance. Reads, writes, and seeks will affect both
|
||||
/// File instances simultaneously.
|
||||
///
|
||||
|
||||
@@ -430,7 +430,7 @@ feature! {
|
||||
self
|
||||
}
|
||||
|
||||
/// Pass custom flags to the `flags` argument of `open`.
|
||||
/// Passes custom flags to the `flags` argument of `open`.
|
||||
///
|
||||
/// The bits that define the access mode are masked out with `O_ACCMODE`, to
|
||||
/// ensure they do not interfere with the access mode set by Rusts options.
|
||||
|
||||
@@ -34,7 +34,7 @@ pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
|
||||
Ok(ReadDir(State::Idle(Some(std))))
|
||||
}
|
||||
|
||||
/// Read the the entries in a directory.
|
||||
/// Reads the the entries in a directory.
|
||||
///
|
||||
/// This struct is returned from the [`read_dir`] function of this module and
|
||||
/// will yield instances of [`DirEntry`]. Through a [`DirEntry`] information
|
||||
@@ -287,7 +287,7 @@ impl DirEntry {
|
||||
asyncify(move || std.file_type()).await
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying `std::fs::DirEntry`
|
||||
/// Returns a reference to the underlying `std::fs::DirEntry`.
|
||||
#[cfg(unix)]
|
||||
pub(super) fn as_inner(&self) -> &std::fs::DirEntry {
|
||||
&self.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Definition of the MaybeDone combinator
|
||||
//! Definition of the MaybeDone combinator.
|
||||
|
||||
use std::future::Future;
|
||||
use std::mem;
|
||||
@@ -8,9 +8,9 @@ use std::task::{Context, Poll};
|
||||
/// A future that may have completed.
|
||||
#[derive(Debug)]
|
||||
pub enum MaybeDone<Fut: Future> {
|
||||
/// A not-yet-completed future
|
||||
/// A not-yet-completed future.
|
||||
Future(Fut),
|
||||
/// The output of the completed future
|
||||
/// The output of the completed future.
|
||||
Done(Fut::Output),
|
||||
/// The empty variant after the result of a [`MaybeDone`] has been
|
||||
/// taken using the [`take_output`](MaybeDone::take_output) method.
|
||||
@@ -20,7 +20,7 @@ pub enum MaybeDone<Fut: Future> {
|
||||
// Safe because we never generate `Pin<&mut Fut::Output>`
|
||||
impl<Fut: Future + Unpin> Unpin for MaybeDone<Fut> {}
|
||||
|
||||
/// Wraps a future into a `MaybeDone`
|
||||
/// Wraps a future into a `MaybeDone`.
|
||||
pub fn maybe_done<Fut: Future>(future: Fut) -> MaybeDone<Fut> {
|
||||
MaybeDone::Future(future)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
//! Definition of the `PollFn` adapter combinator
|
||||
//! Definition of the `PollFn` adapter combinator.
|
||||
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
|
||||
@@ -205,13 +205,13 @@ impl<T: AsRawFd> AsyncFd<T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the backing object of this [`AsyncFd`]
|
||||
/// Returns a shared reference to the backing object of this [`AsyncFd`].
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> &T {
|
||||
self.inner.as_ref().unwrap()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the backing object of this [`AsyncFd`]
|
||||
/// Returns a mutable reference to the backing object of this [`AsyncFd`].
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.as_mut().unwrap()
|
||||
|
||||
@@ -16,7 +16,7 @@ use self::State::*;
|
||||
pub(crate) struct Blocking<T> {
|
||||
inner: Option<T>,
|
||||
state: State<T>,
|
||||
/// `true` if the lower IO layer needs flushing
|
||||
/// `true` if the lower IO layer needs flushing.
|
||||
need_flush: bool,
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Repeats operations that are interrupted
|
||||
/// Repeats operations that are interrupted.
|
||||
macro_rules! uninterruptibly {
|
||||
($e:expr) => {{
|
||||
loop {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Use POSIX AIO futures with Tokio
|
||||
//! Use POSIX AIO futures with Tokio.
|
||||
|
||||
use crate::io::driver::{Handle, Interest, ReadyEvent, Registration};
|
||||
use mio::event::Source;
|
||||
@@ -16,14 +16,14 @@ use std::task::{Context, Poll};
|
||||
/// Tokio's consumer must pass an implementor of this trait to create a
|
||||
/// [`Aio`] object.
|
||||
pub trait AioSource {
|
||||
/// Register this AIO event source with Tokio's reactor
|
||||
/// Registers this AIO event source with Tokio's reactor.
|
||||
fn register(&mut self, kq: RawFd, token: usize);
|
||||
|
||||
/// Deregister this AIO event source with Tokio's reactor
|
||||
/// Deregisters this AIO event source with Tokio's reactor.
|
||||
fn deregister(&mut self);
|
||||
}
|
||||
|
||||
/// Wrap the user's AioSource in order to implement mio::event::Source, which
|
||||
/// Wraps the user's AioSource in order to implement mio::event::Source, which
|
||||
/// is what the rest of the crate wants.
|
||||
struct MioSource<T>(T);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::io::driver::Ready;
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
|
||||
/// Readiness event interest
|
||||
/// Readiness event interest.
|
||||
///
|
||||
/// Specifies the readiness events the caller is interested in when awaiting on
|
||||
/// I/O resource readiness states.
|
||||
@@ -17,19 +17,19 @@ impl Interest {
|
||||
// The non-FreeBSD definitions in this block are active only when
|
||||
// building documentation.
|
||||
cfg_aio! {
|
||||
/// Interest for POSIX AIO
|
||||
/// Interest for POSIX AIO.
|
||||
#[cfg(target_os = "freebsd")]
|
||||
pub const AIO: Interest = Interest(mio::Interest::AIO);
|
||||
|
||||
/// Interest for POSIX AIO
|
||||
/// Interest for POSIX AIO.
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
pub const AIO: Interest = Interest(mio::Interest::READABLE);
|
||||
|
||||
/// Interest for POSIX AIO lio_listio events
|
||||
/// Interest for POSIX AIO lio_listio events.
|
||||
#[cfg(target_os = "freebsd")]
|
||||
pub const LIO: Interest = Interest(mio::Interest::LIO);
|
||||
|
||||
/// Interest for POSIX AIO lio_listio events
|
||||
/// Interest for POSIX AIO lio_listio events.
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
pub const LIO: Interest = Interest(mio::Interest::READABLE);
|
||||
}
|
||||
@@ -39,7 +39,7 @@ impl Interest {
|
||||
/// Readable interest includes read-closed events.
|
||||
pub const READABLE: Interest = Interest(mio::Interest::READABLE);
|
||||
|
||||
/// Interest in all writable events
|
||||
/// Interest in all writable events.
|
||||
///
|
||||
/// Writable interest includes write-closed events.
|
||||
pub const WRITABLE: Interest = Interest(mio::Interest::WRITABLE);
|
||||
|
||||
@@ -23,10 +23,10 @@ use std::io;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
/// I/O driver, backed by Mio
|
||||
/// I/O driver, backed by Mio.
|
||||
pub(crate) struct Driver {
|
||||
/// Tracks the number of times `turn` is called. It is safe for this to wrap
|
||||
/// as it is mostly used to determine when to call `compact()`
|
||||
/// as it is mostly used to determine when to call `compact()`.
|
||||
tick: u8,
|
||||
|
||||
/// Reuse the `mio::Events` value across calls to poll.
|
||||
@@ -35,17 +35,17 @@ pub(crate) struct Driver {
|
||||
/// Primary slab handle containing the state for each resource registered
|
||||
/// with this driver. During Drop this is moved into the Inner structure, so
|
||||
/// this is an Option to allow it to be vacated (until Drop this is always
|
||||
/// Some)
|
||||
/// Some).
|
||||
resources: Option<Slab<ScheduledIo>>,
|
||||
|
||||
/// The system event queue
|
||||
/// The system event queue.
|
||||
poll: mio::Poll,
|
||||
|
||||
/// State shared between the reactor and the handles.
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
/// A reference to an I/O driver
|
||||
/// A reference to an I/O driver.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Handle {
|
||||
inner: Weak<Inner>,
|
||||
@@ -66,13 +66,13 @@ pub(super) struct Inner {
|
||||
/// without risking new ones being registered in the meantime.
|
||||
resources: Mutex<Option<Slab<ScheduledIo>>>,
|
||||
|
||||
/// Registers I/O resources
|
||||
/// Registers I/O resources.
|
||||
registry: mio::Registry,
|
||||
|
||||
/// Allocates `ScheduledIo` handles when creating new resources.
|
||||
pub(super) io_dispatch: slab::Allocator<ScheduledIo>,
|
||||
|
||||
/// Used to wake up the reactor from a call to `turn`
|
||||
/// Used to wake up the reactor from a call to `turn`.
|
||||
waker: mio::Waker,
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ impl fmt::Debug for Driver {
|
||||
|
||||
cfg_rt! {
|
||||
impl Handle {
|
||||
/// Returns a handle to the current reactor
|
||||
/// Returns a handle to the current reactor.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@@ -267,7 +267,7 @@ cfg_rt! {
|
||||
|
||||
cfg_not_rt! {
|
||||
impl Handle {
|
||||
/// Returns a handle to the current reactor
|
||||
/// Returns a handle to the current reactor.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
||||
@@ -68,7 +68,7 @@ impl Ready {
|
||||
ready
|
||||
}
|
||||
|
||||
/// Returns true if `Ready` is the empty set
|
||||
/// Returns true if `Ready` is the empty set.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -82,7 +82,7 @@ impl Ready {
|
||||
self == Ready::EMPTY
|
||||
}
|
||||
|
||||
/// Returns `true` if the value includes `readable`
|
||||
/// Returns `true` if the value includes `readable`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -98,7 +98,7 @@ impl Ready {
|
||||
self.contains(Ready::READABLE) || self.is_read_closed()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value includes writable `readiness`
|
||||
/// Returns `true` if the value includes writable `readiness`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -114,7 +114,7 @@ impl Ready {
|
||||
self.contains(Ready::WRITABLE) || self.is_write_closed()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value includes read-closed `readiness`
|
||||
/// Returns `true` if the value includes read-closed `readiness`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -129,7 +129,7 @@ impl Ready {
|
||||
self.contains(Ready::READ_CLOSED)
|
||||
}
|
||||
|
||||
/// Returns `true` if the value includes write-closed `readiness`
|
||||
/// Returns `true` if the value includes write-closed `readiness`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -154,7 +154,7 @@ impl Ready {
|
||||
(self & other) == other
|
||||
}
|
||||
|
||||
/// Create a `Ready` instance using the given `usize` representation.
|
||||
/// Creates a `Ready` instance using the given `usize` representation.
|
||||
///
|
||||
/// The `usize` representation must have been obtained from a call to
|
||||
/// `Readiness::as_usize`.
|
||||
|
||||
@@ -36,16 +36,16 @@ cfg_io_readiness! {
|
||||
#[derive(Debug, Default)]
|
||||
struct Waiters {
|
||||
#[cfg(feature = "net")]
|
||||
/// List of all current waiters
|
||||
/// List of all current waiters.
|
||||
list: WaitList,
|
||||
|
||||
/// Waker used for AsyncRead
|
||||
/// Waker used for AsyncRead.
|
||||
reader: Option<Waker>,
|
||||
|
||||
/// Waker used for AsyncWrite
|
||||
/// Waker used for AsyncWrite.
|
||||
writer: Option<Waker>,
|
||||
|
||||
/// True if this ScheduledIo has been killed due to IO driver shutdown
|
||||
/// True if this ScheduledIo has been killed due to IO driver shutdown.
|
||||
is_shutdown: bool,
|
||||
}
|
||||
|
||||
@@ -54,19 +54,19 @@ cfg_io_readiness! {
|
||||
struct Waiter {
|
||||
pointers: linked_list::Pointers<Waiter>,
|
||||
|
||||
/// The waker for this task
|
||||
/// The waker for this task.
|
||||
waker: Option<Waker>,
|
||||
|
||||
/// The interest this waiter is waiting on
|
||||
/// The interest this waiter is waiting on.
|
||||
interest: Interest,
|
||||
|
||||
is_ready: bool,
|
||||
|
||||
/// Should never be `!Unpin`
|
||||
/// Should never be `!Unpin`.
|
||||
_p: PhantomPinned,
|
||||
}
|
||||
|
||||
/// Future returned by `readiness()`
|
||||
/// Future returned by `readiness()`.
|
||||
struct Readiness<'a> {
|
||||
scheduled_io: &'a ScheduledIo,
|
||||
|
||||
@@ -276,7 +276,7 @@ impl ScheduledIo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll version of checking readiness for a certain direction.
|
||||
/// Polls for readiness events in a given direction.
|
||||
///
|
||||
/// These are to support `AsyncRead` and `AsyncWrite` polling methods,
|
||||
/// which cannot use the `async fn` version. This uses reserved reader
|
||||
@@ -363,7 +363,7 @@ unsafe impl Sync for ScheduledIo {}
|
||||
|
||||
cfg_io_readiness! {
|
||||
impl ScheduledIo {
|
||||
/// An async version of `poll_readiness` which uses a linked list of wakers
|
||||
/// An async version of `poll_readiness` which uses a linked list of wakers.
|
||||
pub(crate) async fn readiness(&self, interest: Interest) -> ReadyEvent {
|
||||
self.readiness_fut(interest).await
|
||||
}
|
||||
|
||||
+1
-1
@@ -218,7 +218,7 @@ cfg_io_driver_impl! {
|
||||
}
|
||||
|
||||
cfg_aio! {
|
||||
/// BSD-specific I/O types
|
||||
/// BSD-specific I/O types.
|
||||
pub mod bsd {
|
||||
mod poll_aio;
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ impl<E: Source> PollEvented<E> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference to the registration
|
||||
/// Returns a reference to the registration.
|
||||
#[cfg(any(
|
||||
feature = "net",
|
||||
all(unix, feature = "process"),
|
||||
@@ -123,7 +123,7 @@ impl<E: Source> PollEvented<E> {
|
||||
&self.registration
|
||||
}
|
||||
|
||||
/// Deregister the inner io from the registration and returns a Result containing the inner io
|
||||
/// Deregisters the inner io from the registration and returns a Result containing the inner io.
|
||||
#[cfg(any(feature = "net", feature = "process"))]
|
||||
pub(crate) fn into_inner(mut self) -> io::Result<E> {
|
||||
let mut inner = self.io.take().unwrap(); // As io shouldn't ever be None, just unwrap here.
|
||||
|
||||
@@ -90,7 +90,7 @@ impl<T> ReadHalf<T> {
|
||||
}
|
||||
|
||||
impl<T> WriteHalf<T> {
|
||||
/// Check if this `WriteHalf` and some `ReadHalf` were split from the same
|
||||
/// Checks if this `WriteHalf` and some `ReadHalf` were split from the same
|
||||
/// stream.
|
||||
pub fn is_pair_of(&self, other: &ReadHalf<T>) -> bool {
|
||||
Arc::ptr_eq(&self.inner, &other.inner)
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::task::{Context, Poll};
|
||||
/// if buffer contents seems to be utf8. Otherwise it only trims buffer down to MAX_BUF.
|
||||
/// That's why, wrapped writer will always receive well-formed utf-8 bytes.
|
||||
/// # Other platforms
|
||||
/// passes data to `inner` as is
|
||||
/// Passes data to `inner` as is.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SplitByUtf8BoundaryIfWindows<W> {
|
||||
inner: W,
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::io::IoSlice;
|
||||
use bytes::Buf;
|
||||
|
||||
cfg_io_util! {
|
||||
/// Defines numeric writer
|
||||
/// Defines numeric writer.
|
||||
macro_rules! write_impl {
|
||||
(
|
||||
$(
|
||||
@@ -256,7 +256,7 @@ cfg_io_util! {
|
||||
write_buf(self, src)
|
||||
}
|
||||
|
||||
/// Attempts to write an entire buffer into this writer
|
||||
/// Attempts to write an entire buffer into this writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
|
||||
@@ -155,7 +155,7 @@ pub(super) enum SeekState {
|
||||
Pending,
|
||||
}
|
||||
|
||||
/// Seek to an offset, in bytes, in the underlying reader.
|
||||
/// Seeks to an offset, in bytes, in the underlying reader.
|
||||
///
|
||||
/// The position used for seeking with `SeekFrom::Current(_)` is the
|
||||
/// position the underlying reader would be at if the `BufReader` had no
|
||||
|
||||
@@ -84,6 +84,14 @@ impl CopyBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
// If pos larger than cap, this loop will never stop.
|
||||
// In particular, user's wrong poll_write implementation returning
|
||||
// incorrect written length may lead to thread blocking.
|
||||
debug_assert!(
|
||||
self.pos <= self.cap,
|
||||
"writer returned length larger than input slice"
|
||||
);
|
||||
|
||||
// If we've written all the data and we've seen EOF, flush out the
|
||||
// data and finish the transfer.
|
||||
if self.pos == self.cap && self.read_done {
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pin_project! {
|
||||
/// Read lines from an [`AsyncBufRead`].
|
||||
/// Reads lines from an [`AsyncBufRead`].
|
||||
///
|
||||
/// A `Lines` can be turned into a `Stream` with [`LinesStream`].
|
||||
///
|
||||
@@ -72,12 +72,12 @@ where
|
||||
poll_fn(|cx| Pin::new(&mut *self).poll_next_line(cx)).await
|
||||
}
|
||||
|
||||
/// Obtain a mutable reference to the underlying reader
|
||||
/// Obtains a mutable reference to the underlying reader.
|
||||
pub fn get_mut(&mut self) -> &mut R {
|
||||
&mut self.reader
|
||||
}
|
||||
|
||||
/// Obtain a reference to the underlying reader
|
||||
/// Obtains a reference to the underlying reader.
|
||||
pub fn get_ref(&mut self) -> &R {
|
||||
&self.reader
|
||||
}
|
||||
|
||||
+19
-2
@@ -16,6 +16,10 @@
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg_hide))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(loom)))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(not(loom))))]
|
||||
#![cfg_attr(docsrs, allow(unused_attributes))]
|
||||
|
||||
//! A runtime for writing reliable network applications without compromising speed.
|
||||
@@ -161,8 +165,8 @@
|
||||
//! [`tokio::runtime`]: crate::runtime
|
||||
//! [`Builder`]: crate::runtime::Builder
|
||||
//! [`Runtime`]: crate::runtime::Runtime
|
||||
//! [rt]: runtime/index.html#basic-scheduler
|
||||
//! [rt-multi-thread]: runtime/index.html#threaded-scheduler
|
||||
//! [rt]: runtime/index.html#current-thread-scheduler
|
||||
//! [rt-multi-thread]: runtime/index.html#multi-thread-scheduler
|
||||
//! [rt-features]: runtime/index.html#runtime-scheduler
|
||||
//!
|
||||
//! ## CPU-bound tasks and blocking code
|
||||
@@ -350,6 +354,19 @@
|
||||
//!
|
||||
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
|
||||
|
||||
// Test that pointer width is compatible. This asserts that e.g. usize is at
|
||||
// least 32 bits, which a lot of components in Tokio currently assumes.
|
||||
//
|
||||
// TODO: improve once we have MSRV access to const eval to make more flexible.
|
||||
#[cfg(not(any(
|
||||
target_pointer_width = "32",
|
||||
target_pointer_width = "64",
|
||||
target_pointer_width = "128"
|
||||
)))]
|
||||
compile_error! {
|
||||
"Tokio requires the platform pointer width to be 32, 64, or 128 bits"
|
||||
}
|
||||
|
||||
// Includes re-exports used by macros.
|
||||
//
|
||||
// This module is not intended to be part of the public API. In general, any
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::{self, MutexGuard, TryLockError};
|
||||
|
||||
/// Adapter for `std::Mutex` that removes the poisoning aspects
|
||||
// from its api
|
||||
/// from its api.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Mutex<T: ?Sized>(sync::Mutex<T>);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ macro_rules! feature {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables enter::block_on
|
||||
/// Enables enter::block_on.
|
||||
macro_rules! cfg_block_on {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
@@ -28,7 +28,7 @@ macro_rules! cfg_block_on {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables internal `AtomicWaker` impl
|
||||
/// Enables internal `AtomicWaker` impl.
|
||||
macro_rules! cfg_atomic_waker_impl {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
@@ -99,6 +99,7 @@ macro_rules! cfg_io_driver_impl {
|
||||
feature = "process",
|
||||
all(unix, feature = "signal"),
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
@@ -422,7 +423,8 @@ macro_rules! cfg_has_atomic_u64 {
|
||||
#[cfg(not(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc"
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32"
|
||||
)))]
|
||||
$item
|
||||
)*
|
||||
@@ -435,7 +437,8 @@ macro_rules! cfg_not_has_atomic_u64 {
|
||||
#[cfg(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc"
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32"
|
||||
))]
|
||||
$item
|
||||
)*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// Wait on multiple concurrent branches, returning when **all** branches
|
||||
/// Waits on multiple concurrent branches, returning when **all** branches
|
||||
/// complete.
|
||||
///
|
||||
/// The `join!` macro must be used inside of async functions, closures, and
|
||||
|
||||
@@ -15,6 +15,11 @@ mod ready;
|
||||
#[macro_use]
|
||||
mod thread_local;
|
||||
|
||||
cfg_trace! {
|
||||
#[macro_use]
|
||||
mod trace;
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
#[cfg(feature = "rt")]
|
||||
pub(crate) mod scoped_tls;
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::loom::thread::LocalKey;
|
||||
use std::cell::Cell;
|
||||
use std::marker;
|
||||
|
||||
/// Set a reference as a thread-local
|
||||
/// Sets a reference as a thread-local.
|
||||
macro_rules! scoped_thread_local {
|
||||
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => (
|
||||
$(#[$attrs])*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// Wait on multiple concurrent branches, returning when the **first** branch
|
||||
/// Waits on multiple concurrent branches, returning when the **first** branch
|
||||
/// completes, cancelling the remaining branches.
|
||||
///
|
||||
/// The `select!` macro must be used inside of async functions, closures, and
|
||||
@@ -502,7 +502,7 @@ macro_rules! select {
|
||||
let mut fut = unsafe { Pin::new_unchecked(fut) };
|
||||
|
||||
// Try polling it
|
||||
let out = match fut.poll(cx) {
|
||||
let out = match Future::poll(fut, cx) {
|
||||
Ready(out) => out,
|
||||
Pending => {
|
||||
// Track that at least one future is
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
cfg_trace! {
|
||||
macro_rules! trace_op {
|
||||
($name:literal, $readiness:literal, $parent:expr) => {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::poll_op",
|
||||
parent: $parent,
|
||||
op_name = $name,
|
||||
is_ready = $readiness
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! trace_poll_op {
|
||||
($name:literal, $poll:expr, $parent:expr $(,)*) => {
|
||||
match $poll {
|
||||
std::task::Poll::Ready(t) => {
|
||||
trace_op!($name, true, $parent);
|
||||
std::task::Poll::Ready(t)
|
||||
}
|
||||
std::task::Poll::Pending => {
|
||||
trace_op!($name, false, $parent);
|
||||
return std::task::Poll::Pending;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/// Wait on multiple concurrent branches, returning when **all** branches
|
||||
/// Waits on multiple concurrent branches, returning when **all** branches
|
||||
/// complete with `Ok(_)` or on the first `Err(_)`.
|
||||
///
|
||||
/// The `try_join!` macro must be used inside of async functions, closures, and
|
||||
@@ -59,6 +59,45 @@
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using `try_join!` with spawned tasks.
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::task::JoinHandle;
|
||||
///
|
||||
/// async fn do_stuff_async() -> Result<(), &'static str> {
|
||||
/// // async work
|
||||
/// # Err("failed")
|
||||
/// }
|
||||
///
|
||||
/// async fn more_async_work() -> Result<(), &'static str> {
|
||||
/// // more here
|
||||
/// # Ok(())
|
||||
/// }
|
||||
///
|
||||
/// async fn flatten<T>(handle: JoinHandle<Result<T, &'static str>>) -> Result<T, &'static str> {
|
||||
/// match handle.await {
|
||||
/// Ok(Ok(result)) => Ok(result),
|
||||
/// Ok(Err(err)) => Err(err),
|
||||
/// Err(err) => Err("handling failed"),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let handle1 = tokio::spawn(do_stuff_async());
|
||||
/// let handle2 = tokio::spawn(more_async_work());
|
||||
/// match tokio::try_join!(flatten(handle1), flatten(handle2)) {
|
||||
/// Ok(val) => {
|
||||
/// // do something with the values
|
||||
/// }
|
||||
/// Err(err) => {
|
||||
/// println!("Failed with {}.", err);
|
||||
/// # assert_eq!(err, "failed");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
macro_rules! try_join {
|
||||
|
||||
@@ -227,7 +227,7 @@ impl TcpListener {
|
||||
Ok(TcpListener { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::TcpListener`] into a [`std::net::TcpListener`].
|
||||
/// Turns a [`tokio::net::TcpListener`] into a [`std::net::TcpListener`].
|
||||
///
|
||||
/// The returned [`std::net::TcpListener`] will have nonblocking mode set as
|
||||
/// `true`. Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! TCP utility types
|
||||
//! TCP utility types.
|
||||
|
||||
pub(crate) mod listener;
|
||||
|
||||
|
||||
+10
-10
@@ -87,7 +87,7 @@ cfg_net! {
|
||||
}
|
||||
|
||||
impl TcpSocket {
|
||||
/// Create a new socket configured for IPv4.
|
||||
/// Creates a new socket configured for IPv4.
|
||||
///
|
||||
/// Calls `socket(2)` with `AF_INET` and `SOCK_STREAM`.
|
||||
///
|
||||
@@ -121,7 +121,7 @@ impl TcpSocket {
|
||||
Ok(TcpSocket { inner })
|
||||
}
|
||||
|
||||
/// Create a new socket configured for IPv6.
|
||||
/// Creates a new socket configured for IPv6.
|
||||
///
|
||||
/// Calls `socket(2)` with `AF_INET6` and `SOCK_STREAM`.
|
||||
///
|
||||
@@ -155,7 +155,7 @@ impl TcpSocket {
|
||||
Ok(TcpSocket { inner })
|
||||
}
|
||||
|
||||
/// Allow the socket to bind to an in-use address.
|
||||
/// Allows the socket to bind to an in-use address.
|
||||
///
|
||||
/// Behavior is platform specific. Refer to the target platform's
|
||||
/// documentation for more details.
|
||||
@@ -185,7 +185,7 @@ impl TcpSocket {
|
||||
self.inner.set_reuseaddr(reuseaddr)
|
||||
}
|
||||
|
||||
/// Retrieves the value set for `SO_REUSEADDR` on this socket
|
||||
/// Retrieves the value set for `SO_REUSEADDR` on this socket.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -211,7 +211,7 @@ impl TcpSocket {
|
||||
self.inner.get_reuseaddr()
|
||||
}
|
||||
|
||||
/// Allow the socket to bind to an in-use port. Only available for unix systems
|
||||
/// Allows the socket to bind to an in-use port. Only available for unix systems
|
||||
/// (excluding Solaris & Illumos).
|
||||
///
|
||||
/// Behavior is platform specific. Refer to the target platform's
|
||||
@@ -245,7 +245,7 @@ impl TcpSocket {
|
||||
self.inner.set_reuseport(reuseport)
|
||||
}
|
||||
|
||||
/// Allow the socket to bind to an in-use port. Only available for unix systems
|
||||
/// Allows the socket to bind to an in-use port. Only available for unix systems
|
||||
/// (excluding Solaris & Illumos).
|
||||
///
|
||||
/// Behavior is platform specific. Refer to the target platform's
|
||||
@@ -348,7 +348,7 @@ impl TcpSocket {
|
||||
self.inner.get_recv_buffer_size()
|
||||
}
|
||||
|
||||
/// Get the local address of this socket.
|
||||
/// Gets the local address of this socket.
|
||||
///
|
||||
/// Will fail on windows if called before `bind`.
|
||||
///
|
||||
@@ -374,7 +374,7 @@ impl TcpSocket {
|
||||
self.inner.get_localaddr()
|
||||
}
|
||||
|
||||
/// Bind the socket to the given address.
|
||||
/// Binds the socket to the given address.
|
||||
///
|
||||
/// This calls the `bind(2)` operating-system function. Behavior is
|
||||
/// platform specific. Refer to the target platform's documentation for more
|
||||
@@ -406,7 +406,7 @@ impl TcpSocket {
|
||||
self.inner.bind(addr)
|
||||
}
|
||||
|
||||
/// Establish a TCP connection with a peer at the specified socket address.
|
||||
/// Establishes a TCP connection with a peer at the specified socket address.
|
||||
///
|
||||
/// The `TcpSocket` is consumed. Once the connection is established, a
|
||||
/// connected [`TcpStream`] is returned. If the connection fails, the
|
||||
@@ -443,7 +443,7 @@ impl TcpSocket {
|
||||
TcpStream::connect_mio(mio).await
|
||||
}
|
||||
|
||||
/// Convert the socket into a `TcpListener`.
|
||||
/// Converts the socket into a `TcpListener`.
|
||||
///
|
||||
/// `backlog` defines the maximum number of pending connections are queued
|
||||
/// by the operating system at any given time. Connection are removed from
|
||||
|
||||
+212
-3
@@ -9,14 +9,18 @@
|
||||
//! level.
|
||||
|
||||
use crate::future::poll_fn;
|
||||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, ReadBuf, Ready};
|
||||
use crate::net::TcpStream;
|
||||
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
cfg_io_util! {
|
||||
use bytes::BufMut;
|
||||
}
|
||||
|
||||
/// Borrowed read half of a [`TcpStream`], created by [`split`].
|
||||
///
|
||||
/// Reading from a `ReadHalf` is usually done using the convenience methods found on the
|
||||
@@ -49,7 +53,7 @@ pub(crate) fn split(stream: &mut TcpStream) -> (ReadHalf<'_>, WriteHalf<'_>) {
|
||||
}
|
||||
|
||||
impl ReadHalf<'_> {
|
||||
/// Attempt to receive data on the socket, without removing that data from
|
||||
/// Attempts to receive data on the socket, without removing that data from
|
||||
/// the queue, registering the current task for wakeup if data is not yet
|
||||
/// available.
|
||||
///
|
||||
@@ -134,6 +138,211 @@ impl ReadHalf<'_> {
|
||||
let mut buf = ReadBuf::new(buf);
|
||||
poll_fn(|cx| self.poll_peek(cx, &mut buf)).await
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.0.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
///
|
||||
/// This function is also equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn readable(&self) -> io::Result<()> {
|
||||
self.0.readable().await
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.try_read(buf)
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
/// written to possibly being only partially filled. This method behaves
|
||||
/// equivalently to a single call to [`try_read()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_vectored()` is non-blocking, the buffer does not have to be
|
||||
/// stored by the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read()
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.try_read_vectored(bufs)
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.0.try_read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the remote address that this stream is connected to.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the local address that this stream is bound to.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteHalf<'_> {
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.0.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn writable(&self) -> io::Result<()> {
|
||||
self.0.writable().await
|
||||
}
|
||||
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
/// only part of the buffer may be written.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.try_write(buf)
|
||||
}
|
||||
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
/// from possible being only partially consumed. This method behaves
|
||||
/// equivalently to a single call to [`try_write()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write_vectored(&self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.try_write_vectored(bufs)
|
||||
}
|
||||
|
||||
/// Returns the remote address that this stream is connected to.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the local address that this stream is bound to.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for ReadHalf<'_> {
|
||||
|
||||
@@ -9,16 +9,20 @@
|
||||
//! level.
|
||||
|
||||
use crate::future::poll_fn;
|
||||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, ReadBuf, Ready};
|
||||
use crate::net::TcpStream;
|
||||
|
||||
use std::error::Error;
|
||||
use std::net::Shutdown;
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{fmt, io};
|
||||
|
||||
cfg_io_util! {
|
||||
use bytes::BufMut;
|
||||
}
|
||||
|
||||
/// Owned read half of a [`TcpStream`], created by [`into_split`].
|
||||
///
|
||||
/// Reading from an `OwnedReadHalf` is usually done using the convenience methods found
|
||||
@@ -189,6 +193,128 @@ impl OwnedReadHalf {
|
||||
let mut buf = ReadBuf::new(buf);
|
||||
poll_fn(|cx| self.poll_peek(cx, &mut buf)).await
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.inner.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
///
|
||||
/// This function is also equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn readable(&self) -> io::Result<()> {
|
||||
self.inner.readable().await
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.try_read(buf)
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
/// written to possibly being only partially filled. This method behaves
|
||||
/// equivalently to a single call to [`try_read()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_vectored()` is non-blocking, the buffer does not have to be
|
||||
/// stored by the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read()
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.inner.try_read_vectored(bufs)
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.inner.try_read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the remote address that this stream is connected to.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the local address that this stream is bound to.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for OwnedReadHalf {
|
||||
@@ -211,13 +337,94 @@ impl OwnedWriteHalf {
|
||||
reunite(other, self)
|
||||
}
|
||||
|
||||
/// Destroy the write half, but don't close the write half of the stream
|
||||
/// Destroys the write half, but don't close the write half of the stream
|
||||
/// until the read half is dropped. If the read half has already been
|
||||
/// dropped, this closes the stream.
|
||||
pub fn forget(mut self) {
|
||||
self.shutdown_on_drop = false;
|
||||
drop(self);
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.inner.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn writable(&self) -> io::Result<()> {
|
||||
self.inner.writable().await
|
||||
}
|
||||
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
/// only part of the buffer may be written.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.try_write(buf)
|
||||
}
|
||||
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
/// from possible being only partially consumed. This method behaves
|
||||
/// equivalently to a single call to [`try_write()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write_vectored(&self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.inner.try_write_vectored(bufs)
|
||||
}
|
||||
|
||||
/// Returns the remote address that this stream is connected to.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the local address that this stream is bound to.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OwnedWriteHalf {
|
||||
|
||||
@@ -192,7 +192,7 @@ impl TcpStream {
|
||||
Ok(TcpStream { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::TcpStream`] into a [`std::net::TcpStream`].
|
||||
/// Turns a [`tokio::net::TcpStream`] into a [`std::net::TcpStream`].
|
||||
///
|
||||
/// The returned [`std::net::TcpStream`] will have nonblocking mode set as `true`.
|
||||
/// Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
@@ -350,7 +350,7 @@ impl TcpStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
@@ -422,7 +422,7 @@ impl TcpStream {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the socket to become readable.
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
@@ -510,7 +510,7 @@ impl TcpStream {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to read data from the stream into the provided buffer, returning how
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
@@ -577,7 +577,7 @@ impl TcpStream {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read(buf))
|
||||
}
|
||||
|
||||
/// Try to read data from the stream into the provided buffers, returning
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
@@ -656,7 +656,7 @@ impl TcpStream {
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Try to read data from the stream into the provided buffer, advancing the
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
@@ -734,7 +734,7 @@ impl TcpStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the socket to become writable.
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
@@ -874,7 +874,7 @@ impl TcpStream {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write(buf))
|
||||
}
|
||||
|
||||
/// Try to write several buffers to the stream, returning how many bytes
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
@@ -936,7 +936,7 @@ impl TcpStream {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(bufs))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
|
||||
+78
-12
@@ -12,7 +12,7 @@ cfg_io_util! {
|
||||
}
|
||||
|
||||
cfg_net! {
|
||||
/// A UDP socket
|
||||
/// A UDP socket.
|
||||
///
|
||||
/// UDP is "connectionless", unlike TCP. Meaning, regardless of what address you've bound to, a `UdpSocket`
|
||||
/// is free to communicate with many different remotes. In tokio there are basically two main ways to use `UdpSocket`:
|
||||
@@ -211,7 +211,7 @@ impl UdpSocket {
|
||||
UdpSocket::new(io)
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::UdpSocket`] into a [`std::net::UdpSocket`].
|
||||
/// Turns a [`tokio::net::UdpSocket`] into a [`std::net::UdpSocket`].
|
||||
///
|
||||
/// The returned [`std::net::UdpSocket`] will have nonblocking mode set as
|
||||
/// `true`. Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
@@ -317,7 +317,7 @@ impl UdpSocket {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_recv()` or `try_send()`. It
|
||||
/// can be used to concurrently recv / send to the same socket on a single
|
||||
@@ -388,7 +388,7 @@ impl UdpSocket {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the socket to become writable.
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is
|
||||
/// usually paired with `try_send()` or `try_send_to()`.
|
||||
@@ -443,6 +443,39 @@ impl UdpSocket {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Polls for write/send readiness.
|
||||
///
|
||||
/// If the udp stream is not currently ready for sending, this method will
|
||||
/// store a clone of the `Waker` from the provided `Context`. When the udp
|
||||
/// stream becomes ready for sending, `Waker::wake` will be called on the
|
||||
/// waker.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_send_ready` or `poll_send`, only
|
||||
/// the `Waker` from the `Context` passed to the most recent call is
|
||||
/// scheduled to receive a wakeup. (However, `poll_recv_ready` retains a
|
||||
/// second, independent waker.)
|
||||
///
|
||||
/// This function is intended for cases where creating and pinning a future
|
||||
/// via [`writable`] is not feasible. Where possible, using [`writable`] is
|
||||
/// preferred, as this supports polling from multiple tasks at once.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
///
|
||||
/// * `Poll::Pending` if the udp stream is not ready for writing.
|
||||
/// * `Poll::Ready(Ok(()))` if the udp stream is ready for writing.
|
||||
/// * `Poll::Ready(Err(e))` if an error is encountered.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`writable`]: method@Self::writable
|
||||
pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the remote address that the socket is
|
||||
/// connected to.
|
||||
///
|
||||
@@ -516,7 +549,7 @@ impl UdpSocket {
|
||||
.poll_write_io(cx, || self.io.send(buf))
|
||||
}
|
||||
|
||||
/// Try to send data on the socket to the remote address to which it is
|
||||
/// Tries to send data on the socket to the remote address to which it is
|
||||
/// connected.
|
||||
///
|
||||
/// When the socket buffer is full, `Err(io::ErrorKind::WouldBlock)` is
|
||||
@@ -570,7 +603,7 @@ impl UdpSocket {
|
||||
.try_io(Interest::WRITABLE, || self.io.send(buf))
|
||||
}
|
||||
|
||||
/// Wait for the socket to become readable.
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_recv()`.
|
||||
@@ -630,6 +663,39 @@ impl UdpSocket {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Polls for read/receive readiness.
|
||||
///
|
||||
/// If the udp stream is not currently ready for receiving, this method will
|
||||
/// store a clone of the `Waker` from the provided `Context`. When the udp
|
||||
/// socket becomes ready for reading, `Waker::wake` will be called on the
|
||||
/// waker.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_recv_ready`, `poll_recv` or
|
||||
/// `poll_peek`, only the `Waker` from the `Context` passed to the most
|
||||
/// recent call is scheduled to receive a wakeup. (However,
|
||||
/// `poll_send_ready` retains a second, independent waker.)
|
||||
///
|
||||
/// This function is intended for cases where creating and pinning a future
|
||||
/// via [`readable`] is not feasible. Where possible, using [`readable`] is
|
||||
/// preferred, as this supports polling from multiple tasks at once.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
///
|
||||
/// * `Poll::Pending` if the udp stream is not ready for reading.
|
||||
/// * `Poll::Ready(Ok(()))` if the udp stream is ready for reading.
|
||||
/// * `Poll::Ready(Err(e))` if an error is encountered.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`readable`]: method@Self::readable
|
||||
pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Receives a single datagram message on the socket from the remote address
|
||||
/// to which it is connected. On success, returns the number of bytes read.
|
||||
///
|
||||
@@ -715,7 +781,7 @@ impl UdpSocket {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Try to receive a single datagram message on the socket from the remote
|
||||
/// Tries to receive a single datagram message on the socket from the remote
|
||||
/// address to which it is connected. On success, returns the number of
|
||||
/// bytes read.
|
||||
///
|
||||
@@ -772,7 +838,7 @@ impl UdpSocket {
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Try to receive data from the stream into the provided buffer, advancing the
|
||||
/// Tries to receive data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
@@ -837,7 +903,7 @@ impl UdpSocket {
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to receive a single datagram message on the socket. On success,
|
||||
/// Tries to receive a single datagram message on the socket. On success,
|
||||
/// returns the number of bytes read and the origin.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
@@ -978,7 +1044,7 @@ impl UdpSocket {
|
||||
.poll_write_io(cx, || self.io.send_to(buf, target))
|
||||
}
|
||||
|
||||
/// Try to send data on the socket to the given address, but if the send is
|
||||
/// Tries to send data on the socket to the given address, but if the send is
|
||||
/// blocked this will return right away.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
@@ -1116,7 +1182,7 @@ impl UdpSocket {
|
||||
Poll::Ready(Ok(addr))
|
||||
}
|
||||
|
||||
/// Try to receive a single datagram message on the socket. On success,
|
||||
/// Tries to receive a single datagram message on the socket. On success,
|
||||
/// returns the number of bytes read and the origin.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
@@ -1170,7 +1236,7 @@ impl UdpSocket {
|
||||
.try_io(Interest::READABLE, || self.io.recv_from(buf))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
|
||||
@@ -96,7 +96,7 @@ cfg_net_unix! {
|
||||
}
|
||||
|
||||
impl UnixDatagram {
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_recv()` or `try_send()`. It
|
||||
/// can be used to concurrently recv / send to the same socket on a single
|
||||
@@ -169,7 +169,7 @@ impl UnixDatagram {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the socket to become writable.
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is
|
||||
/// usually paired with `try_send()` or `try_send_to()`.
|
||||
@@ -226,7 +226,40 @@ impl UnixDatagram {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Wait for the socket to become readable.
|
||||
/// Polls for write/send readiness.
|
||||
///
|
||||
/// If the socket is not currently ready for sending, this method will
|
||||
/// store a clone of the `Waker` from the provided `Context`. When the socket
|
||||
/// becomes ready for sending, `Waker::wake` will be called on the
|
||||
/// waker.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_send_ready` or `poll_send`, only
|
||||
/// the `Waker` from the `Context` passed to the most recent call is
|
||||
/// scheduled to receive a wakeup. (However, `poll_recv_ready` retains a
|
||||
/// second, independent waker.)
|
||||
///
|
||||
/// This function is intended for cases where creating and pinning a future
|
||||
/// via [`writable`] is not feasible. Where possible, using [`writable`] is
|
||||
/// preferred, as this supports polling from multiple tasks at once.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
///
|
||||
/// * `Poll::Pending` if the socket is not ready for writing.
|
||||
/// * `Poll::Ready(Ok(()))` if the socket is ready for writing.
|
||||
/// * `Poll::Ready(Err(e))` if an error is encountered.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`writable`]: method@Self::writable
|
||||
pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_recv()`.
|
||||
@@ -289,6 +322,39 @@ impl UnixDatagram {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Polls for read/receive readiness.
|
||||
///
|
||||
/// If the socket is not currently ready for receiving, this method will
|
||||
/// store a clone of the `Waker` from the provided `Context`. When the
|
||||
/// socket becomes ready for reading, `Waker::wake` will be called on the
|
||||
/// waker.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_recv_ready`, `poll_recv` or
|
||||
/// `poll_peek`, only the `Waker` from the `Context` passed to the most
|
||||
/// recent call is scheduled to receive a wakeup. (However,
|
||||
/// `poll_send_ready` retains a second, independent waker.)
|
||||
///
|
||||
/// This function is intended for cases where creating and pinning a future
|
||||
/// via [`readable`] is not feasible. Where possible, using [`readable`] is
|
||||
/// preferred, as this supports polling from multiple tasks at once.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
///
|
||||
/// * `Poll::Pending` if the socket is not ready for reading.
|
||||
/// * `Poll::Ready(Ok(()))` if the socket is ready for reading.
|
||||
/// * `Poll::Ready(Err(e))` if an error is encountered.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`readable`]: method@Self::readable
|
||||
pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Creates a new `UnixDatagram` bound to the specified path.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -397,7 +463,7 @@ impl UnixDatagram {
|
||||
Ok(UnixDatagram { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::UnixDatagram`] into a [`std::os::unix::net::UnixDatagram`].
|
||||
/// Turns a [`tokio::net::UnixDatagram`] into a [`std::os::unix::net::UnixDatagram`].
|
||||
///
|
||||
/// The returned [`std::os::unix::net::UnixDatagram`] will have nonblocking
|
||||
/// mode set as `true`. Use [`set_nonblocking`] to change the blocking mode
|
||||
@@ -548,7 +614,7 @@ impl UnixDatagram {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Try to send a datagram to the peer without waiting.
|
||||
/// Tries to send a datagram to the peer without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -592,7 +658,7 @@ impl UnixDatagram {
|
||||
.try_io(Interest::WRITABLE, || self.io.send(buf))
|
||||
}
|
||||
|
||||
/// Try to send a datagram to the peer without waiting.
|
||||
/// Tries to send a datagram to the peer without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -678,7 +744,7 @@ impl UnixDatagram {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Try to receive a datagram from the peer without waiting.
|
||||
/// Tries to receive a datagram from the peer without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -729,7 +795,7 @@ impl UnixDatagram {
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Try to receive data from the socket without waiting.
|
||||
/// Tries to receive data from the socket without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -790,7 +856,7 @@ impl UnixDatagram {
|
||||
Ok((n, SocketAddr(addr)))
|
||||
}
|
||||
|
||||
/// Try to read data from the stream into the provided buffer, advancing the
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -1091,7 +1157,7 @@ impl UnixDatagram {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Try to receive data from the socket without waiting.
|
||||
/// Tries to receive data from the socket without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1143,7 +1209,7 @@ impl UnixDatagram {
|
||||
Ok((n, SocketAddr(addr)))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
|
||||
@@ -88,7 +88,7 @@ impl UnixListener {
|
||||
Ok(UnixListener { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::UnixListener`] into a [`std::os::unix::net::UnixListener`].
|
||||
/// Turns a [`tokio::net::UnixListener`] into a [`std::os::unix::net::UnixListener`].
|
||||
///
|
||||
/// The returned [`std::os::unix::net::UnixListener`] will have nonblocking mode
|
||||
/// set as `true`. Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Unix domain socket utility types
|
||||
//! Unix domain socket utility types.
|
||||
|
||||
// This module does not currently provide any public API, but it was
|
||||
// unintentionally defined as a public module. Hide it from the documentation
|
||||
|
||||
+206
-1
@@ -8,14 +8,19 @@
|
||||
//! split has no associated overhead and enforces all invariants at the type
|
||||
//! level.
|
||||
|
||||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, ReadBuf, Ready};
|
||||
use crate::net::UnixStream;
|
||||
|
||||
use crate::net::unix::SocketAddr;
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
cfg_io_util! {
|
||||
use bytes::BufMut;
|
||||
}
|
||||
|
||||
/// Borrowed read half of a [`UnixStream`], created by [`split`].
|
||||
///
|
||||
/// Reading from a `ReadHalf` is usually done using the convenience methods found on the
|
||||
@@ -47,6 +52,206 @@ pub(crate) fn split(stream: &mut UnixStream) -> (ReadHalf<'_>, WriteHalf<'_>) {
|
||||
(ReadHalf(stream), WriteHalf(stream))
|
||||
}
|
||||
|
||||
impl ReadHalf<'_> {
|
||||
/// Wait for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.0.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn readable(&self) -> io::Result<()> {
|
||||
self.0.readable().await
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.try_read(buf)
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
pub fn try_read_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.0.try_read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
/// written to possibly being only partially filled. This method behaves
|
||||
/// equivalently to a single call to [`try_read()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_vectored()` is non-blocking, the buffer does not have to be
|
||||
/// stored by the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read()
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.try_read_vectored(bufs)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteHalf<'_> {
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.0.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn writable(&self) -> io::Result<()> {
|
||||
self.0.writable().await
|
||||
}
|
||||
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
/// only part of the buffer may be written.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.try_write(buf)
|
||||
}
|
||||
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
/// from possible being only partially consumed. This method behaves
|
||||
/// equivalently to a single call to [`try_write()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write_vectored(&self, buf: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.try_write_vectored(buf)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for ReadHalf<'_> {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
//! split has no associated overhead and enforces all invariants at the type
|
||||
//! level.
|
||||
|
||||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, ReadBuf, Ready};
|
||||
use crate::net::UnixStream;
|
||||
|
||||
use crate::net::unix::SocketAddr;
|
||||
use std::error::Error;
|
||||
use std::net::Shutdown;
|
||||
use std::pin::Pin;
|
||||
@@ -18,6 +19,10 @@ use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{fmt, io};
|
||||
|
||||
cfg_io_util! {
|
||||
use bytes::BufMut;
|
||||
}
|
||||
|
||||
/// Owned read half of a [`UnixStream`], created by [`into_split`].
|
||||
///
|
||||
/// Reading from an `OwnedReadHalf` is usually done using the convenience methods found
|
||||
@@ -102,6 +107,124 @@ impl OwnedReadHalf {
|
||||
pub fn reunite(self, other: OwnedWriteHalf) -> Result<UnixStream, ReuniteError> {
|
||||
reunite(self, other)
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.inner.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn readable(&self) -> io::Result<()> {
|
||||
self.inner.readable().await
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.try_read(buf)
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.inner.try_read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
/// written to possibly being only partially filled. This method behaves
|
||||
/// equivalently to a single call to [`try_read()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_vectored()` is non-blocking, the buffer does not have to be
|
||||
/// stored by the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read()
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.inner.try_read_vectored(bufs)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for OwnedReadHalf {
|
||||
@@ -124,13 +247,92 @@ impl OwnedWriteHalf {
|
||||
reunite(other, self)
|
||||
}
|
||||
|
||||
/// Destroy the write half, but don't close the write half of the stream
|
||||
/// Destroys the write half, but don't close the write half of the stream
|
||||
/// until the read half is dropped. If the read half has already been
|
||||
/// dropped, this closes the stream.
|
||||
pub fn forget(mut self) {
|
||||
self.shutdown_on_drop = false;
|
||||
drop(self);
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.inner.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn writable(&self) -> io::Result<()> {
|
||||
self.inner.writable().await
|
||||
}
|
||||
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
/// only part of the buffer may be written.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.try_write(buf)
|
||||
}
|
||||
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
/// from possible being only partially consumed. This method behaves
|
||||
/// equivalently to a single call to [`try_write()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write_vectored(&self, buf: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.inner.try_write_vectored(buf)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OwnedWriteHalf {
|
||||
|
||||
@@ -59,7 +59,7 @@ impl UnixStream {
|
||||
Ok(stream)
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
@@ -133,7 +133,7 @@ impl UnixStream {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the socket to become readable.
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
@@ -290,7 +290,7 @@ impl UnixStream {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read(buf))
|
||||
}
|
||||
|
||||
/// Try to read data from the stream into the provided buffers, returning
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
@@ -369,7 +369,7 @@ impl UnixStream {
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Try to read data from the stream into the provided buffer, advancing the
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
@@ -449,7 +449,7 @@ impl UnixStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the socket to become writable.
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
@@ -535,7 +535,7 @@ impl UnixStream {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to write a buffer to the stream, returning how many bytes were
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
@@ -591,7 +591,7 @@ impl UnixStream {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write(buf))
|
||||
}
|
||||
|
||||
/// Try to write several buffers to the stream, returning how many bytes
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
@@ -653,7 +653,7 @@ impl UnixStream {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
@@ -709,7 +709,7 @@ impl UnixStream {
|
||||
Ok(UnixStream { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::UnixStream`] into a [`std::os::unix::net::UnixStream`].
|
||||
/// Turns a [`tokio::net::UnixStream`] into a [`std::os::unix::net::UnixStream`].
|
||||
///
|
||||
/// The returned [`std::os::unix::net::UnixStream`] will have nonblocking
|
||||
/// mode set as `true`. Use [`set_nonblocking`] to change the blocking
|
||||
@@ -773,11 +773,41 @@ impl UnixStream {
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::UnixStream;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let dir = tempfile::tempdir().unwrap();
|
||||
/// let bind_path = dir.path().join("bind_path");
|
||||
/// let stream = UnixStream::connect(bind_path).await?;
|
||||
///
|
||||
/// println!("{:?}", stream.local_addr()?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.io.local_addr().map(SocketAddr)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::UnixStream;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let dir = tempfile::tempdir().unwrap();
|
||||
/// let bind_path = dir.path().join("bind_path");
|
||||
/// let stream = UnixStream::connect(bind_path).await?;
|
||||
///
|
||||
/// println!("{:?}", stream.peer_addr()?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.io.peer_addr().map(SocketAddr)
|
||||
}
|
||||
@@ -804,7 +834,7 @@ impl UnixStream {
|
||||
// These lifetime markers also appear in the generated documentation, and make
|
||||
// it more clear that this is a *borrowed* split.
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
/// Split a `UnixStream` into a read half and a write half, which can be used
|
||||
/// Splits a `UnixStream` into a read half and a write half, which can be used
|
||||
/// to read and write the stream concurrently.
|
||||
///
|
||||
/// This method is more efficient than [`into_split`], but the halves cannot be
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use libc::{gid_t, pid_t, uid_t};
|
||||
|
||||
/// Credentials of a process
|
||||
/// Credentials of a process.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct UCred {
|
||||
/// PID (process ID) of the process
|
||||
/// PID (process ID) of the process.
|
||||
pid: Option<pid_t>,
|
||||
/// UID (user ID) of the process
|
||||
/// UID (user ID) of the process.
|
||||
uid: uid_t,
|
||||
/// GID (group ID) of the process
|
||||
/// GID (group ID) of the process.
|
||||
gid: gid_t,
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ pub struct NamedPipeServer {
|
||||
}
|
||||
|
||||
impl NamedPipeServer {
|
||||
/// Construct a new named pipe server from the specified raw handle.
|
||||
/// Constructs a new named pipe server from the specified raw handle.
|
||||
///
|
||||
/// This function will consume ownership of the handle given, passing
|
||||
/// responsibility for closing the handle to the returned object.
|
||||
@@ -234,7 +234,7 @@ impl NamedPipeServer {
|
||||
self.io.disconnect()
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same pipe on a single
|
||||
@@ -301,7 +301,7 @@ impl NamedPipeServer {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the pipe to become readable.
|
||||
/// Waits for the pipe to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
@@ -383,7 +383,7 @@ impl NamedPipeServer {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to read data from the pipe into the provided buffer, returning how
|
||||
/// Tries to read data from the pipe into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the pipe but does not wait for new data
|
||||
@@ -450,7 +450,7 @@ impl NamedPipeServer {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read(buf))
|
||||
}
|
||||
|
||||
/// Try to read data from the pipe into the provided buffers, returning
|
||||
/// Tries to read data from the pipe into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
@@ -528,7 +528,7 @@ impl NamedPipeServer {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read_vectored(bufs))
|
||||
}
|
||||
|
||||
/// Wait for the pipe to become writable.
|
||||
/// Waits for the pipe to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
@@ -606,7 +606,7 @@ impl NamedPipeServer {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to write a buffer to the pipe, returning how many bytes were
|
||||
/// Tries to write a buffer to the pipe, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
@@ -662,7 +662,7 @@ impl NamedPipeServer {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write(buf))
|
||||
}
|
||||
|
||||
/// Try to write several buffers to the pipe, returning how many bytes
|
||||
/// Tries to write several buffers to the pipe, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
@@ -724,7 +724,7 @@ impl NamedPipeServer {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
@@ -846,7 +846,7 @@ pub struct NamedPipeClient {
|
||||
}
|
||||
|
||||
impl NamedPipeClient {
|
||||
/// Construct a new named pipe client from the specified raw handle.
|
||||
/// Constructs a new named pipe client from the specified raw handle.
|
||||
///
|
||||
/// This function will consume ownership of the handle given, passing
|
||||
/// responsibility for closing the handle to the returned object.
|
||||
@@ -896,7 +896,7 @@ impl NamedPipeClient {
|
||||
unsafe { named_pipe_info(self.io.as_raw_handle()) }
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same pipe on a single
|
||||
@@ -962,7 +962,7 @@ impl NamedPipeClient {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the pipe to become readable.
|
||||
/// Waits for the pipe to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
@@ -1043,7 +1043,7 @@ impl NamedPipeClient {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to read data from the pipe into the provided buffer, returning how
|
||||
/// Tries to read data from the pipe into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the pipe but does not wait for new data
|
||||
@@ -1109,7 +1109,7 @@ impl NamedPipeClient {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read(buf))
|
||||
}
|
||||
|
||||
/// Try to read data from the pipe into the provided buffers, returning
|
||||
/// Tries to read data from the pipe into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
@@ -1186,7 +1186,7 @@ impl NamedPipeClient {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read_vectored(bufs))
|
||||
}
|
||||
|
||||
/// Wait for the pipe to become writable.
|
||||
/// Waits for the pipe to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
@@ -1263,7 +1263,7 @@ impl NamedPipeClient {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to write a buffer to the pipe, returning how many bytes were
|
||||
/// Tries to write a buffer to the pipe, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
@@ -1318,7 +1318,7 @@ impl NamedPipeClient {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write(buf))
|
||||
}
|
||||
|
||||
/// Try to write several buffers to the pipe, returning how many bytes
|
||||
/// Tries to write several buffers to the pipe, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
@@ -1379,7 +1379,7 @@ impl NamedPipeClient {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
@@ -1882,7 +1882,7 @@ impl ServerOptions {
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the named pipe identified by `addr` for use as a server.
|
||||
/// Creates the named pipe identified by `addr` for use as a server.
|
||||
///
|
||||
/// This uses the [`CreateNamedPipe`] function.
|
||||
///
|
||||
@@ -1913,7 +1913,7 @@ impl ServerOptions {
|
||||
unsafe { self.create_with_security_attributes_raw(addr, ptr::null_mut()) }
|
||||
}
|
||||
|
||||
/// Create the named pipe identified by `addr` for use as a server.
|
||||
/// Creates the named pipe identified by `addr` for use as a server.
|
||||
///
|
||||
/// This is the same as [`create`] except that it supports providing the raw
|
||||
/// pointer to a structure of [`SECURITY_ATTRIBUTES`] which will be passed
|
||||
@@ -2042,7 +2042,7 @@ impl ClientOptions {
|
||||
self
|
||||
}
|
||||
|
||||
/// Open the named pipe identified by `addr`.
|
||||
/// Opens the named pipe identified by `addr`.
|
||||
///
|
||||
/// This opens the client using [`CreateFile`] with the
|
||||
/// `dwCreationDisposition` option set to `OPEN_EXISTING`.
|
||||
@@ -2099,7 +2099,7 @@ impl ClientOptions {
|
||||
unsafe { self.open_with_security_attributes_raw(addr, ptr::null_mut()) }
|
||||
}
|
||||
|
||||
/// Open the named pipe identified by `addr`.
|
||||
/// Opens the named pipe identified by `addr`.
|
||||
///
|
||||
/// This is the same as [`open`] except that it supports providing the raw
|
||||
/// pointer to a structure of [`SECURITY_ATTRIBUTES`] which will be passed
|
||||
@@ -2201,7 +2201,7 @@ pub struct PipeInfo {
|
||||
pub in_buffer_size: u32,
|
||||
}
|
||||
|
||||
/// Encode an address so that it is a null-terminated wide string.
|
||||
/// Encodes an address so that it is a null-terminated wide string.
|
||||
fn encode_addr(addr: impl AsRef<OsStr>) -> Box<[u16]> {
|
||||
let len = addr.as_ref().encode_wide().count();
|
||||
let mut vec = Vec::with_capacity(len + 1);
|
||||
|
||||
@@ -45,12 +45,12 @@ use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
/// Block the current thread.
|
||||
/// Blocks the current thread.
|
||||
pub(crate) trait Park {
|
||||
/// Unpark handle type for the `Park` implementation.
|
||||
type Unpark: Unpark;
|
||||
|
||||
/// Error returned by `park`
|
||||
/// Error returned by `park`.
|
||||
type Error: Debug;
|
||||
|
||||
/// Gets a new `Unpark` handle associated with this `Park` instance.
|
||||
@@ -66,7 +66,7 @@ pub(crate) trait Park {
|
||||
///
|
||||
/// This function **should** not panic, but ultimately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Park` implementation
|
||||
/// `Park` implementation.
|
||||
fn park(&mut self) -> Result<(), Self::Error>;
|
||||
|
||||
/// Parks the current thread for at most `duration`.
|
||||
@@ -82,10 +82,10 @@ pub(crate) trait Park {
|
||||
///
|
||||
/// This function **should** not panic, but ultimately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Park` implementation
|
||||
/// `Park` implementation.
|
||||
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error>;
|
||||
|
||||
/// Release all resources holded by the parker for proper leak-free shutdown
|
||||
/// Releases all resources holded by the parker for proper leak-free shutdown.
|
||||
fn shutdown(&mut self);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ pub(crate) trait Unpark: Sync + Send + 'static {
|
||||
///
|
||||
/// This function **should** not panic, but ultimately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Unpark` implementation
|
||||
/// `Unpark` implementation.
|
||||
fn unpark(&self);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ impl Park for ParkThread {
|
||||
// ==== impl Inner ====
|
||||
|
||||
impl Inner {
|
||||
/// Park the current thread for at most `dur`.
|
||||
/// Parks the current thread for at most `dur`.
|
||||
fn park(&self) {
|
||||
// If we were previously notified then we consume this notification and
|
||||
// return quickly.
|
||||
@@ -227,7 +227,7 @@ pub(crate) struct CachedParkThread {
|
||||
}
|
||||
|
||||
impl CachedParkThread {
|
||||
/// Create a new `ParkThread` handle for the current thread.
|
||||
/// Creates a new `ParkThread` handle for the current thread.
|
||||
///
|
||||
/// This type cannot be moved to other threads, so it should be created on
|
||||
/// the thread that the caller intends to park.
|
||||
@@ -241,7 +241,7 @@ impl CachedParkThread {
|
||||
self.with_current(|park_thread| park_thread.unpark())
|
||||
}
|
||||
|
||||
/// Get a reference to the `ParkThread` handle for this thread.
|
||||
/// Gets a reference to the `ParkThread` handle for this thread.
|
||||
fn with_current<F, R>(&self, f: F) -> Result<R, ParkError>
|
||||
where
|
||||
F: FnOnce(&ParkThread) -> R,
|
||||
|
||||
@@ -578,7 +578,7 @@ impl Command {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set executable argument
|
||||
/// Sets executable argument.
|
||||
///
|
||||
/// Set the first process argument, `argv[0]`, to something other than the
|
||||
/// default executable path.
|
||||
@@ -1173,7 +1173,7 @@ pub struct ChildStderr {
|
||||
}
|
||||
|
||||
impl ChildStdin {
|
||||
/// Create an asynchronous `ChildStdin` from a synchronous one.
|
||||
/// Creates an asynchronous `ChildStdin` from a synchronous one.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -1188,7 +1188,7 @@ impl ChildStdin {
|
||||
}
|
||||
|
||||
impl ChildStdout {
|
||||
/// Create an asynchronous `ChildStderr` from a synchronous one.
|
||||
/// Creates an asynchronous `ChildStderr` from a synchronous one.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -1203,7 +1203,7 @@ impl ChildStdout {
|
||||
}
|
||||
|
||||
impl ChildStderr {
|
||||
/// Create an asynchronous `ChildStderr` from a synchronous one.
|
||||
/// Creates an asynchronous `ChildStderr` from a synchronous one.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![cfg_attr(not(feature = "rt"), allow(dead_code))]
|
||||
|
||||
//! Process driver
|
||||
//! Process driver.
|
||||
|
||||
use crate::park::Park;
|
||||
use crate::process::unix::GlobalOrphanQueue;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Unix handling of child processes
|
||||
//! Unix handling of child processes.
|
||||
//!
|
||||
//! Right now the only "fancy" thing about this is how we implement the
|
||||
//! `Future` implementation on `Child` to get the exit status. Unix offers
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::sync::atomic::Ordering::{AcqRel, Acquire, Release};
|
||||
use std::sync::atomic::Ordering::{AcqRel, Release};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll::{Pending, Ready};
|
||||
use std::time::Duration;
|
||||
@@ -213,19 +213,17 @@ impl<P: Park> BasicScheduler<P> {
|
||||
}
|
||||
|
||||
impl<P: Park> Inner<P> {
|
||||
/// Block on the future provided and drive the runtime's driver.
|
||||
/// Blocks on the provided future and drives the runtime's driver.
|
||||
fn block_on<F: Future>(&mut self, future: F) -> F::Output {
|
||||
enter(self, |scheduler, context| {
|
||||
let _enter = crate::runtime::enter(false);
|
||||
let waker = scheduler.spawner.waker_ref();
|
||||
let mut cx = std::task::Context::from_waker(&waker);
|
||||
let mut polled = false;
|
||||
|
||||
pin!(future);
|
||||
|
||||
'outer: loop {
|
||||
if scheduler.spawner.was_woken() || !polled {
|
||||
polled = true;
|
||||
if scheduler.spawner.reset_woken() {
|
||||
scheduler.stats.incr_poll_count();
|
||||
if let Ready(v) = crate::coop::budget(|| future.as_mut().poll(&mut cx)) {
|
||||
return v;
|
||||
@@ -301,8 +299,8 @@ impl<P: Park> Inner<P> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter the scheduler context. This sets the queue and other necessary
|
||||
/// scheduler state in the thread-local
|
||||
/// Enters the scheduler context. This sets the queue and other necessary
|
||||
/// scheduler state in the thread-local.
|
||||
fn enter<F, R, P>(scheduler: &mut Inner<P>, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Inner<P>, &Context) -> R,
|
||||
@@ -418,13 +416,15 @@ impl Spawner {
|
||||
}
|
||||
|
||||
fn waker_ref(&self) -> WakerRef<'_> {
|
||||
// clear the woken bit
|
||||
self.shared.woken.swap(false, AcqRel);
|
||||
// Set woken to true when enter block_on, ensure outer future
|
||||
// be polled for the first time when enter loop
|
||||
self.shared.woken.store(true, Release);
|
||||
waker_ref(&self.shared)
|
||||
}
|
||||
|
||||
fn was_woken(&self) -> bool {
|
||||
self.shared.woken.load(Acquire)
|
||||
// reset woken to false and return original value
|
||||
pub(crate) fn reset_woken(&self) -> bool {
|
||||
self.shared.woken.swap(false, AcqRel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,28 +25,28 @@ pub(crate) struct Spawner {
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
/// State shared between worker threads
|
||||
/// State shared between worker threads.
|
||||
shared: Mutex<Shared>,
|
||||
|
||||
/// Pool threads wait on this.
|
||||
condvar: Condvar,
|
||||
|
||||
/// Spawned threads use this name
|
||||
/// Spawned threads use this name.
|
||||
thread_name: ThreadNameFn,
|
||||
|
||||
/// Spawned thread stack size
|
||||
/// Spawned thread stack size.
|
||||
stack_size: Option<usize>,
|
||||
|
||||
/// Call after a thread starts
|
||||
/// Call after a thread starts.
|
||||
after_start: Option<Callback>,
|
||||
|
||||
/// Call before a thread stops
|
||||
/// Call before a thread stops.
|
||||
before_stop: Option<Callback>,
|
||||
|
||||
// Maximum number of threads
|
||||
// Maximum number of threads.
|
||||
thread_cap: usize,
|
||||
|
||||
// Customizable wait timeout
|
||||
// Customizable wait timeout.
|
||||
keep_alive: Duration,
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ struct Shared {
|
||||
/// calling shutdown handles joining on these.
|
||||
worker_threads: HashMap<usize, thread::JoinHandle<()>>,
|
||||
/// This is a counter used to iterate worker_threads in a consistent order (for loom's
|
||||
/// benefit)
|
||||
/// benefit).
|
||||
worker_thread_index: usize,
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ type Task = task::UnownedTask<NoopSchedule>;
|
||||
|
||||
const KEEP_ALIVE: Duration = Duration::from_secs(10);
|
||||
|
||||
/// Run the provided function on an executor dedicated to blocking operations.
|
||||
/// Runs the provided function on an executor dedicated to blocking operations.
|
||||
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
|
||||
where
|
||||
F: FnOnce() -> R + Send + 'static,
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::time::Duration;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct Sender {
|
||||
tx: Arc<oneshot::Sender<()>>,
|
||||
_tx: Arc<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -20,7 +20,7 @@ pub(super) struct Receiver {
|
||||
|
||||
pub(super) fn channel() -> (Sender, Receiver) {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let tx = Sender { tx: Arc::new(tx) };
|
||||
let tx = Sender { _tx: Arc::new(tx) };
|
||||
let rx = Receiver { rx };
|
||||
|
||||
(tx, rx)
|
||||
|
||||
@@ -2,13 +2,13 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Converts a function to a future that completes on poll
|
||||
/// Converts a function to a future that completes on poll.
|
||||
pub(crate) struct BlockingTask<T> {
|
||||
func: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> BlockingTask<T> {
|
||||
/// Initializes a new blocking task from the given function
|
||||
/// Initializes a new blocking task from the given function.
|
||||
pub(crate) fn new(func: T) -> BlockingTask<T> {
|
||||
BlockingTask { func: Some(func) }
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ cfg_rt! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set this [`Handle`] as the current active [`Handle`].
|
||||
/// Sets this [`Handle`] as the current active [`Handle`].
|
||||
///
|
||||
/// [`Handle`]: Handle
|
||||
pub(crate) fn enter(new: Handle) -> EnterGuard {
|
||||
|
||||
@@ -92,7 +92,7 @@ cfg_rt_multi_thread! {
|
||||
}
|
||||
|
||||
cfg_rt! {
|
||||
/// Disallow blocking in the current runtime context until the guard is dropped.
|
||||
/// Disallows blocking in the current runtime context until the guard is dropped.
|
||||
pub(crate) fn disallow_blocking() -> DisallowBlockingGuard {
|
||||
let reset = ENTERED.with(|c| {
|
||||
if let EnterContext::Entered {
|
||||
|
||||
+25
-10
@@ -4,6 +4,7 @@ use crate::runtime::{blocking, context, driver, Spawner};
|
||||
use crate::util::error::CONTEXT_MISSING_ERROR;
|
||||
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::{error, fmt};
|
||||
|
||||
/// Handle to the runtime.
|
||||
@@ -17,15 +18,25 @@ pub struct Handle {
|
||||
pub(super) spawner: Spawner,
|
||||
|
||||
/// Handles to the I/O drivers
|
||||
#[cfg_attr(
|
||||
not(any(feature = "net", feature = "process", all(unix, feature = "signal"))),
|
||||
allow(dead_code)
|
||||
)]
|
||||
pub(super) io_handle: driver::IoHandle,
|
||||
|
||||
/// Handles to the signal drivers
|
||||
#[cfg_attr(
|
||||
not(any(feature = "signal", all(unix, feature = "process"))),
|
||||
allow(dead_code)
|
||||
)]
|
||||
pub(super) signal_handle: driver::SignalHandle,
|
||||
|
||||
/// Handles to the time drivers
|
||||
#[cfg_attr(not(feature = "time"), allow(dead_code))]
|
||||
pub(super) time_handle: driver::TimeHandle,
|
||||
|
||||
/// Source of `Instant::now()`
|
||||
#[cfg_attr(not(all(feature = "time", feature = "test-util")), allow(dead_code))]
|
||||
pub(super) clock: driver::Clock,
|
||||
|
||||
/// Blocking pool spawner
|
||||
@@ -41,12 +52,12 @@ pub struct Handle {
|
||||
#[derive(Debug)]
|
||||
#[must_use = "Creating and dropping a guard does nothing"]
|
||||
pub struct EnterGuard<'a> {
|
||||
handle: &'a Handle,
|
||||
guard: context::EnterGuard,
|
||||
_guard: context::EnterGuard,
|
||||
_handle_lifetime: PhantomData<&'a Handle>,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
/// Enter the runtime context. This allows you to construct types that must
|
||||
/// Enters the runtime context. This allows you to construct types that must
|
||||
/// have an executor available on creation such as [`Sleep`] or [`TcpStream`].
|
||||
/// It will also allow you to call methods such as [`tokio::spawn`].
|
||||
///
|
||||
@@ -55,12 +66,12 @@ impl Handle {
|
||||
/// [`tokio::spawn`]: fn@crate::spawn
|
||||
pub fn enter(&self) -> EnterGuard<'_> {
|
||||
EnterGuard {
|
||||
handle: self,
|
||||
guard: context::enter(self.clone()),
|
||||
_guard: context::enter(self.clone()),
|
||||
_handle_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a `Handle` view over the currently running `Runtime`
|
||||
/// Returns a `Handle` view over the currently running `Runtime`.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
@@ -119,7 +130,7 @@ impl Handle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a future onto the Tokio runtime.
|
||||
/// Spawns a future onto the Tokio runtime.
|
||||
///
|
||||
/// This spawns the given future onto the runtime's executor, usually a
|
||||
/// thread pool. The thread pool is then responsible for polling the future
|
||||
@@ -157,7 +168,7 @@ impl Handle {
|
||||
self.spawner.spawn(future)
|
||||
}
|
||||
|
||||
/// Run the provided function on an executor dedicated to blocking
|
||||
/// Runs the provided function on an executor dedicated to blocking.
|
||||
/// operations.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -182,7 +193,11 @@ impl Handle {
|
||||
F: FnOnce() -> R + Send + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.spawn_blocking_inner(func, None)
|
||||
if cfg!(debug_assertions) && std::mem::size_of::<F>() > 2048 {
|
||||
self.spawn_blocking_inner(Box::new(func), None)
|
||||
} else {
|
||||
self.spawn_blocking_inner(func, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tokio_track_caller, track_caller)]
|
||||
@@ -226,7 +241,7 @@ impl Handle {
|
||||
handle
|
||||
}
|
||||
|
||||
/// Run a future to completion on this `Handle`'s associated `Runtime`.
|
||||
/// Runs a future to completion on this `Handle`'s associated `Runtime`.
|
||||
///
|
||||
/// This runs the given future on the current thread, blocking until it is
|
||||
/// complete, and yielding its resolved result. Any tasks or timers which
|
||||
|
||||
@@ -294,7 +294,7 @@ cfg_rt! {
|
||||
type Callback = std::sync::Arc<dyn Fn() + Send + Sync>;
|
||||
|
||||
impl Runtime {
|
||||
/// Create a new runtime instance with default configuration values.
|
||||
/// Creates a new runtime instance with default configuration values.
|
||||
///
|
||||
/// This results in the multi threaded scheduler, I/O driver, and time driver being
|
||||
/// initialized.
|
||||
@@ -329,7 +329,7 @@ cfg_rt! {
|
||||
Builder::new_multi_thread().enable_all().build()
|
||||
}
|
||||
|
||||
/// Return a handle to the runtime's spawner.
|
||||
/// Returns a handle to the runtime's spawner.
|
||||
///
|
||||
/// The returned handle can be used to spawn tasks that run on this runtime, and can
|
||||
/// be cloned to allow moving the `Handle` to other threads.
|
||||
@@ -350,7 +350,7 @@ cfg_rt! {
|
||||
&self.handle
|
||||
}
|
||||
|
||||
/// Spawn a future onto the Tokio runtime.
|
||||
/// Spawns a future onto the Tokio runtime.
|
||||
///
|
||||
/// This spawns the given future onto the runtime's executor, usually a
|
||||
/// thread pool. The thread pool is then responsible for polling the future
|
||||
@@ -384,7 +384,7 @@ cfg_rt! {
|
||||
self.handle.spawn(future)
|
||||
}
|
||||
|
||||
/// Run the provided function on an executor dedicated to blocking operations.
|
||||
/// Runs the provided function on an executor dedicated to blocking operations.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -409,7 +409,7 @@ cfg_rt! {
|
||||
self.handle.spawn_blocking(func)
|
||||
}
|
||||
|
||||
/// Run a future to completion on the Tokio runtime. This is the
|
||||
/// Runs a future to completion on the Tokio runtime. This is the
|
||||
/// runtime's entry point.
|
||||
///
|
||||
/// This runs the given future on the current thread, blocking until it is
|
||||
@@ -464,7 +464,7 @@ cfg_rt! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter the runtime context.
|
||||
/// Enters the runtime context.
|
||||
///
|
||||
/// This allows you to construct types that must have an executor
|
||||
/// available on creation such as [`Sleep`] or [`TcpStream`]. It will
|
||||
@@ -500,7 +500,7 @@ cfg_rt! {
|
||||
self.handle.enter()
|
||||
}
|
||||
|
||||
/// Shutdown the runtime, waiting for at most `duration` for all spawned
|
||||
/// Shuts down the runtime, waiting for at most `duration` for all spawned
|
||||
/// task to shutdown.
|
||||
///
|
||||
/// Usually, dropping a `Runtime` handle is sufficient as tasks are able to
|
||||
@@ -541,7 +541,7 @@ cfg_rt! {
|
||||
self.blocking_pool.shutdown(Some(duration));
|
||||
}
|
||||
|
||||
/// Shutdown the runtime, without waiting for any spawned tasks to shutdown.
|
||||
/// Shuts down the runtime, without waiting for any spawned tasks to shutdown.
|
||||
///
|
||||
/// This can be useful if you want to drop a runtime from within another runtime.
|
||||
/// Normally, dropping a runtime will block indefinitely for spawned blocking tasks
|
||||
|
||||
@@ -44,22 +44,22 @@ pub(super) struct CoreStage<T: Future> {
|
||||
///
|
||||
/// Holds the future or output, depending on the stage of execution.
|
||||
pub(super) struct Core<T: Future, S> {
|
||||
/// Scheduler used to drive this future
|
||||
/// Scheduler used to drive this future.
|
||||
pub(super) scheduler: S,
|
||||
|
||||
/// Either the future or the output
|
||||
/// Either the future or the output.
|
||||
pub(super) stage: CoreStage<T>,
|
||||
}
|
||||
|
||||
/// Crate public as this is also needed by the pool.
|
||||
#[repr(C)]
|
||||
pub(crate) struct Header {
|
||||
/// Task state
|
||||
/// Task 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>>>,
|
||||
|
||||
/// Table of function pointers for executing actions on the task.
|
||||
@@ -133,7 +133,7 @@ impl<T: Future> CoreStage<T> {
|
||||
self.stage.with_mut(f)
|
||||
}
|
||||
|
||||
/// Poll the future
|
||||
/// Polls the future.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
@@ -169,7 +169,7 @@ impl<T: Future> CoreStage<T> {
|
||||
res
|
||||
}
|
||||
|
||||
/// Drop the future
|
||||
/// Drops the future.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
@@ -181,7 +181,7 @@ impl<T: Future> CoreStage<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Store the task output
|
||||
/// Stores the task output.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
@@ -193,7 +193,7 @@ impl<T: Future> CoreStage<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the task output
|
||||
/// Takes the task output.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
|
||||
@@ -29,12 +29,12 @@ impl JoinError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the error was caused by the task being cancelled
|
||||
/// Returns true if the error was caused by the task being cancelled.
|
||||
pub fn is_cancelled(&self) -> bool {
|
||||
matches!(&self.repr, Repr::Cancelled)
|
||||
}
|
||||
|
||||
/// Returns true if the error was caused by the task panicking
|
||||
/// Returns true if the error was caused by the task panicking.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::panic;
|
||||
use std::ptr::NonNull;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
|
||||
/// Typed raw task handle
|
||||
/// Typed raw task handle.
|
||||
pub(super) struct Harness<T: Future, S: 'static> {
|
||||
cell: NonNull<Cell<T, S>>,
|
||||
}
|
||||
@@ -74,7 +74,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll the task and cancel it if necessary. This takes ownership of a
|
||||
/// Polls the task and cancel it if necessary. This takes ownership of a
|
||||
/// ref-count.
|
||||
///
|
||||
/// If the return value is Notified, the caller is given ownership of two
|
||||
@@ -124,7 +124,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Forcibly shutdown the task
|
||||
/// Forcibly shuts down the task.
|
||||
///
|
||||
/// Attempt to transition to `Running` in order to forcibly shutdown the
|
||||
/// task. If the task is currently running or in a state of completion, then
|
||||
@@ -192,7 +192,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Remotely abort the task.
|
||||
/// Remotely aborts the task.
|
||||
///
|
||||
/// The caller should hold a ref-count, but we do not consume it.
|
||||
///
|
||||
@@ -280,7 +280,7 @@ where
|
||||
|
||||
// ====== internal ======
|
||||
|
||||
/// Complete the task. This method assumes that the state is RUNNING.
|
||||
/// Completes the task. This method assumes that the state is RUNNING.
|
||||
fn complete(self) {
|
||||
// The future has completed and its output has been written to the task
|
||||
// stage. We transition from running to complete.
|
||||
@@ -310,7 +310,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Release the task from the scheduler. Returns the number of ref-counts
|
||||
/// Releases the task from the scheduler. Returns the number of ref-counts
|
||||
/// that should be decremented.
|
||||
fn release(&self) -> usize {
|
||||
// We don't actually increment the ref-count here, but the new task is
|
||||
@@ -325,7 +325,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new task that holds its own ref-count.
|
||||
/// Creates a new task that holds its own ref-count.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
@@ -425,7 +425,7 @@ enum PollFuture {
|
||||
Dealloc,
|
||||
}
|
||||
|
||||
/// Cancel the task and store the appropriate error in the stage field.
|
||||
/// Cancels the task and store the appropriate error in the stage field.
|
||||
fn cancel_task<T: Future>(stage: &CoreStage<T>) {
|
||||
// Drop the future from a panic guard.
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
@@ -442,7 +442,7 @@ fn cancel_task<T: Future>(stage: &CoreStage<T>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll the future. If the future completes, the output is written to the
|
||||
/// Polls the future. If the future completes, the output is written to the
|
||||
/// stage field.
|
||||
fn poll_future<T: Future>(core: &CoreStage<T>, cx: Context<'_>) -> Poll<()> {
|
||||
// Poll the future.
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::sync::atomic::Ordering::{Acquire, Release};
|
||||
/// Growable, MPMC queue used to inject new tasks into the scheduler and as an
|
||||
/// overflow queue when the local, fixed-size, array queue overflows.
|
||||
pub(crate) struct Inject<T: 'static> {
|
||||
/// Pointers to the head and tail of the queue
|
||||
/// Pointers to the head and tail of the queue.
|
||||
pointers: Mutex<Pointers>,
|
||||
|
||||
/// Number of pending tasks in the queue. This helps prevent unnecessary
|
||||
@@ -22,13 +22,13 @@ pub(crate) struct Inject<T: 'static> {
|
||||
}
|
||||
|
||||
struct Pointers {
|
||||
/// True if the queue is closed
|
||||
/// True if the queue is closed.
|
||||
is_closed: bool,
|
||||
|
||||
/// Linked-list head
|
||||
/// Linked-list head.
|
||||
head: Option<NonNull<task::Header>>,
|
||||
|
||||
/// Linked-list tail
|
||||
/// Linked-list tail.
|
||||
tail: Option<NonNull<task::Header>>,
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ impl<T: 'static> Inject<T> {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Close the injection queue, returns `true` if the queue is open when the
|
||||
/// Closes the injection queue, returns `true` if the queue is open when the
|
||||
/// transition is made.
|
||||
pub(crate) fn close(&self) -> bool {
|
||||
let mut p = self.pointers.lock();
|
||||
@@ -137,7 +137,7 @@ impl<T: 'static> Inject<T> {
|
||||
self.push_batch_inner(first, prev, counter);
|
||||
}
|
||||
|
||||
/// Insert several tasks that have been linked together into the queue.
|
||||
/// Inserts several tasks that have been linked together into the queue.
|
||||
///
|
||||
/// The provided head and tail may be be the same task. In this case, a
|
||||
/// single task is inserted.
|
||||
|
||||
@@ -78,7 +78,7 @@ impl<S: 'static> OwnedTasks<S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Bind the provided task to this OwnedTasks instance. This fails if the
|
||||
/// Binds the provided task to this OwnedTasks instance. This fails if the
|
||||
/// OwnedTasks has been closed.
|
||||
pub(crate) fn bind<T>(
|
||||
&self,
|
||||
@@ -110,7 +110,7 @@ impl<S: 'static> OwnedTasks<S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert that the given task is owned by this OwnedTasks and convert it to
|
||||
/// Asserts that the given task is owned by this OwnedTasks and convert it to
|
||||
/// a LocalNotified, giving the thread permission to poll this task.
|
||||
#[inline]
|
||||
pub(crate) fn assert_owner(&self, task: Notified<S>) -> LocalNotified<S> {
|
||||
@@ -124,7 +124,7 @@ impl<S: 'static> OwnedTasks<S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Shut down all tasks in the collection. This call also closes the
|
||||
/// Shuts down all tasks in the collection. This call also closes the
|
||||
/// collection, preventing new items from being added.
|
||||
pub(crate) fn close_and_shutdown_all(&self)
|
||||
where
|
||||
@@ -213,7 +213,7 @@ impl<S: 'static> LocalOwnedTasks<S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Shut down all tasks in the collection. This call also closes the
|
||||
/// Shuts down all tasks in the collection. This call also closes the
|
||||
/// collection, preventing new items from being added.
|
||||
pub(crate) fn close_and_shutdown_all(&self)
|
||||
where
|
||||
@@ -241,7 +241,7 @@ impl<S: 'static> LocalOwnedTasks<S> {
|
||||
unsafe { inner.list.remove(task.header().into()) })
|
||||
}
|
||||
|
||||
/// Assert that the given task is owned by this LocalOwnedTasks and convert
|
||||
/// Asserts that the given task is owned by this LocalOwnedTasks and convert
|
||||
/// it to a LocalNotified, giving the thread permission to poll this task.
|
||||
#[inline]
|
||||
pub(crate) fn assert_owner(&self, task: Notified<S>) -> LocalNotified<S> {
|
||||
|
||||
@@ -173,7 +173,7 @@ use std::marker::PhantomData;
|
||||
use std::ptr::NonNull;
|
||||
use std::{fmt, mem};
|
||||
|
||||
/// An owned handle to the task, tracked by ref count
|
||||
/// An owned handle to the task, tracked by ref count.
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct Task<S: 'static> {
|
||||
raw: RawTask,
|
||||
@@ -211,7 +211,7 @@ pub(crate) struct UnownedTask<S: 'static> {
|
||||
unsafe impl<S> Send for UnownedTask<S> {}
|
||||
unsafe impl<S> Sync for UnownedTask<S> {}
|
||||
|
||||
/// Task result sent back
|
||||
/// Task result sent back.
|
||||
pub(crate) type Result<T> = std::result::Result<T, JoinError>;
|
||||
|
||||
pub(crate) trait Schedule: Sync + Sized + 'static {
|
||||
@@ -260,7 +260,7 @@ cfg_rt! {
|
||||
(task, notified, join)
|
||||
}
|
||||
|
||||
/// Create a new task with an associated join handle. This method is used
|
||||
/// Creates a new task with an associated join handle. This method is used
|
||||
/// only when the task is not going to be stored in an `OwnedTasks` list.
|
||||
///
|
||||
/// Currently only blocking tasks use this method.
|
||||
@@ -327,7 +327,7 @@ cfg_rt_multi_thread! {
|
||||
}
|
||||
|
||||
impl<S: Schedule> Task<S> {
|
||||
/// Pre-emptively cancel the task as part of the shutdown process.
|
||||
/// Pre-emptively cancels the task as part of the shutdown process.
|
||||
pub(crate) fn shutdown(self) {
|
||||
let raw = self.raw;
|
||||
mem::forget(self);
|
||||
@@ -336,7 +336,7 @@ impl<S: Schedule> Task<S> {
|
||||
}
|
||||
|
||||
impl<S: Schedule> LocalNotified<S> {
|
||||
/// Run the task
|
||||
/// Runs the task.
|
||||
pub(crate) fn run(self) {
|
||||
let raw = self.task.raw;
|
||||
mem::forget(self);
|
||||
@@ -420,7 +420,7 @@ impl<S> fmt::Debug for Notified<S> {
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Tasks are pinned
|
||||
/// Tasks are pinned.
|
||||
unsafe impl<S> linked_list::Link for Task<S> {
|
||||
type Handle = Task<S>;
|
||||
type Target = Header;
|
||||
|
||||
@@ -10,22 +10,22 @@ pub(super) struct RawTask {
|
||||
}
|
||||
|
||||
pub(super) struct Vtable {
|
||||
/// Poll the future
|
||||
/// Polls the future.
|
||||
pub(super) poll: unsafe fn(NonNull<Header>),
|
||||
|
||||
/// Deallocate the memory
|
||||
/// Deallocates the memory.
|
||||
pub(super) dealloc: unsafe fn(NonNull<Header>),
|
||||
|
||||
/// Read the task output, if complete
|
||||
/// Reads the task output, if complete.
|
||||
pub(super) try_read_output: unsafe fn(NonNull<Header>, *mut (), &Waker),
|
||||
|
||||
/// The join handle has been dropped
|
||||
/// The join handle has been dropped.
|
||||
pub(super) drop_join_handle_slow: unsafe fn(NonNull<Header>),
|
||||
|
||||
/// The task is remotely aborted
|
||||
/// The task is remotely aborted.
|
||||
pub(super) remote_abort: unsafe fn(NonNull<Header>),
|
||||
|
||||
/// Scheduler is being shutdown
|
||||
/// Scheduler is being shutdown.
|
||||
pub(super) shutdown: unsafe fn(NonNull<Header>),
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ pub(super) struct State {
|
||||
val: AtomicUsize,
|
||||
}
|
||||
|
||||
/// Current state value
|
||||
/// Current state value.
|
||||
#[derive(Copy, Clone)]
|
||||
pub(super) struct Snapshot(usize);
|
||||
|
||||
@@ -19,20 +19,20 @@ const RUNNING: usize = 0b0001;
|
||||
|
||||
/// The task is complete.
|
||||
///
|
||||
/// Once this bit is set, it is never unset
|
||||
/// Once this bit is set, it is never unset.
|
||||
const COMPLETE: usize = 0b0010;
|
||||
|
||||
/// Extracts the task's lifecycle value from the state
|
||||
/// Extracts the task's lifecycle value from the state.
|
||||
const LIFECYCLE_MASK: usize = 0b11;
|
||||
|
||||
/// Flag tracking if the task has been pushed into a run queue.
|
||||
const NOTIFIED: usize = 0b100;
|
||||
|
||||
/// The join handle is still around
|
||||
/// The join handle is still around.
|
||||
#[allow(clippy::unusual_byte_groupings)] // https://github.com/rust-lang/rust-clippy/issues/6556
|
||||
const JOIN_INTEREST: usize = 0b1_000;
|
||||
|
||||
/// A join handle waker has been set
|
||||
/// A join handle waker has been set.
|
||||
#[allow(clippy::unusual_byte_groupings)] // https://github.com/rust-lang/rust-clippy/issues/6556
|
||||
const JOIN_WAKER: usize = 0b10_000;
|
||||
|
||||
@@ -40,19 +40,19 @@ const JOIN_WAKER: usize = 0b10_000;
|
||||
#[allow(clippy::unusual_byte_groupings)] // https://github.com/rust-lang/rust-clippy/issues/6556
|
||||
const CANCELLED: usize = 0b100_000;
|
||||
|
||||
/// All bits
|
||||
/// All bits.
|
||||
const STATE_MASK: usize = LIFECYCLE_MASK | NOTIFIED | JOIN_INTEREST | JOIN_WAKER | CANCELLED;
|
||||
|
||||
/// Bits used by the ref count portion of the state.
|
||||
const REF_COUNT_MASK: usize = !STATE_MASK;
|
||||
|
||||
/// Number of positions to shift the ref count
|
||||
/// Number of positions to shift the ref count.
|
||||
const REF_COUNT_SHIFT: usize = REF_COUNT_MASK.count_zeros() as usize;
|
||||
|
||||
/// One ref count
|
||||
/// One ref count.
|
||||
const REF_ONE: usize = 1 << REF_COUNT_SHIFT;
|
||||
|
||||
/// State a task is initialized with
|
||||
/// State a task is initialized with.
|
||||
///
|
||||
/// A task is initialized with three references:
|
||||
///
|
||||
@@ -96,7 +96,7 @@ pub(super) enum TransitionToNotifiedByRef {
|
||||
/// All transitions are performed via RMW operations. This establishes an
|
||||
/// unambiguous modification order.
|
||||
impl State {
|
||||
/// Return a task's initial state
|
||||
/// Returns a task's initial state.
|
||||
pub(super) fn new() -> State {
|
||||
// The raw task returned by this method has a ref-count of three. See
|
||||
// the comment on INITIAL_STATE for more.
|
||||
@@ -110,7 +110,7 @@ impl State {
|
||||
Snapshot(self.val.load(Acquire))
|
||||
}
|
||||
|
||||
/// Attempt to transition the lifecycle to `Running`. This sets the
|
||||
/// Attempts to transition the lifecycle to `Running`. This sets the
|
||||
/// notified bit to false so notifications during the poll can be detected.
|
||||
pub(super) fn transition_to_running(&self) -> TransitionToRunning {
|
||||
self.fetch_update_action(|mut next| {
|
||||
@@ -190,7 +190,7 @@ impl State {
|
||||
Snapshot(prev.0 ^ DELTA)
|
||||
}
|
||||
|
||||
/// Transition from `Complete` -> `Terminal`, decrementing the reference
|
||||
/// Transitions from `Complete` -> `Terminal`, decrementing the reference
|
||||
/// count the specified number of times.
|
||||
///
|
||||
/// Returns true if the task should be deallocated.
|
||||
@@ -270,10 +270,10 @@ impl State {
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the cancelled bit and transition the state to `NOTIFIED` if idle.
|
||||
/// Sets the cancelled bit and transitions the state to `NOTIFIED` if idle.
|
||||
///
|
||||
/// Returns `true` if the task needs to be submitted to the pool for
|
||||
/// execution
|
||||
/// execution.
|
||||
pub(super) fn transition_to_notified_and_cancel(&self) -> bool {
|
||||
self.fetch_update_action(|mut snapshot| {
|
||||
if snapshot.is_cancelled() || snapshot.is_complete() {
|
||||
@@ -306,7 +306,7 @@ impl State {
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the `CANCELLED` bit and attempt to transition to `Running`.
|
||||
/// Sets the `CANCELLED` bit and attempts to transition to `Running`.
|
||||
///
|
||||
/// Returns `true` if the transition to `Running` succeeded.
|
||||
pub(super) fn transition_to_shutdown(&self) -> bool {
|
||||
@@ -330,7 +330,7 @@ impl State {
|
||||
}
|
||||
|
||||
/// Optimistically tries to swap the state assuming the join handle is
|
||||
/// __immediately__ dropped on spawn
|
||||
/// __immediately__ dropped on spawn.
|
||||
pub(super) fn drop_join_handle_fast(&self) -> Result<(), ()> {
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
@@ -352,7 +352,7 @@ impl State {
|
||||
.map_err(|_| ())
|
||||
}
|
||||
|
||||
/// Try to unset the JOIN_INTEREST flag.
|
||||
/// Tries to unset the JOIN_INTEREST flag.
|
||||
///
|
||||
/// Returns `Ok` if the operation happens before the task transitions to a
|
||||
/// completed state, `Err` otherwise.
|
||||
@@ -371,7 +371,7 @@ impl State {
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the `JOIN_WAKER` bit.
|
||||
/// Sets the `JOIN_WAKER` bit.
|
||||
///
|
||||
/// Returns `Ok` if the bit is set, `Err` otherwise. This operation fails if
|
||||
/// the task has completed.
|
||||
|
||||
@@ -63,6 +63,45 @@ fn block_on_num_polls() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_no_unnecessary_polls() {
|
||||
loom::model(|| {
|
||||
// // After we poll outer future, woken should reset to false
|
||||
let rt = Builder::new_current_thread().build().unwrap();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let pending_cnt = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
rt.spawn(async move {
|
||||
for _ in 0..24 {
|
||||
task::yield_now().await;
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
let pending_cnt_clone = pending_cnt.clone();
|
||||
rt.block_on(async move {
|
||||
// use task::yield_now() to ensure woken set to true
|
||||
// ResetFuture will be polled at most once
|
||||
// Here comes two cases
|
||||
// 1. recv no message from channel, ResetFuture will be polled
|
||||
// but get Pending and we record ResetFuture.pending_cnt ++.
|
||||
// Then when message arrive, ResetFuture returns Ready. So we
|
||||
// expect ResetFuture.pending_cnt = 1
|
||||
// 2. recv message from channel, ResetFuture returns Ready immediately.
|
||||
// We expect ResetFuture.pending_cnt = 0
|
||||
task::yield_now().await;
|
||||
ResetFuture {
|
||||
rx,
|
||||
pending_cnt: pending_cnt_clone,
|
||||
}
|
||||
.await;
|
||||
});
|
||||
|
||||
let pending_cnt = pending_cnt.load(Acquire);
|
||||
assert!(pending_cnt <= 1);
|
||||
});
|
||||
}
|
||||
|
||||
struct BlockedFuture {
|
||||
rx: Receiver<()>,
|
||||
num_polls: Arc<AtomicUsize>,
|
||||
@@ -80,3 +119,22 @@ impl Future for BlockedFuture {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ResetFuture {
|
||||
rx: Receiver<()>,
|
||||
pending_cnt: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
impl Future for ResetFuture {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match Pin::new(&mut self.rx).poll(cx) {
|
||||
Poll::Pending => {
|
||||
self.pending_cnt.fetch_add(1, Release);
|
||||
Poll::Pending
|
||||
}
|
||||
_ => Poll::Ready(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ impl Idle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `worker_id` is contained in the sleep set
|
||||
/// Returns `true` if `worker_id` is contained in the sleep set.
|
||||
pub(super) fn is_parked(&self, worker_id: usize) -> bool {
|
||||
let sleepers = self.sleepers.lock();
|
||||
sleepers.contains(&worker_id)
|
||||
|
||||
@@ -24,7 +24,7 @@ pub(crate) struct ThreadPool {
|
||||
spawner: Spawner,
|
||||
}
|
||||
|
||||
/// Submit futures to the associated thread pool for execution.
|
||||
/// Submits futures to the associated thread pool for execution.
|
||||
///
|
||||
/// A `Spawner` instance is a handle to a single thread pool that allows the owner
|
||||
/// of the handle to spawn futures onto the thread pool.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user