diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd46e1c65..7e339daf7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,18 +56,23 @@ jobs: - rust: nightly os: ubuntu-latest target: aarch64-unknown-linux-gnu + - rust: nightly + os: ubuntu-latest + target: armv7-unknown-linux-gnueabihf # Test 32-bit target that does not have AtomicU64/AtomicI64. - rust: nightly os: ubuntu-latest target: armv5te-unknown-linux-gnueabi runs-on: ${{ matrix.os }} + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} - - name: Install cross - uses: taiki-e/install-action@cross + - uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: ${{ matrix.target }} if: matrix.target != '' - name: Test run: ci/test.sh @@ -83,8 +88,9 @@ jobs: - msrv - nightly runs-on: ubuntu-latest + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} if: matrix.rust != 'msrv' @@ -96,8 +102,9 @@ jobs: # Check for duplicate dependencies. dependencies: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Install cargo-hack @@ -113,8 +120,9 @@ jobs: permissions: contents: write pull-requests: write + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update stable - run: ci/no_atomic.sh @@ -146,8 +154,9 @@ jobs: # Check formatting. rustfmt: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update stable - name: rustfmt @@ -158,8 +167,9 @@ jobs: # # Check clippy. # clippy: # runs-on: ubuntu-latest + # timeout-minutes: 60 # steps: - # - uses: actions/checkout@v4 + # - uses: taiki-e/checkout-action@v1 # - name: Install Rust # run: rustup update stable # - name: clippy @@ -168,28 +178,46 @@ jobs: # Run miri. miri: runs-on: ubuntu-latest + timeout-minutes: 120 # TODO steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup toolchain install nightly --component miri && rustup default nightly - name: miri run: ci/miri.sh + # Run cargo-careful. + careful: + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: taiki-e/checkout-action@v1 + - name: Install Rust + run: rustup toolchain install nightly --component rust-src && rustup default nightly + - name: Install cargo-careful + run: cargo install cargo-careful --locked --debug + - name: Run cargo-careful + run: ./ci/careful.sh + # Run sanitizers. san: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update nightly && rustup default nightly + # https://github.com/google/sanitizers/issues/1716 / https://github.com/actions/runner-images/issues/9491 + - run: sudo sysctl vm.mmap_rnd_bits=28 - name: Run sanitizers run: ci/san.sh # Run loom tests. loom: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update stable - name: loom @@ -198,8 +226,9 @@ jobs: # Check if the document can be generated without warning. docs: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update nightly && rustup default nightly - name: docs @@ -207,8 +236,9 @@ jobs: shellcheck: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install shellcheck uses: taiki-e/install-action@shellcheck - run: shellcheck $(git ls-files '*.sh') @@ -230,6 +260,7 @@ jobs: - rustfmt # - clippy - miri + - careful - san - loom - docs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 514f11b5d..739bfce65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,8 +17,9 @@ jobs: create-release: if: github.repository_owner == 'crossbeam-rs' runs-on: ubuntu-latest + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - uses: taiki-e/create-gh-release-action@v1 with: prefix: crossbeam(-[a-z]+)? diff --git a/Cargo.toml b/Cargo.toml index 63ef92bde..14f46133e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,9 @@ crossbeam-utils = { version = "0.8.18", path = "crossbeam-utils", default-featur [dev-dependencies] rand = "0.8" +[lints] +workspace = true + [workspace] resolver = "2" members = [ @@ -62,3 +65,9 @@ members = [ "crossbeam-skiplist", "crossbeam-utils", ] + +[workspace.lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = [ + 'cfg(crossbeam_loom)', + 'cfg(crossbeam_sanitize)', +] } diff --git a/ci/careful.sh b/ci/careful.sh new file mode 100755 index 000000000..dffe2e734 --- /dev/null +++ b/ci/careful.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -euxo pipefail +IFS=$'\n\t' +cd "$(dirname "$0")"/.. + +export RUSTFLAGS="${RUSTFLAGS:-} -Z randomize-layout" + +cargo careful test --all --all-features --exclude benchmarks -- --test-threads=1 diff --git a/ci/test.sh b/ci/test.sh index ab70b5e2e..b06a20a6d 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -3,9 +3,9 @@ set -euxo pipefail IFS=$'\n\t' cd "$(dirname "$0")"/.. +# shellcheck disable=SC2086 if [[ -n "${RUST_TARGET:-}" ]]; then - # If RUST_TARGET is specified, use cross for testing. - cross test --all --target "$RUST_TARGET" --exclude benchmarks -- --test-threads=1 + cargo test --all --target "$RUST_TARGET" --exclude benchmarks ${DOCTEST_XCOMPILE:-} -- --test-threads=1 # For now, the non-host target only runs tests. exit 0 diff --git a/crossbeam-channel/CHANGELOG.md b/crossbeam-channel/CHANGELOG.md index c9f834486..4d7fa0f18 100644 --- a/crossbeam-channel/CHANGELOG.md +++ b/crossbeam-channel/CHANGELOG.md @@ -1,3 +1,7 @@ +# Version 0.5.13 + +- Add `select_biased!` macro. (#1040) + # Version 0.5.12 - Fix memory leak in unbounded channel. (#1084) diff --git a/crossbeam-channel/Cargo.toml b/crossbeam-channel/Cargo.toml index e70b4dd97..8d4adf8aa 100644 --- a/crossbeam-channel/Cargo.toml +++ b/crossbeam-channel/Cargo.toml @@ -4,7 +4,7 @@ name = "crossbeam-channel" # - Update CHANGELOG.md # - Update README.md (when increasing major or minor version) # - Run './tools/publish.sh crossbeam-channel ' -version = "0.5.12" +version = "0.5.13" edition = "2021" rust-version = "1.60" license = "MIT OR Apache-2.0" @@ -30,3 +30,6 @@ crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-fea num_cpus = "1.13.0" rand = "0.8" signal-hook = "0.3" + +[lints] +workspace = true diff --git a/crossbeam-channel/benchmarks/Cargo.toml b/crossbeam-channel/benchmarks/Cargo.toml index 751fdcedc..43b7d8426 100644 --- a/crossbeam-channel/benchmarks/Cargo.toml +++ b/crossbeam-channel/benchmarks/Cargo.toml @@ -75,3 +75,6 @@ doc = false name = "mpmc" path = "mpmc.rs" doc = false + +[lints] +workspace = true diff --git a/crossbeam-channel/src/flavors/array.rs b/crossbeam-channel/src/flavors/array.rs index f08bf5863..4087c054a 100644 --- a/crossbeam-channel/src/flavors/array.rs +++ b/crossbeam-channel/src/flavors/array.rs @@ -243,7 +243,7 @@ impl Channel { let slot = unsafe { self.buffer.get_unchecked(index) }; let stamp = slot.stamp.load(Ordering::Acquire); - // If the the stamp is ahead of the head by 1, we may attempt to pop. + // If the stamp is ahead of the head by 1, we may attempt to pop. if head + 1 == stamp { let new = if index + 1 < self.cap { // Same lap, incremented index. diff --git a/crossbeam-channel/src/flavors/list.rs b/crossbeam-channel/src/flavors/list.rs index 6d7ba7ebf..e7fb6150f 100644 --- a/crossbeam-channel/src/flavors/list.rs +++ b/crossbeam-channel/src/flavors/list.rs @@ -583,6 +583,9 @@ impl Channel { } let mut head = self.head.index.load(Ordering::Acquire); + // The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts + // to initialize the first block before noticing that the receivers disconnected. Late allocations + // will be deallocated by the sender in Drop let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel); // If we're going to be dropping messages we need to synchronize with initialization diff --git a/crossbeam-channel/src/select.rs b/crossbeam-channel/src/select.rs index 20d6ef4e9..4bfa65c7f 100644 --- a/crossbeam-channel/src/select.rs +++ b/crossbeam-channel/src/select.rs @@ -177,6 +177,7 @@ enum Timeout { fn run_select( handles: &mut [(&dyn SelectHandle, usize, *const u8)], timeout: Timeout, + is_biased: bool, ) -> Option<(Token, usize, *const u8)> { if handles.is_empty() { // Wait until the timeout and return. @@ -193,8 +194,10 @@ fn run_select( } } - // Shuffle the operations for fairness. - utils::shuffle(handles); + if !is_biased { + // Shuffle the operations for fairness. + utils::shuffle(handles); + } // Create a token, which serves as a temporary variable that gets initialized in this function // and is later used by a call to `channel::read()` or `channel::write()` that completes the @@ -325,6 +328,7 @@ fn run_select( fn run_ready( handles: &mut [(&dyn SelectHandle, usize, *const u8)], timeout: Timeout, + is_biased: bool, ) -> Option { if handles.is_empty() { // Wait until the timeout and return. @@ -341,8 +345,10 @@ fn run_ready( } } - // Shuffle the operations for fairness. - utils::shuffle(handles); + if !is_biased { + // Shuffle the operations for fairness. + utils::shuffle(handles); + } loop { let backoff = Backoff::new(); @@ -450,8 +456,9 @@ fn run_ready( #[inline] pub fn try_select<'a>( handles: &mut [(&'a dyn SelectHandle, usize, *const u8)], + is_biased: bool, ) -> Result, TrySelectError> { - match run_select(handles, Timeout::Now) { + match run_select(handles, Timeout::Now, is_biased) { None => Err(TrySelectError), Some((token, index, ptr)) => Ok(SelectedOperation { token, @@ -467,12 +474,13 @@ pub fn try_select<'a>( #[inline] pub fn select<'a>( handles: &mut [(&'a dyn SelectHandle, usize, *const u8)], + is_biased: bool, ) -> SelectedOperation<'a> { if handles.is_empty() { panic!("no operations have been added to `Select`"); } - let (token, index, ptr) = run_select(handles, Timeout::Never).unwrap(); + let (token, index, ptr) = run_select(handles, Timeout::Never, is_biased).unwrap(); SelectedOperation { token, index, @@ -487,10 +495,11 @@ pub fn select<'a>( pub fn select_timeout<'a>( handles: &mut [(&'a dyn SelectHandle, usize, *const u8)], timeout: Duration, + is_biased: bool, ) -> Result, SelectTimeoutError> { match Instant::now().checked_add(timeout) { - Some(deadline) => select_deadline(handles, deadline), - None => Ok(select(handles)), + Some(deadline) => select_deadline(handles, deadline, is_biased), + None => Ok(select(handles, is_biased)), } } @@ -499,8 +508,9 @@ pub fn select_timeout<'a>( pub(crate) fn select_deadline<'a>( handles: &mut [(&'a dyn SelectHandle, usize, *const u8)], deadline: Instant, + is_biased: bool, ) -> Result, SelectTimeoutError> { - match run_select(handles, Timeout::At(deadline)) { + match run_select(handles, Timeout::At(deadline), is_biased) { None => Err(SelectTimeoutError), Some((token, index, ptr)) => Ok(SelectedOperation { token, @@ -764,7 +774,7 @@ impl<'a> Select<'a> { /// } /// ``` pub fn try_select(&mut self) -> Result, TrySelectError> { - try_select(&mut self.handles) + try_select(&mut self.handles, false) } /// Blocks until one of the operations becomes ready and selects it. @@ -811,7 +821,7 @@ impl<'a> Select<'a> { /// } /// ``` pub fn select(&mut self) -> SelectedOperation<'a> { - select(&mut self.handles) + select(&mut self.handles, false) } /// Blocks for a limited time until one of the operations becomes ready and selects it. @@ -861,7 +871,7 @@ impl<'a> Select<'a> { &mut self, timeout: Duration, ) -> Result, SelectTimeoutError> { - select_timeout(&mut self.handles, timeout) + select_timeout(&mut self.handles, timeout, false) } /// Blocks until a given deadline, or until one of the operations becomes ready and selects it. @@ -913,7 +923,7 @@ impl<'a> Select<'a> { &mut self, deadline: Instant, ) -> Result, SelectTimeoutError> { - select_deadline(&mut self.handles, deadline) + select_deadline(&mut self.handles, deadline, false) } /// Attempts to find a ready operation without blocking. @@ -952,7 +962,7 @@ impl<'a> Select<'a> { /// } /// ``` pub fn try_ready(&mut self) -> Result { - match run_ready(&mut self.handles, Timeout::Now) { + match run_ready(&mut self.handles, Timeout::Now, false) { None => Err(TryReadyError), Some(index) => Ok(index), } @@ -1005,7 +1015,7 @@ impl<'a> Select<'a> { panic!("no operations have been added to `Select`"); } - run_ready(&mut self.handles, Timeout::Never).unwrap() + run_ready(&mut self.handles, Timeout::Never, false).unwrap() } /// Blocks for a limited time until one of the operations becomes ready. @@ -1098,7 +1108,7 @@ impl<'a> Select<'a> { /// } /// ``` pub fn ready_deadline(&mut self, deadline: Instant) -> Result { - match run_ready(&mut self.handles, Timeout::At(deadline)) { + match run_ready(&mut self.handles, Timeout::At(deadline), false) { None => Err(ReadyTimeoutError), Some(index) => Ok(index), } diff --git a/crossbeam-channel/src/select_macro.rs b/crossbeam-channel/src/select_macro.rs index 43932a668..e109404f7 100644 --- a/crossbeam-channel/src/select_macro.rs +++ b/crossbeam-channel/src/select_macro.rs @@ -750,7 +750,7 @@ macro_rules! crossbeam_channel_internal { $cases:tt ) => {{ let _oper: $crate::SelectedOperation<'_> = { - let _oper = $crate::internal::select(&mut $sel); + let _oper = $crate::internal::select(&mut $sel, _IS_BIASED); // Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe { ::std::mem::transmute(_oper) } @@ -772,7 +772,7 @@ macro_rules! crossbeam_channel_internal { $cases:tt ) => {{ let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = { - let _oper = $crate::internal::try_select(&mut $sel); + let _oper = $crate::internal::try_select(&mut $sel, _IS_BIASED); // Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe { ::std::mem::transmute(_oper) } @@ -802,7 +802,7 @@ macro_rules! crossbeam_channel_internal { $cases:tt ) => {{ let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = { - let _oper = $crate::internal::select_timeout(&mut $sel, $timeout); + let _oper = $crate::internal::select_timeout(&mut $sel, $timeout, _IS_BIASED); // Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe { ::std::mem::transmute(_oper) } @@ -985,7 +985,8 @@ macro_rules! crossbeam_channel_internal { /// /// This macro allows you to define a set of channel operations, wait until any one of them becomes /// ready, and finally execute it. If multiple operations are ready at the same time, a random one -/// among them is selected. +/// among them is selected (i.e. the unbiased selection). Use `select_biased!` for the biased +/// selection. /// /// It is also possible to define a `default` case that gets executed if none of the operations are /// ready, either right away or for a certain duration of time. @@ -1109,8 +1110,33 @@ macro_rules! crossbeam_channel_internal { #[macro_export] macro_rules! select { ($($tokens:tt)*) => { - $crate::crossbeam_channel_internal!( - $($tokens)* - ) + { + const _IS_BIASED: bool = false; + + $crate::crossbeam_channel_internal!( + $($tokens)* + ) + } + }; +} + +/// Selects from a set of channel operations. +/// +/// This macro allows you to define a list of channel operations, wait until any one of them +/// becomes ready, and finally execute it. If multiple operations are ready at the same time, the +/// operation nearest to the front of the list is always selected (i.e. the biased selection). Use +/// [`select!`] for the unbiased selection. +/// +/// Otherwise, this macro's functionality is identical to [`select!`]. Refer to it for the syntax. +#[macro_export] +macro_rules! select_biased { + ($($tokens:tt)*) => { + { + const _IS_BIASED: bool = true; + + $crate::crossbeam_channel_internal!( + $($tokens)* + ) + } }; } diff --git a/crossbeam-channel/tests/mpsc.rs b/crossbeam-channel/tests/mpsc.rs index 2060b6fbe..3e969468e 100644 --- a/crossbeam-channel/tests/mpsc.rs +++ b/crossbeam-channel/tests/mpsc.rs @@ -176,6 +176,8 @@ macro_rules! select { ( $($name:pat = $rx:ident.$meth:ident() => $code:expr),+ ) => ({ + const _IS_BIASED: bool = false; + cc::crossbeam_channel_internal! { $( $meth(($rx).inner) -> res => { diff --git a/crossbeam-channel/tests/select_macro.rs b/crossbeam-channel/tests/select_macro.rs index c48080975..794e026b8 100644 --- a/crossbeam-channel/tests/select_macro.rs +++ b/crossbeam-channel/tests/select_macro.rs @@ -9,7 +9,7 @@ use std::ops::Deref; use std::thread; use std::time::{Duration, Instant}; -use crossbeam_channel::{after, bounded, never, select, tick, unbounded}; +use crossbeam_channel::{after, bounded, never, select, select_biased, tick, unbounded}; use crossbeam_channel::{Receiver, RecvError, SendError, Sender, TryRecvError}; use crossbeam_utils::thread::scope; @@ -943,7 +943,122 @@ fn fairness_send() { assert!(hits.iter().all(|x| *x >= COUNT / 4)); } -#[allow(clippy::or_fun_call)] // This is intentional. +#[test] +fn unfairness() { + #[cfg(miri)] + const COUNT: usize = 100; + #[cfg(not(miri))] + const COUNT: usize = 10_000; + + let (s1, r1) = unbounded::<()>(); + let (s2, r2) = unbounded::<()>(); + let (s3, r3) = unbounded::<()>(); + + for _ in 0..COUNT { + s1.send(()).unwrap(); + s2.send(()).unwrap(); + } + s3.send(()).unwrap(); + + let mut hits = [0usize; 3]; + for _ in 0..COUNT { + select_biased! { + recv(r1) -> _ => hits[0] += 1, + recv(r2) -> _ => hits[1] += 1, + recv(r3) -> _ => hits[2] += 1, + } + } + assert_eq!(hits, [COUNT, 0, 0]); + + for _ in 0..COUNT { + select_biased! { + recv(r1) -> _ => hits[0] += 1, + recv(r2) -> _ => hits[1] += 1, + recv(r3) -> _ => hits[2] += 1, + } + } + assert_eq!(hits, [COUNT, COUNT, 0]); +} + +#[test] +fn unfairness_timeout() { + #[cfg(miri)] + const COUNT: usize = 100; + #[cfg(not(miri))] + const COUNT: usize = 10_000; + + let (s1, r1) = unbounded::<()>(); + let (s2, r2) = unbounded::<()>(); + let (s3, r3) = unbounded::<()>(); + + for _ in 0..COUNT { + s1.send(()).unwrap(); + s2.send(()).unwrap(); + } + s3.send(()).unwrap(); + + let mut hits = [0usize; 3]; + for _ in 0..COUNT { + select_biased! { + recv(r1) -> _ => hits[0] += 1, + recv(r2) -> _ => hits[1] += 1, + recv(r3) -> _ => hits[2] += 1, + default(ms(1000)) => unreachable!(), + } + } + assert_eq!(hits, [COUNT, 0, 0]); + + for _ in 0..COUNT { + select_biased! { + recv(r1) -> _ => hits[0] += 1, + recv(r2) -> _ => hits[1] += 1, + recv(r3) -> _ => hits[2] += 1, + default(ms(1000)) => unreachable!(), + } + } + assert_eq!(hits, [COUNT, COUNT, 0]); +} + +#[test] +fn unfairness_try() { + #[cfg(miri)] + const COUNT: usize = 100; + #[cfg(not(miri))] + const COUNT: usize = 10_000; + + let (s1, r1) = unbounded::<()>(); + let (s2, r2) = unbounded::<()>(); + let (s3, r3) = unbounded::<()>(); + + for _ in 0..COUNT { + s1.send(()).unwrap(); + s2.send(()).unwrap(); + } + s3.send(()).unwrap(); + + let mut hits = [0usize; 3]; + for _ in 0..COUNT { + select_biased! { + recv(r1) -> _ => hits[0] += 1, + recv(r2) -> _ => hits[1] += 1, + recv(r3) -> _ => hits[2] += 1, + default() => unreachable!(), + } + } + assert_eq!(hits, [COUNT, 0, 0]); + + for _ in 0..COUNT { + select_biased! { + recv(r1) -> _ => hits[0] += 1, + recv(r2) -> _ => hits[1] += 1, + recv(r3) -> _ => hits[2] += 1, + default() => unreachable!(), + } + } + assert_eq!(hits, [COUNT, COUNT, 0]); +} + +#[allow(clippy::or_fun_call, clippy::unnecessary_literal_unwrap)] // This is intentional. #[test] fn references() { let (s, r) = unbounded::(); diff --git a/crossbeam-deque/Cargo.toml b/crossbeam-deque/Cargo.toml index 187e6028f..37e5f784a 100644 --- a/crossbeam-deque/Cargo.toml +++ b/crossbeam-deque/Cargo.toml @@ -29,3 +29,6 @@ crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-fea [dev-dependencies] rand = "0.8" + +[lints] +workspace = true diff --git a/crossbeam-epoch/Cargo.toml b/crossbeam-epoch/Cargo.toml index 1cb0750d4..1900b4baf 100644 --- a/crossbeam-epoch/Cargo.toml +++ b/crossbeam-epoch/Cargo.toml @@ -54,3 +54,6 @@ loom-crate = { package = "loom", version = "0.7.1", optional = true } [dev-dependencies] rand = "0.8" + +[lints] +workspace = true diff --git a/crossbeam-epoch/src/guard.rs b/crossbeam-epoch/src/guard.rs index 5fe33807c..3d7982fe3 100644 --- a/crossbeam-epoch/src/guard.rs +++ b/crossbeam-epoch/src/guard.rs @@ -273,7 +273,7 @@ impl Guard { /// global cache. /// /// Call this method after deferring execution of a function if you want to get it executed as - /// soon as possible. Flushing will make sure it is residing in in the global cache, so that + /// soon as possible. Flushing will make sure it is residing in the global cache, so that /// any thread has a chance of taking the function and executing it. /// /// If this method is called from an [`unprotected`] guard, it is a no-op (nothing happens). diff --git a/crossbeam-queue/Cargo.toml b/crossbeam-queue/Cargo.toml index 050c2c6d5..0cec3bdae 100644 --- a/crossbeam-queue/Cargo.toml +++ b/crossbeam-queue/Cargo.toml @@ -41,3 +41,6 @@ crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-fea [dev-dependencies] rand = "0.8" + +[lints] +workspace = true diff --git a/crossbeam-queue/src/array_queue.rs b/crossbeam-queue/src/array_queue.rs index 3f6d19533..0d2569d4d 100644 --- a/crossbeam-queue/src/array_queue.rs +++ b/crossbeam-queue/src/array_queue.rs @@ -290,7 +290,7 @@ impl ArrayQueue { let slot = unsafe { self.buffer.get_unchecked(index) }; let stamp = slot.stamp.load(Ordering::Acquire); - // If the the stamp is ahead of the head by 1, we may attempt to pop. + // If the stamp is ahead of the head by 1, we may attempt to pop. if head + 1 == stamp { let new = if index + 1 < self.cap { // Same lap, incremented index. diff --git a/crossbeam-skiplist/Cargo.toml b/crossbeam-skiplist/Cargo.toml index 5f9bacd82..a5fdb3568 100644 --- a/crossbeam-skiplist/Cargo.toml +++ b/crossbeam-skiplist/Cargo.toml @@ -33,3 +33,6 @@ crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-fea [dev-dependencies] rand = "0.8" + +[lints] +workspace = true diff --git a/crossbeam-skiplist/src/base.rs b/crossbeam-skiplist/src/base.rs index efa0a1c7f..f8987303c 100644 --- a/crossbeam-skiplist/src/base.rs +++ b/crossbeam-skiplist/src/base.rs @@ -1946,7 +1946,7 @@ where None => self.range.end_bound(), }; if below_upper_bound(&bound, h.key().borrow()) { - self.head = next_head.clone(); + self.head.clone_from(&next_head); next_head } else { unsafe { @@ -1973,7 +1973,7 @@ where None => self.range.start_bound(), }; if above_lower_bound(&bound, t.key().borrow()) { - self.tail = next_tail.clone(); + self.tail.clone_from(&next_tail); next_tail } else { unsafe { diff --git a/crossbeam-utils/CHANGELOG.md b/crossbeam-utils/CHANGELOG.md index f6f346381..4da2deb0f 100644 --- a/crossbeam-utils/CHANGELOG.md +++ b/crossbeam-utils/CHANGELOG.md @@ -1,3 +1,7 @@ +# Version 0.8.20 + +- Implement `Display` for `CachePadded`. (#1097) + # Version 0.8.19 - Remove dependency on `cfg-if`. (#1072) diff --git a/crossbeam-utils/Cargo.toml b/crossbeam-utils/Cargo.toml index ea87401b1..955f8a87d 100644 --- a/crossbeam-utils/Cargo.toml +++ b/crossbeam-utils/Cargo.toml @@ -4,7 +4,7 @@ name = "crossbeam-utils" # - Update CHANGELOG.md # - Update README.md (when increasing major or minor version) # - Run './tools/publish.sh crossbeam-utils ' -version = "0.8.19" +version = "0.8.20" edition = "2021" rust-version = "1.60" license = "MIT OR Apache-2.0" @@ -41,3 +41,6 @@ loom = { version = "0.7.1", optional = true } [dev-dependencies] rand = "0.8" + +[lints] +workspace = true diff --git a/crossbeam-utils/build.rs b/crossbeam-utils/build.rs index c71c23136..ff7e81f94 100644 --- a/crossbeam-utils/build.rs +++ b/crossbeam-utils/build.rs @@ -19,6 +19,7 @@ include!("build-common.rs"); fn main() { println!("cargo:rerun-if-changed=no_atomic.rs"); + println!("cargo:rustc-check-cfg=cfg(crossbeam_no_atomic,crossbeam_sanitize_thread)"); let target = match env::var("TARGET") { Ok(target) => convert_custom_linux_target(target), diff --git a/crossbeam-utils/src/atomic/mod.rs b/crossbeam-utils/src/atomic/mod.rs index 7b39fe474..8662ded56 100644 --- a/crossbeam-utils/src/atomic/mod.rs +++ b/crossbeam-utils/src/atomic/mod.rs @@ -11,7 +11,7 @@ // In narrow architectures (pointer width <= 16), the counter is still <= 32-bit and may be // vulnerable to wrap around. But it's mostly okay, since in such a primitive hardware, the // counter will not be increased that fast. -// Note that Rust (and C99) pointers must be at least 16-bits: https://github.com/rust-lang/rust/pull/49305 +// Note that Rust (and C99) pointers must be at least 16-bit (i.e., 8-bit targets are impossible): https://github.com/rust-lang/rust/pull/49305 #[cfg_attr( any(target_pointer_width = "16", target_pointer_width = "32"), path = "seq_lock_wide.rs" diff --git a/crossbeam-utils/src/cache_padded.rs b/crossbeam-utils/src/cache_padded.rs index f44f2d7b4..44212b835 100644 --- a/crossbeam-utils/src/cache_padded.rs +++ b/crossbeam-utils/src/cache_padded.rs @@ -207,3 +207,9 @@ impl From for CachePadded { CachePadded::new(t) } } + +impl fmt::Display for CachePadded { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.value, f) + } +} diff --git a/crossbeam-utils/src/thread.rs b/crossbeam-utils/src/thread.rs index 9e706c695..847f4cf11 100644 --- a/crossbeam-utils/src/thread.rs +++ b/crossbeam-utils/src/thread.rs @@ -500,7 +500,7 @@ pub struct ScopedJoinHandle<'scope, T> { /// Holds the result of the inner closure. result: SharedOption, - /// A handle to the the spawned thread. + /// A handle to the spawned thread. thread: thread::Thread, /// Borrows the parent scope with lifetime `'scope`.