mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfb0f00838 | ||
|
|
4a91f197b0 | ||
|
|
601c383ab6 | ||
|
|
484cb52d8d | ||
|
|
de6ef21a81 | ||
|
|
90551d234f | ||
|
|
bd4c3dddca | ||
|
|
49b331855e | ||
|
|
da292dfb66 | ||
|
|
b9feac8d68 | ||
|
|
4fee6e3500 | ||
|
|
b1e69e560c | ||
|
|
f020b5c4b5 | ||
|
|
db8edc620f | ||
|
|
1979615cbf | ||
|
|
f669a609cf | ||
|
|
9e044e144b | ||
|
|
cb08fbc6c3 | ||
|
|
e59584a661 | ||
|
|
f7fb0bdc7a | ||
|
|
9faea740df | ||
|
|
a7b658c35b | ||
|
|
c1c8d1033d | ||
|
|
aa303bc205 | ||
|
|
7b6ccb515f | ||
|
|
4b174ce2c9 | ||
|
|
5f3296df77 | ||
|
|
9681ce2b95 |
+1
-1
@@ -1,7 +1,7 @@
|
||||
only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'master' || $CIRRUS_BRANCH =~ 'tokio-.*')
|
||||
auto_cancellation: $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH !=~ 'tokio-.*'
|
||||
freebsd_instance:
|
||||
image_family: freebsd-14-1
|
||||
image_family: freebsd-14-3
|
||||
env:
|
||||
RUST_STABLE: stable
|
||||
RUST_NIGHTLY: nightly-2024-05-05
|
||||
|
||||
+31
-13
@@ -475,10 +475,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check semver
|
||||
- name: Check `tokio` semver
|
||||
uses: obi1kenobi/cargo-semver-checks-action@v2
|
||||
with:
|
||||
rust-toolchain: ${{ env.rust_stable }}
|
||||
package: tokio
|
||||
release-type: minor
|
||||
- name: Check semver for rest of the workspace
|
||||
if: ${{ !startsWith(github.event.pull_request.base.ref, 'tokio-1.') }}
|
||||
uses: obi1kenobi/cargo-semver-checks-action@v2
|
||||
with:
|
||||
rust-toolchain: ${{ env.rust_stable }}
|
||||
exclude: tokio
|
||||
release-type: minor
|
||||
|
||||
cross-check:
|
||||
@@ -710,7 +718,14 @@ jobs:
|
||||
toolchain: ${{ env.rust_min }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "check --workspace --all-features"
|
||||
run: cargo check --workspace --all-features
|
||||
run: |
|
||||
if [[ "${{ github.event.pull_request.base.ref }}" =~ ^tokio-1\..* ]]; then
|
||||
# Only check `tokio` crate as the PR is backporting to an earlier tokio release.
|
||||
cargo check -p tokio --all-features
|
||||
else
|
||||
# Check all crates in the workspace
|
||||
cargo check --workspace --all-features
|
||||
fi
|
||||
env:
|
||||
RUSTFLAGS: "" # remove -Dwarnings
|
||||
|
||||
@@ -976,10 +991,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust 1.81
|
||||
- name: Install Rust 1.88.0
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: 1.81
|
||||
toolchain: 1.88.0
|
||||
- name: Install wasm-pack
|
||||
uses: taiki-e/install-action@wasm-pack
|
||||
|
||||
@@ -999,52 +1014,55 @@ jobs:
|
||||
- wasm32-wasip1-threads
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
- name: Install Rust 1.88.0
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
toolchain: 1.88.0
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
# Install dependencies
|
||||
- name: Install cargo-hack, wasmtime, and cargo-wasi
|
||||
- name: Install cargo-hack, wasmtime
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-hack,wasmtime,cargo-wasi
|
||||
tool: cargo-hack,wasmtime
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: WASI test tokio full
|
||||
run: cargo test -p tokio --target ${{ matrix.target }} --features full
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -S threads=y --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -W shared-memory=y -S threads=y --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
- name: WASI test tokio-util full
|
||||
run: cargo test -p tokio-util --target ${{ matrix.target }} --features full
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -S threads=y --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -W shared-memory=y -S threads=y --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
- name: WASI test tokio-stream
|
||||
run: cargo test -p tokio-stream --target ${{ matrix.target }} --features time,net,io-util,sync
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -S threads=y --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -W shared-memory=y -S threads=y --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
- name: test tests-integration --features wasi-rt
|
||||
# TODO: this should become: `cargo hack wasi test --each-feature`
|
||||
run: cargo wasi test --test rt_yield --features wasi-rt
|
||||
run: cargo test --target ${{ matrix.target }} --test rt_yield --features wasi-rt
|
||||
if: matrix.target == 'wasm32-wasip1'
|
||||
working-directory: tests-integration
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
|
||||
RUSTFLAGS: -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
- name: test tests-integration --features wasi-threads-rt
|
||||
run: cargo test --target ${{ matrix.target }} --features wasi-threads-rt
|
||||
if: matrix.target == 'wasm32-wasip1-threads'
|
||||
working-directory: tests-integration
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -S threads=y --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -W shared-memory=y -S threads=y --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
check-external-types:
|
||||
|
||||
Generated
+2137
File diff suppressed because it is too large
Load Diff
+13
@@ -17,3 +17,16 @@ members = [
|
||||
|
||||
[workspace.metadata.spellcheck]
|
||||
config = "spellcheck.toml"
|
||||
|
||||
[workspace.lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = [
|
||||
'cfg(fuzzing)',
|
||||
'cfg(loom)',
|
||||
'cfg(mio_unsupported_force_poll_poll)',
|
||||
'cfg(tokio_allow_from_blocking_fd)',
|
||||
'cfg(tokio_internal_mt_counters)',
|
||||
'cfg(tokio_no_parking_lot)',
|
||||
'cfg(tokio_no_tuning_tests)',
|
||||
'cfg(tokio_taskdump)',
|
||||
'cfg(tokio_unstable)',
|
||||
] }
|
||||
|
||||
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.42.0", features = ["full"] }
|
||||
tokio = { version = "1.43.4", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
@@ -95,3 +95,6 @@ path = "named-pipe-multi-client.rs"
|
||||
[[example]]
|
||||
name = "dump"
|
||||
path = "dump.rs"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -25,7 +25,7 @@ error[E0308]: mismatched types
|
||||
found enum `Result<(), _>`
|
||||
help: a return type might be missing here
|
||||
|
|
||||
9 | async fn missing_return_type() -> _ {
|
||||
9 | async fn missing_return_type() -> _ {
|
||||
| ++++
|
||||
help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panicking if the value is a `Result::Err`
|
||||
|
|
||||
|
||||
@@ -1,3 +1,113 @@
|
||||
# 1.43.4 (January 3rd, 2026)
|
||||
|
||||
### Fixed
|
||||
|
||||
* sync: return `TryRecvError::Disconnected` from `Receiver::try_recv` after `Receiver::close` ([#7686])
|
||||
|
||||
[#7686]: https://github.com/tokio-rs/tokio/pull/7686
|
||||
|
||||
# 1.43.3 (October 14th, 2025)
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: use release ordering in `wake_by_ref()` even if already woken ([#7622])
|
||||
- sync: close the `broadcast::Sender` in `broadcast::Sender::new()` ([#7629])
|
||||
- process: fix error when runtime is shut down on nightly-2025-10-12 ([#7672])
|
||||
|
||||
[#7622]: https://github.com/tokio-rs/tokio/pull/7622
|
||||
[#7629]: https://github.com/tokio-rs/tokio/pull/7629
|
||||
[#7672]: https://github.com/tokio-rs/tokio/pull/7672
|
||||
|
||||
# 1.43.2 (August 1st, 2025)
|
||||
|
||||
### Fixed
|
||||
|
||||
- process: fix panic from spurious pidfd wakeup ([#7494])
|
||||
|
||||
[#7494]: https://github.com/tokio-rs/tokio/pull/7494
|
||||
|
||||
# 1.43.1 (April 5th, 2025)
|
||||
|
||||
This release fixes a soundness issue in the broadcast channel. The channel
|
||||
accepts values that are `Send` but `!Sync`. Previously, the channel called
|
||||
`clone()` on these values without synchronizing. This release fixes the channel
|
||||
by synchronizing calls to `.clone()` (Thanks Austin Bonander for finding and
|
||||
reporting the issue).
|
||||
|
||||
### Fixed
|
||||
|
||||
- sync: synchronize `clone()` call in broadcast channel ([#7232])
|
||||
|
||||
[#7232]: https://github.com/tokio-rs/tokio/pull/7232
|
||||
|
||||
# 1.43.0 (Jan 8th, 2025)
|
||||
|
||||
### Added
|
||||
|
||||
- net: add `UdpSocket::peek` methods ([#7068])
|
||||
- net: add support for Haiku OS ([#7042])
|
||||
- process: add `Command::into_std()` ([#7014])
|
||||
- signal: add `SignalKind::info` on illumos ([#6995])
|
||||
- signal: add support for realtime signals on illumos ([#7029])
|
||||
|
||||
### Fixed
|
||||
|
||||
- io: don't call `set_len` before initializing vector in `Blocking` ([#7054])
|
||||
- macros: suppress `clippy::needless_return` in `#[tokio::main]` ([#6874])
|
||||
- runtime: fix thread parking on WebAssembly ([#7041])
|
||||
|
||||
### Changes
|
||||
|
||||
- chore: use unsync loads for `unsync_load` ([#7073])
|
||||
- io: use `Buf::put_bytes` in `Repeat` read impl ([#7055])
|
||||
- task: drop the join waker of a task eagerly ([#6986])
|
||||
|
||||
### Changes to unstable APIs
|
||||
|
||||
- metrics: improve flexibility of H2Histogram Configuration ([#6963])
|
||||
- taskdump: add accessor methods for backtrace ([#6975])
|
||||
|
||||
### Documented
|
||||
|
||||
- io: clarify `ReadBuf::uninit` allows initialized buffers as well ([#7053])
|
||||
- net: fix ambiguity in `TcpStream::try_write_vectored` docs ([#7067])
|
||||
- runtime: fix `LocalRuntime` doc links ([#7074])
|
||||
- sync: extend documentation for `watch::Receiver::wait_for` ([#7038])
|
||||
- sync: fix typos in `OnceCell` docs ([#7047])
|
||||
|
||||
[#6874]: https://github.com/tokio-rs/tokio/pull/6874
|
||||
[#6963]: https://github.com/tokio-rs/tokio/pull/6963
|
||||
[#6975]: https://github.com/tokio-rs/tokio/pull/6975
|
||||
[#6986]: https://github.com/tokio-rs/tokio/pull/6986
|
||||
[#6995]: https://github.com/tokio-rs/tokio/pull/6995
|
||||
[#7014]: https://github.com/tokio-rs/tokio/pull/7014
|
||||
[#7029]: https://github.com/tokio-rs/tokio/pull/7029
|
||||
[#7038]: https://github.com/tokio-rs/tokio/pull/7038
|
||||
[#7041]: https://github.com/tokio-rs/tokio/pull/7041
|
||||
[#7042]: https://github.com/tokio-rs/tokio/pull/7042
|
||||
[#7047]: https://github.com/tokio-rs/tokio/pull/7047
|
||||
[#7053]: https://github.com/tokio-rs/tokio/pull/7053
|
||||
[#7054]: https://github.com/tokio-rs/tokio/pull/7054
|
||||
[#7055]: https://github.com/tokio-rs/tokio/pull/7055
|
||||
[#7067]: https://github.com/tokio-rs/tokio/pull/7067
|
||||
[#7068]: https://github.com/tokio-rs/tokio/pull/7068
|
||||
[#7073]: https://github.com/tokio-rs/tokio/pull/7073
|
||||
[#7074]: https://github.com/tokio-rs/tokio/pull/7074
|
||||
|
||||
# 1.42.1 (April 8th, 2025)
|
||||
|
||||
This release fixes a soundness issue in the broadcast channel. The channel
|
||||
accepts values that are `Send` but `!Sync`. Previously, the channel called
|
||||
`clone()` on these values without synchronizing. This release fixes the channel
|
||||
by synchronizing calls to `.clone()` (Thanks Austin Bonander for finding and
|
||||
reporting the issue).
|
||||
|
||||
### Fixed
|
||||
|
||||
- sync: synchronize `clone()` call in broadcast channel ([#7232])
|
||||
|
||||
[#7232]: https://github.com/tokio-rs/tokio/pull/7232
|
||||
|
||||
# 1.42.0 (Dec 3rd, 2024)
|
||||
|
||||
### Added
|
||||
@@ -249,6 +359,20 @@ Yanked. Please use 1.39.1 instead.
|
||||
[#6709]: https://github.com/tokio-rs/tokio/pull/6709
|
||||
[#6710]: https://github.com/tokio-rs/tokio/pull/6710
|
||||
|
||||
# 1.38.2 (April 2nd, 2025)
|
||||
|
||||
This release fixes a soundness issue in the broadcast channel. The channel
|
||||
accepts values that are `Send` but `!Sync`. Previously, the channel called
|
||||
`clone()` on these values without synchronizing. This release fixes the channel
|
||||
by synchronizing calls to `.clone()` (Thanks Austin Bonander for finding and
|
||||
reporting the issue).
|
||||
|
||||
### Fixed
|
||||
|
||||
- sync: synchronize `clone()` call in broadcast channel ([#7232])
|
||||
|
||||
[#7232]: https://github.com/tokio-rs/tokio/pull/7232
|
||||
|
||||
# 1.38.1 (July 16th, 2024)
|
||||
|
||||
This release fixes the bug identified as ([#6682]), which caused timers not
|
||||
|
||||
+4
-1
@@ -6,7 +6,7 @@ name = "tokio"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v1.x.y" git tag.
|
||||
version = "1.42.0"
|
||||
version = "1.43.4"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
@@ -173,3 +173,6 @@ allowed_external_types = [
|
||||
"bytes::buf::buf_mut::BufMut",
|
||||
"tokio_macros::*",
|
||||
]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+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.42.0", features = ["full"] }
|
||||
tokio = { version = "1.43.4", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ impl<E: Source> PollEvented<E> {
|
||||
}
|
||||
|
||||
/// Returns a reference to the registration.
|
||||
#[cfg(feature = "net")]
|
||||
#[cfg(any(feature = "net", all(feature = "process", target_os = "linux")))]
|
||||
pub(crate) fn registration(&self) -> &Registration {
|
||||
&self.registration
|
||||
}
|
||||
@@ -138,14 +138,6 @@ impl<E: Source> PollEvented<E> {
|
||||
Ok(inner)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "process", target_os = "linux"))]
|
||||
pub(crate) fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.registration
|
||||
.poll_read_ready(cx)
|
||||
.map_err(io::Error::from)
|
||||
.map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Re-register under new runtime with `interest`.
|
||||
#[cfg(all(feature = "process", target_os = "linux"))]
|
||||
pub(crate) fn reregister(&mut self, interest: Interest) -> io::Result<()> {
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::{
|
||||
pin::Pin,
|
||||
process::ExitStatus,
|
||||
sync::atomic::{AtomicBool, Ordering::Relaxed},
|
||||
task::{ready, Context, Poll},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -95,14 +95,41 @@ where
|
||||
pidfd: PollEvented<Pidfd>,
|
||||
}
|
||||
|
||||
fn display_eq(d: impl std::fmt::Display, s: &str) -> bool {
|
||||
use std::fmt::Write;
|
||||
|
||||
struct FormatEq<'r> {
|
||||
remainder: &'r str,
|
||||
unequal: bool,
|
||||
}
|
||||
|
||||
impl<'r> Write for FormatEq<'r> {
|
||||
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||
if !self.unequal {
|
||||
if let Some(new_remainder) = self.remainder.strip_prefix(s) {
|
||||
self.remainder = new_remainder;
|
||||
} else {
|
||||
self.unequal = true;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut fmt_eq = FormatEq {
|
||||
remainder: s,
|
||||
unequal: false,
|
||||
};
|
||||
let _ = write!(fmt_eq, "{d}");
|
||||
fmt_eq.remainder.is_empty() && !fmt_eq.unequal
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn is_rt_shutdown_err(err: &io::Error) -> bool {
|
||||
if let Some(inner) = err.get_ref() {
|
||||
// Using `Error::description()` is more efficient than `format!("{inner}")`,
|
||||
// so we use it here even if it is deprecated.
|
||||
err.kind() == io::ErrorKind::Other
|
||||
&& inner.source().is_none()
|
||||
&& inner.description() == RUNTIME_SHUTTING_DOWN_ERROR
|
||||
&& display_eq(inner, RUNTIME_SHUTTING_DOWN_ERROR)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -117,17 +144,21 @@ where
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = Pin::into_inner(self);
|
||||
|
||||
match ready!(this.pidfd.poll_read_ready(cx)) {
|
||||
Err(err) if is_rt_shutdown_err(&err) => {
|
||||
this.pidfd.reregister(Interest::READABLE)?;
|
||||
ready!(this.pidfd.poll_read_ready(cx))?
|
||||
match this.pidfd.registration().poll_read_ready(cx) {
|
||||
Poll::Ready(Ok(evt)) => {
|
||||
if let Some(exit_code) = this.inner.try_wait()? {
|
||||
return Poll::Ready(Ok(exit_code));
|
||||
}
|
||||
this.pidfd.registration().clear_readiness(evt);
|
||||
}
|
||||
res => res?,
|
||||
}
|
||||
Poll::Ready(Ok(this
|
||||
.inner
|
||||
.try_wait()?
|
||||
.expect("pidfd is ready to read, the process should have exited")))
|
||||
Poll::Ready(Err(err)) if is_rt_shutdown_err(&err) => {}
|
||||
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
|
||||
Poll::Pending => return Poll::Pending,
|
||||
};
|
||||
|
||||
this.pidfd.reregister(Interest::READABLE)?;
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -252,9 +252,15 @@ impl State {
|
||||
/// Transitions the state to `NOTIFIED`.
|
||||
pub(super) fn transition_to_notified_by_ref(&self) -> TransitionToNotifiedByRef {
|
||||
self.fetch_update_action(|mut snapshot| {
|
||||
if snapshot.is_complete() || snapshot.is_notified() {
|
||||
// There is nothing to do in this case.
|
||||
if snapshot.is_complete() {
|
||||
// The complete state is final
|
||||
(TransitionToNotifiedByRef::DoNothing, None)
|
||||
} else if snapshot.is_notified() {
|
||||
// Even hough we have nothing to do in this branch,
|
||||
// wake_by_ref() should synchronize-with the task starting execution,
|
||||
// therefore we must use an Release store (with the same value),
|
||||
// to pair with the Acquire in transition_to_running.
|
||||
(TransitionToNotifiedByRef::DoNothing, Some(snapshot))
|
||||
} else if snapshot.is_running() {
|
||||
// If the task is running, we mark it as notified, but we should
|
||||
// not submit as the thread currently running the future is
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::runtime::scheduler::multi_thread::{queue, Stats};
|
||||
use crate::runtime::task::{self, Schedule, Task, TaskHarnessScheduleHooks};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::thread;
|
||||
@@ -272,22 +271,3 @@ fn stress2() {
|
||||
assert_eq!(num_pop, NUM_TASKS);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Runtime;
|
||||
|
||||
impl Schedule for Runtime {
|
||||
fn release(&self, _task: &Task<Self>) -> Option<Task<Self>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn schedule(&self, _task: task::Notified<Self>) {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn hooks(&self) -> TaskHarnessScheduleHooks {
|
||||
TaskHarnessScheduleHooks {
|
||||
task_terminate_callback: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-29
@@ -118,7 +118,7 @@
|
||||
|
||||
use crate::loom::cell::UnsafeCell;
|
||||
use crate::loom::sync::atomic::{AtomicBool, AtomicUsize};
|
||||
use crate::loom::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard};
|
||||
use crate::loom::sync::{Arc, Mutex, MutexGuard};
|
||||
use crate::runtime::coop::cooperative;
|
||||
use crate::util::linked_list::{self, GuardedLinkedList, LinkedList};
|
||||
use crate::util::WakeList;
|
||||
@@ -304,7 +304,7 @@ use self::error::{RecvError, SendError, TryRecvError};
|
||||
/// Data shared between senders and receivers.
|
||||
struct Shared<T> {
|
||||
/// slots in the channel.
|
||||
buffer: Box<[RwLock<Slot<T>>]>,
|
||||
buffer: Box<[Mutex<Slot<T>>]>,
|
||||
|
||||
/// Mask a position -> index.
|
||||
mask: usize,
|
||||
@@ -348,7 +348,7 @@ struct Slot<T> {
|
||||
///
|
||||
/// The value is set by `send` when the write lock is held. When a reader
|
||||
/// drops, `rem` is decremented. When it hits zero, the value is dropped.
|
||||
val: UnsafeCell<Option<T>>,
|
||||
val: Option<T>,
|
||||
}
|
||||
|
||||
/// An entry in the wait queue.
|
||||
@@ -386,7 +386,7 @@ generate_addr_of_methods! {
|
||||
}
|
||||
|
||||
struct RecvGuard<'a, T> {
|
||||
slot: RwLockReadGuard<'a, Slot<T>>,
|
||||
slot: MutexGuard<'a, Slot<T>>,
|
||||
}
|
||||
|
||||
/// Receive a value future.
|
||||
@@ -395,11 +395,15 @@ struct Recv<'a, T> {
|
||||
receiver: &'a mut Receiver<T>,
|
||||
|
||||
/// Entry in the waiter `LinkedList`.
|
||||
waiter: UnsafeCell<Waiter>,
|
||||
waiter: WaiterCell,
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: Send> Send for Recv<'a, T> {}
|
||||
unsafe impl<'a, T: Send> Sync for Recv<'a, T> {}
|
||||
// The wrapper around `UnsafeCell` isolates the unsafe impl `Send` and `Sync`
|
||||
// from `Recv`.
|
||||
struct WaiterCell(UnsafeCell<Waiter>);
|
||||
|
||||
unsafe impl Send for WaiterCell {}
|
||||
unsafe impl Sync for WaiterCell {}
|
||||
|
||||
/// Max number of receivers. Reserve space to lock.
|
||||
const MAX_RECEIVERS: usize = usize::MAX >> 2;
|
||||
@@ -467,12 +471,6 @@ pub fn channel<T: Clone>(capacity: usize) -> (Sender<T>, Receiver<T>) {
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for Sender<T> {}
|
||||
unsafe impl<T: Send> Sync for Sender<T> {}
|
||||
|
||||
unsafe impl<T: Send> Send for Receiver<T> {}
|
||||
unsafe impl<T: Send> Sync for Receiver<T> {}
|
||||
|
||||
impl<T> Sender<T> {
|
||||
/// Creates the sending-half of the [`broadcast`] channel.
|
||||
///
|
||||
@@ -511,10 +509,10 @@ impl<T> Sender<T> {
|
||||
let mut buffer = Vec::with_capacity(capacity);
|
||||
|
||||
for i in 0..capacity {
|
||||
buffer.push(RwLock::new(Slot {
|
||||
buffer.push(Mutex::new(Slot {
|
||||
rem: AtomicUsize::new(0),
|
||||
pos: (i as u64).wrapping_sub(capacity as u64),
|
||||
val: UnsafeCell::new(None),
|
||||
val: None,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -524,7 +522,7 @@ impl<T> Sender<T> {
|
||||
tail: Mutex::new(Tail {
|
||||
pos: 0,
|
||||
rx_cnt: receiver_count,
|
||||
closed: false,
|
||||
closed: receiver_count == 0,
|
||||
waiters: LinkedList::new(),
|
||||
}),
|
||||
num_tx: AtomicUsize::new(1),
|
||||
@@ -600,7 +598,7 @@ impl<T> Sender<T> {
|
||||
tail.pos = tail.pos.wrapping_add(1);
|
||||
|
||||
// Get the slot
|
||||
let mut slot = self.shared.buffer[idx].write();
|
||||
let mut slot = self.shared.buffer[idx].lock();
|
||||
|
||||
// Track the position
|
||||
slot.pos = pos;
|
||||
@@ -609,7 +607,7 @@ impl<T> Sender<T> {
|
||||
slot.rem.with_mut(|v| *v = rem);
|
||||
|
||||
// Write the value
|
||||
slot.val = UnsafeCell::new(Some(value));
|
||||
slot.val = Some(value);
|
||||
|
||||
// Release the slot lock before notifying the receivers.
|
||||
drop(slot);
|
||||
@@ -696,7 +694,7 @@ impl<T> Sender<T> {
|
||||
while low < high {
|
||||
let mid = low + (high - low) / 2;
|
||||
let idx = base_idx.wrapping_add(mid) & self.shared.mask;
|
||||
if self.shared.buffer[idx].read().rem.load(SeqCst) == 0 {
|
||||
if self.shared.buffer[idx].lock().rem.load(SeqCst) == 0 {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
high = mid;
|
||||
@@ -738,7 +736,7 @@ impl<T> Sender<T> {
|
||||
let tail = self.shared.tail.lock();
|
||||
|
||||
let idx = (tail.pos.wrapping_sub(1) & self.shared.mask as u64) as usize;
|
||||
self.shared.buffer[idx].read().rem.load(SeqCst) == 0
|
||||
self.shared.buffer[idx].lock().rem.load(SeqCst) == 0
|
||||
}
|
||||
|
||||
/// Returns the number of active receivers.
|
||||
@@ -1058,7 +1056,7 @@ impl<T> Receiver<T> {
|
||||
let idx = (self.next & self.shared.mask as u64) as usize;
|
||||
|
||||
// The slot holding the next value to read
|
||||
let mut slot = self.shared.buffer[idx].read();
|
||||
let mut slot = self.shared.buffer[idx].lock();
|
||||
|
||||
if slot.pos != self.next {
|
||||
// Release the `slot` lock before attempting to acquire the `tail`
|
||||
@@ -1075,7 +1073,7 @@ impl<T> Receiver<T> {
|
||||
let mut tail = self.shared.tail.lock();
|
||||
|
||||
// Acquire slot lock again
|
||||
slot = self.shared.buffer[idx].read();
|
||||
slot = self.shared.buffer[idx].lock();
|
||||
|
||||
// Make sure the position did not change. This could happen in the
|
||||
// unlikely event that the buffer is wrapped between dropping the
|
||||
@@ -1367,12 +1365,12 @@ impl<'a, T> Recv<'a, T> {
|
||||
fn new(receiver: &'a mut Receiver<T>) -> Recv<'a, T> {
|
||||
Recv {
|
||||
receiver,
|
||||
waiter: UnsafeCell::new(Waiter {
|
||||
waiter: WaiterCell(UnsafeCell::new(Waiter {
|
||||
queued: AtomicBool::new(false),
|
||||
waker: None,
|
||||
pointers: linked_list::Pointers::new(),
|
||||
_p: PhantomPinned,
|
||||
}),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1384,7 +1382,7 @@ impl<'a, T> Recv<'a, T> {
|
||||
is_unpin::<&mut Receiver<T>>();
|
||||
|
||||
let me = self.get_unchecked_mut();
|
||||
(me.receiver, &me.waiter)
|
||||
(me.receiver, &me.waiter.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1418,6 +1416,7 @@ impl<'a, T> Drop for Recv<'a, T> {
|
||||
// `Shared::notify_rx` before we drop the object.
|
||||
let queued = self
|
||||
.waiter
|
||||
.0
|
||||
.with(|ptr| unsafe { (*ptr).queued.load(Acquire) });
|
||||
|
||||
// If the waiter is queued, we need to unlink it from the waiters list.
|
||||
@@ -1432,6 +1431,7 @@ impl<'a, T> Drop for Recv<'a, T> {
|
||||
// `Relaxed` order suffices because we hold the tail lock.
|
||||
let queued = self
|
||||
.waiter
|
||||
.0
|
||||
.with_mut(|ptr| unsafe { (*ptr).queued.load(Relaxed) });
|
||||
|
||||
if queued {
|
||||
@@ -1440,7 +1440,7 @@ impl<'a, T> Drop for Recv<'a, T> {
|
||||
// safety: tail lock is held and the wait node is verified to be in
|
||||
// the list.
|
||||
unsafe {
|
||||
self.waiter.with_mut(|ptr| {
|
||||
self.waiter.0.with_mut(|ptr| {
|
||||
tail.waiters.remove((&mut *ptr).into());
|
||||
});
|
||||
}
|
||||
@@ -1486,7 +1486,7 @@ impl<'a, T> RecvGuard<'a, T> {
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
self.slot.val.with(|ptr| unsafe { (*ptr).clone() })
|
||||
self.slot.val.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1494,8 +1494,7 @@ impl<'a, T> Drop for RecvGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
// Decrement the remaining counter
|
||||
if 1 == self.slot.rem.fetch_sub(1, SeqCst) {
|
||||
// Safety: Last receiver, drop the value
|
||||
self.slot.val.with_mut(|ptr| unsafe { *ptr = None });
|
||||
self.slot.val = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,6 +439,10 @@ impl<T, S: Semaphore> Rx<T, S> {
|
||||
return Ok(value);
|
||||
}
|
||||
TryPopResult::Closed => return Err(TryRecvError::Disconnected),
|
||||
// If close() was called, an empty queue should report Disconnected.
|
||||
TryPopResult::Empty if rx_fields.rx_closed => {
|
||||
return Err(TryRecvError::Disconnected)
|
||||
}
|
||||
TryPopResult::Empty => return Err(TryRecvError::Empty),
|
||||
TryPopResult::Busy => {} // fall through
|
||||
}
|
||||
|
||||
@@ -35,8 +35,14 @@ pub(crate) enum TryPopResult<T> {
|
||||
/// Successfully popped a value.
|
||||
Ok(T),
|
||||
/// The channel is empty.
|
||||
///
|
||||
/// Note that `list.rs` only tracks the close state set by senders. If the
|
||||
/// channel is closed by `Rx::close()`, then `TryPopResult::Empty` is still
|
||||
/// returned, and the close state needs to be handled by `chan.rs`.
|
||||
Empty,
|
||||
/// The channel is empty and closed.
|
||||
///
|
||||
/// Returned when the send half is closed (all senders dropped).
|
||||
Closed,
|
||||
/// The channel is not empty, but the first value is being written.
|
||||
Busy,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#![cfg(feature = "process")]
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![cfg(target_os = "linux")]
|
||||
#![cfg(not(miri))]
|
||||
|
||||
use tokio::process::Command;
|
||||
use tokio::time::{sleep, Duration};
|
||||
|
||||
#[tokio::test]
|
||||
async fn issue_7144() {
|
||||
let mut threads = vec![];
|
||||
for _ in 0..20 {
|
||||
threads.push(tokio::spawn(test_one()));
|
||||
}
|
||||
for thread in threads {
|
||||
thread.await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
async fn test_one() {
|
||||
let mut t = Command::new("strace")
|
||||
.args("-o /dev/null -D sleep 5".split(' '))
|
||||
.spawn()
|
||||
.unwrap();
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
unsafe { libc::kill(t.id().unwrap() as _, libc::SIGINT) };
|
||||
t.wait().await.unwrap();
|
||||
}
|
||||
@@ -7,9 +7,7 @@ use wasm_bindgen_test::wasm_bindgen_test as test;
|
||||
|
||||
use tokio::sync::broadcast;
|
||||
use tokio_test::task;
|
||||
use tokio_test::{
|
||||
assert_err, assert_ok, assert_pending, assert_ready, assert_ready_err, assert_ready_ok,
|
||||
};
|
||||
use tokio_test::{assert_err, assert_ok, assert_pending, assert_ready_err, assert_ready_ok};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -656,3 +654,15 @@ async fn receiver_recv_is_cooperative() {
|
||||
_ = tokio::task::yield_now() => {},
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn broadcast_sender_new_must_be_closed() {
|
||||
let capacity = 1;
|
||||
let tx: broadcast::Sender<()> = broadcast::Sender::new(capacity);
|
||||
|
||||
tx.send(()).unwrap_err();
|
||||
|
||||
let _rx = tx.subscribe();
|
||||
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
|
||||
@@ -966,6 +966,15 @@ fn try_recv_unbounded() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_recv_after_receiver_close() {
|
||||
let (_tx, mut rx) = mpsc::channel::<()>(5);
|
||||
|
||||
assert_eq!(Err(TryRecvError::Empty), rx.try_recv());
|
||||
rx.close();
|
||||
assert_eq!(Err(TryRecvError::Disconnected), rx.try_recv());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_recv_close_while_empty_bounded() {
|
||||
let (tx, mut rx) = mpsc::channel::<()>(5);
|
||||
|
||||
Reference in New Issue
Block a user