diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99449182c..46cb6cb8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,9 +30,9 @@ jobs: fail-fast: false matrix: include: - - rust: '1.38' + - rust: '1.61' os: ubuntu-latest - - rust: '1.38' + - rust: '1.61' os: windows-latest - rust: stable os: ubuntu-latest @@ -54,10 +54,10 @@ jobs: # Test 32-bit target that does not have AtomicU64/AtomicI64. - rust: nightly os: ubuntu-latest - target: mips-unknown-linux-gnu + target: armv5te-unknown-linux-gnueabi runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - 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 }} @@ -75,11 +75,11 @@ jobs: fail-fast: false matrix: rust: - - '1.38' + - '1.61' - nightly runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - name: Install cargo-hack @@ -91,7 +91,7 @@ jobs: dependencies: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Install cargo-hack @@ -108,9 +108,9 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust - run: rustup update nightly && rustup default nightly + run: rustup update stable - run: ci/no_atomic.sh - run: git add -N . && git diff --exit-code if: github.repository_owner != 'crossbeam-rs' || github.event_name != 'schedule' @@ -141,7 +141,7 @@ jobs: rustfmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - name: rustfmt @@ -153,7 +153,7 @@ jobs: # clippy: # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v3 + # - uses: actions/checkout@v4 # - name: Install Rust # run: rustup update stable # - name: clippy @@ -163,7 +163,7 @@ jobs: miri: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup toolchain install nightly --component miri && rustup default nightly - name: miri @@ -173,7 +173,7 @@ jobs: san: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Run sanitizers @@ -183,7 +183,7 @@ jobs: loom: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - name: loom @@ -193,7 +193,7 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - name: docs @@ -202,7 +202,7 @@ jobs: shellcheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install shellcheck uses: taiki-e/install-action@shellcheck - run: shellcheck $(git ls-files '*.sh') diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ad0813010..514f11b5d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: if: github.repository_owner == 'crossbeam-rs' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: taiki-e/create-gh-release-action@v1 with: prefix: crossbeam(-[a-z]+)? diff --git a/Cargo.toml b/Cargo.toml index 6b6882a8c..c93ca1ff9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ name = "crossbeam" # - Create "crossbeam-X.Y.Z" git tag version = "0.8.2" edition = "2018" -rust-version = "1.38" +rust-version = "1.61" license = "MIT OR Apache-2.0" repository = "https://github.com/crossbeam-rs/crossbeam" homepage = "https://github.com/crossbeam-rs/crossbeam" @@ -44,31 +44,31 @@ nightly = ["crossbeam-epoch/nightly", "crossbeam-utils/nightly", "crossbeam-queu cfg-if = "1" [dependencies.crossbeam-channel] -version = "0.5" +version = "0.5.9" path = "./crossbeam-channel" default-features = false optional = true [dependencies.crossbeam-deque] -version = "0.8" +version = "0.8.4" path = "./crossbeam-deque" default-features = false optional = true [dependencies.crossbeam-epoch] -version = "0.9.5" +version = "0.9.16" path = "./crossbeam-epoch" default-features = false optional = true [dependencies.crossbeam-queue] -version = "0.3.2" +version = "0.3.9" path = "./crossbeam-queue" default-features = false optional = true [dependencies.crossbeam-utils] -version = "0.8.5" +version = "0.8.17" path = "./crossbeam-utils" default-features = false diff --git a/README.md b/README.md index 1e1d68662..de4fcf631 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ https://github.com/crossbeam-rs/crossbeam#license) https://crates.io/crates/crossbeam) [![Documentation](https://docs.rs/crossbeam/badge.svg)]( https://docs.rs/crossbeam) -[![Rust 1.38+](https://img.shields.io/badge/rust-1.38+-lightgray.svg)]( +[![Rust 1.61+](https://img.shields.io/badge/rust-1.61+-lightgray.svg)]( https://www.rust-lang.org) [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) @@ -94,7 +94,7 @@ crossbeam = "0.8" Crossbeam supports stable Rust releases going back at least six months, and every time the minimum supported Rust version is increased, a new minor -version is released. Currently, the minimum supported Rust version is 1.38. +version is released. Currently, the minimum supported Rust version is 1.61. ## Contributing diff --git a/ci/no_atomic.sh b/ci/no_atomic.sh index 186b8bcce..288150b3b 100755 --- a/ci/no_atomic.sh +++ b/ci/no_atomic.sh @@ -10,30 +10,26 @@ cd "$(dirname "$0")"/.. file="no_atomic.rs" -no_atomic_cas=() -no_atomic_64=() no_atomic=() -for target in $(rustc --print target-list); do - target_spec=$(rustc --print target-spec-json -Z unstable-options --target "${target}") +for target_spec in $(RUSTC_BOOTSTRAP=1 rustc +stable -Z unstable-options --print all-target-specs-json | jq -c '. | to_entries | .[]'); do + target=$(jq <<<"${target_spec}" -r '.key') + target_spec=$(jq <<<"${target_spec}" -c '.value') res=$(jq <<<"${target_spec}" -r 'select(."atomic-cas" == false)') [[ -z "${res}" ]] || no_atomic_cas+=("${target}") max_atomic_width=$(jq <<<"${target_spec}" -r '."max-atomic-width"') min_atomic_width=$(jq <<<"${target_spec}" -r '."min-atomic-width"') case "${max_atomic_width}" in + # `"max-atomic-width" == 0` means that atomic is not supported at all. + # We do not have a cfg for targets with {8,16}-bit atomic only, so + # for now we treat them the same as targets that do not support atomic. + 0) no_atomic+=("${target}") ;; # It is not clear exactly what `"max-atomic-width" == null` means, but they # actually seem to have the same max-atomic-width as the target-pointer-width. # The targets currently included in this group are "mipsel-sony-psp", # "thumbv4t-none-eabi", "thumbv6m-none-eabi", all of which are # `"target-pointer-width" == "32"`, so assuming them `"max-atomic-width" == 32` # for now. - 32 | null) no_atomic_64+=("${target}") ;; - # `"max-atomic-width" == 0` means that atomic is not supported at all. - 0) - no_atomic_64+=("${target}") - no_atomic+=("${target}") - ;; - 64 | 128) ;; - # There is no `"max-atomic-width" == 16` or `"max-atomic-width" == 8` targets. + null | 8 | 16 | 32 | 64 | 128) ;; *) exit 1 ;; esac case "${min_atomic_width}" in @@ -46,24 +42,6 @@ cat >"${file}" <>"${file}" -done -cat >>"${file}" <>"${file}" -done -cat >>"${file}" < Buffer { impl Clone for Buffer { fn clone(&self) -> Buffer { - Buffer { - ptr: self.ptr, - cap: self.cap, - } + *self } } diff --git a/crossbeam-epoch/CHANGELOG.md b/crossbeam-epoch/CHANGELOG.md index f8e7c3c6d..c81580e51 100644 --- a/crossbeam-epoch/CHANGELOG.md +++ b/crossbeam-epoch/CHANGELOG.md @@ -1,3 +1,11 @@ +# Version 0.9.16 + +- Bump the minimum supported Rust version to 1.61. (#1037) +- Improve support for targets without atomic CAS. (#1037) +- Remove build script. (#1037) +- Remove dependency on `scopeguard`. (#1045) +- Update `loom` dependency to 0.7. + # Version 0.9.15 - Update `memoffset` to 0.9. (#981) diff --git a/crossbeam-epoch/Cargo.toml b/crossbeam-epoch/Cargo.toml index 50ec66e9e..be4628d03 100644 --- a/crossbeam-epoch/Cargo.toml +++ b/crossbeam-epoch/Cargo.toml @@ -4,9 +4,9 @@ name = "crossbeam-epoch" # - Update CHANGELOG.md # - Update README.md # - Create "crossbeam-epoch-X.Y.Z" git tag -version = "0.9.15" +version = "0.9.16" edition = "2018" -rust-version = "1.38" +rust-version = "1.61" license = "MIT OR Apache-2.0" repository = "https://github.com/crossbeam-rs/crossbeam" homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-epoch" @@ -48,20 +48,18 @@ autocfg = "1" [dependencies] cfg-if = "1" memoffset = "0.9" -scopeguard = { version = "1.1", default-features = false } # Enable the use of loom for concurrency testing. # # NOTE: This feature is outside of the normal semver guarantees and minor or # patch versions of crossbeam may make breaking changes to them at any time. [target.'cfg(crossbeam_loom)'.dependencies] -loom-crate = { package = "loom", version = "0.5", optional = true } +loom-crate = { package = "loom", version = "0.7.1", optional = true } [dependencies.crossbeam-utils] -version = "0.8.5" +version = "0.8.17" path = "../crossbeam-utils" default-features = false [dev-dependencies] rand = "0.8" -rustversion = "1" diff --git a/crossbeam-epoch/README.md b/crossbeam-epoch/README.md index 2840ea792..ba74c7c75 100644 --- a/crossbeam-epoch/README.md +++ b/crossbeam-epoch/README.md @@ -8,7 +8,7 @@ https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-epoch#license) https://crates.io/crates/crossbeam-epoch) [![Documentation](https://docs.rs/crossbeam-epoch/badge.svg)]( https://docs.rs/crossbeam-epoch) -[![Rust 1.38+](https://img.shields.io/badge/rust-1.38+-lightgray.svg)]( +[![Rust 1.61+](https://img.shields.io/badge/rust-1.61+-lightgray.svg)]( https://www.rust-lang.org) [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) @@ -35,7 +35,7 @@ crossbeam-epoch = "0.9" Crossbeam Epoch supports stable Rust releases going back at least six months, and every time the minimum supported Rust version is increased, a new minor -version is released. Currently, the minimum supported Rust version is 1.38. +version is released. Currently, the minimum supported Rust version is 1.61. ## License diff --git a/crossbeam-epoch/build-common.rs b/crossbeam-epoch/build-common.rs deleted file mode 120000 index 929510c73..000000000 --- a/crossbeam-epoch/build-common.rs +++ /dev/null @@ -1 +0,0 @@ -../build-common.rs \ No newline at end of file diff --git a/crossbeam-epoch/build.rs b/crossbeam-epoch/build.rs deleted file mode 100644 index 978141aa5..000000000 --- a/crossbeam-epoch/build.rs +++ /dev/null @@ -1,57 +0,0 @@ -// The rustc-cfg listed below are considered public API, but it is *unstable* -// and outside of the normal semver guarantees: -// -// - `crossbeam_no_atomic_cas` -// Assume the target does *not* support atomic CAS operations. -// This is usually detected automatically by the build script, but you may -// need to enable it manually when building for custom targets or using -// non-cargo build systems that don't run the build script. -// -// With the exceptions mentioned above, the rustc-cfg emitted by the build -// script are *not* public API. - -#![warn(rust_2018_idioms)] - -use std::env; - -include!("no_atomic.rs"); -include!("build-common.rs"); - -fn main() { - let target = match env::var("TARGET") { - Ok(target) => convert_custom_linux_target(target), - Err(e) => { - println!( - "cargo:warning={}: unable to get TARGET environment variable: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - let cfg = match autocfg::AutoCfg::new() { - Ok(cfg) => cfg, - Err(e) => { - println!( - "cargo:warning={}: unable to determine rustc version: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - // Note that this is `no_`*, not `has_*`. This allows treating as the latest - // stable rustc is used when the build script doesn't run. This is useful - // for non-cargo build systems that don't run the build script. - if NO_ATOMIC_CAS.contains(&&*target) { - println!("cargo:rustc-cfg=crossbeam_no_atomic_cas"); - } - - if !cfg.probe_rustc_version(1, 61) { - println!("cargo:rustc-cfg=crossbeam_no_const_fn_trait_bound"); - } - - println!("cargo:rerun-if-changed=no_atomic.rs"); -} diff --git a/crossbeam-epoch/no_atomic.rs b/crossbeam-epoch/no_atomic.rs deleted file mode 120000 index 417886bb7..000000000 --- a/crossbeam-epoch/no_atomic.rs +++ /dev/null @@ -1 +0,0 @@ -../no_atomic.rs \ No newline at end of file diff --git a/crossbeam-epoch/src/atomic.rs b/crossbeam-epoch/src/atomic.rs index 19bab4729..0dc61021a 100644 --- a/crossbeam-epoch/src/atomic.rs +++ b/crossbeam-epoch/src/atomic.rs @@ -5,12 +5,11 @@ use core::marker::PhantomData; use core::mem::{self, MaybeUninit}; use core::ops::{Deref, DerefMut}; use core::slice; -use core::sync::atomic::Ordering; use crate::alloc::alloc; use crate::alloc::boxed::Box; use crate::guard::Guard; -use crate::primitive::sync::atomic::AtomicUsize; +use crate::primitive::sync::atomic::{AtomicUsize, Ordering}; use crossbeam_utils::atomic::AtomicConsume; /// Given ordering for the success case in a compare-exchange operation, returns the strongest @@ -344,16 +343,15 @@ impl Atomic { /// /// let a = Atomic::::null(); /// ``` - #[cfg(all(not(crossbeam_no_const_fn_trait_bound), not(crossbeam_loom)))] + #[cfg(not(crossbeam_loom))] pub const fn null() -> Atomic { Self { data: AtomicUsize::new(0), _marker: PhantomData, } } - /// Returns a new null atomic pointer. - #[cfg(not(all(not(crossbeam_no_const_fn_trait_bound), not(crossbeam_loom))))] + #[cfg(crossbeam_loom)] pub fn null() -> Atomic { Self { data: AtomicUsize::new(0), @@ -879,17 +877,7 @@ impl Atomic { /// } /// ``` pub unsafe fn into_owned(self) -> Owned { - #[cfg(crossbeam_loom)] - { - // FIXME: loom does not yet support into_inner, so we use unsync_load for now, - // which should have the same synchronization properties: - // https://github.com/tokio-rs/loom/issues/117 - Owned::from_usize(self.data.unsync_load()) - } - #[cfg(not(crossbeam_loom))] - { - Owned::from_usize(self.data.into_inner()) - } + Owned::from_usize(self.data.into_inner()) } /// Takes ownership of the pointee if it is non-null. @@ -926,10 +914,6 @@ impl Atomic { /// } /// ``` pub unsafe fn try_into_owned(self) -> Option> { - // FIXME: See self.into_owned() - #[cfg(crossbeam_loom)] - let data = self.data.unsync_load(); - #[cfg(not(crossbeam_loom))] let data = self.data.into_inner(); if decompose_tag::(data).0 == 0 { None @@ -1313,10 +1297,7 @@ pub struct Shared<'g, T: 'g + ?Sized + Pointable> { impl Clone for Shared<'_, T> { fn clone(&self) -> Self { - Self { - data: self.data, - _marker: PhantomData, - } + *self } } @@ -1702,7 +1683,6 @@ mod tests { Shared::::null().with_tag(7); } - #[rustversion::since(1.61)] #[test] fn const_atomic_null() { use super::Atomic; diff --git a/crossbeam-epoch/src/epoch.rs b/crossbeam-epoch/src/epoch.rs index 663508bd7..18d7418a1 100644 --- a/crossbeam-epoch/src/epoch.rs +++ b/crossbeam-epoch/src/epoch.rs @@ -7,8 +7,7 @@ //! If an object became garbage in some epoch, then we can be sure that after two advancements no //! participant will hold a reference to it. That is the crux of safe memory reclamation. -use crate::primitive::sync::atomic::AtomicUsize; -use core::sync::atomic::Ordering; +use crate::primitive::sync::atomic::{AtomicUsize, Ordering}; /// An epoch that can be marked as pinned or unpinned. /// diff --git a/crossbeam-epoch/src/guard.rs b/crossbeam-epoch/src/guard.rs index ba7fe1b11..5fe33807c 100644 --- a/crossbeam-epoch/src/guard.rs +++ b/crossbeam-epoch/src/guard.rs @@ -1,8 +1,6 @@ use core::fmt; use core::mem; -use scopeguard::defer; - use crate::atomic::Shared; use crate::collector::Collector; use crate::deferred::Deferred; @@ -366,6 +364,17 @@ impl Guard { where F: FnOnce() -> R, { + // Ensure the Guard is re-pinned even if the function panics + struct ScopeGuard(*const Local); + impl Drop for ScopeGuard { + fn drop(&mut self) { + if let Some(local) = unsafe { self.0.as_ref() } { + mem::forget(local.pin()); + local.release_handle(); + } + } + } + if let Some(local) = unsafe { self.local.as_ref() } { // We need to acquire a handle here to ensure the Local doesn't // disappear from under us. @@ -373,13 +382,7 @@ impl Guard { local.unpin(); } - // Ensure the Guard is re-pinned even if the function panics - defer! { - if let Some(local) = unsafe { self.local.as_ref() } { - mem::forget(local.pin()); - local.release_handle(); - } - } + let _guard = ScopeGuard(self.local); f() } @@ -447,8 +450,8 @@ impl fmt::Debug for Guard { /// // Load `a` without pinning the current thread. /// a.load(Relaxed, epoch::unprotected()); /// -/// // It's possible to create more dummy guards by calling `clone()`. -/// let dummy = &epoch::unprotected().clone(); +/// // It's possible to create more dummy guards. +/// let dummy = epoch::unprotected(); /// /// dummy.defer(move || { /// println!("This gets executed immediately."); diff --git a/crossbeam-epoch/src/internal.rs b/crossbeam-epoch/src/internal.rs index 00c66a40a..93065fa6b 100644 --- a/crossbeam-epoch/src/internal.rs +++ b/crossbeam-epoch/src/internal.rs @@ -36,11 +36,10 @@ //! destroyed as soon as the data structure gets dropped. use crate::primitive::cell::UnsafeCell; -use crate::primitive::sync::atomic; +use crate::primitive::sync::atomic::{self, Ordering}; use core::cell::Cell; use core::mem::{self, ManuallyDrop}; use core::num::Wrapping; -use core::sync::atomic::Ordering; use core::{fmt, ptr}; use crossbeam_utils::CachePadded; @@ -273,9 +272,6 @@ pub(crate) struct Local { /// A node in the intrusive linked list of `Local`s. entry: Entry, - /// The local epoch. - epoch: AtomicEpoch, - /// A reference to the global data. /// /// When all guards and handles get dropped, this reference is destroyed. @@ -294,6 +290,9 @@ pub(crate) struct Local { /// /// This is just an auxiliary counter that sometimes kicks off collection. pin_count: Cell>, + + /// The local epoch. + epoch: CachePadded, } // Make sure `Local` is less than or equal to 2048 bytes. @@ -320,12 +319,12 @@ impl Local { let local = Owned::new(Local { entry: Entry::default(), - epoch: AtomicEpoch::new(Epoch::starting()), collector: UnsafeCell::new(ManuallyDrop::new(collector.clone())), bag: UnsafeCell::new(Bag::new()), guard_count: Cell::new(0), handle_count: Cell::new(1), pin_count: Cell::new(Wrapping(0)), + epoch: CachePadded::new(AtomicEpoch::new(Epoch::starting())), }) .into_shared(unprotected()); collector.global.locals.insert(local, unprotected()); diff --git a/crossbeam-epoch/src/lib.rs b/crossbeam-epoch/src/lib.rs index b432c1f40..96374edde 100644 --- a/crossbeam-epoch/src/lib.rs +++ b/crossbeam-epoch/src/lib.rs @@ -76,8 +76,7 @@ mod primitive { } pub(crate) mod sync { pub(crate) mod atomic { - use core::sync::atomic::Ordering; - pub(crate) use loom::sync::atomic::{fence, AtomicUsize}; + pub(crate) use loom::sync::atomic::{fence, AtomicPtr, AtomicUsize, Ordering}; // FIXME: loom does not support compiler_fence at the moment. // https://github.com/tokio-rs/loom/issues/117 @@ -90,11 +89,10 @@ mod primitive { } pub(crate) use loom::thread_local; } -#[cfg(not(crossbeam_no_atomic_cas))] +#[cfg(target_has_atomic = "ptr")] #[cfg(not(crossbeam_loom))] #[allow(unused_imports, dead_code)] mod primitive { - #[cfg(feature = "alloc")] pub(crate) mod cell { #[derive(Debug)] #[repr(transparent)] @@ -122,13 +120,13 @@ mod primitive { } } } - #[cfg(feature = "alloc")] pub(crate) mod sync { pub(crate) mod atomic { - pub(crate) use core::sync::atomic::compiler_fence; - pub(crate) use core::sync::atomic::fence; - pub(crate) use core::sync::atomic::AtomicUsize; + pub(crate) use core::sync::atomic::{ + compiler_fence, fence, AtomicPtr, AtomicUsize, Ordering, + }; } + #[cfg(feature = "alloc")] pub(crate) use alloc::sync::Arc; } @@ -136,7 +134,7 @@ mod primitive { pub(crate) use std::thread_local; } -#[cfg(not(crossbeam_no_atomic_cas))] +#[cfg(target_has_atomic = "ptr")] cfg_if! { if #[cfg(feature = "alloc")] { extern crate alloc; diff --git a/crossbeam-queue/CHANGELOG.md b/crossbeam-queue/CHANGELOG.md index 79aaacdcc..1023c3fa9 100644 --- a/crossbeam-queue/CHANGELOG.md +++ b/crossbeam-queue/CHANGELOG.md @@ -1,3 +1,9 @@ +# Version 0.3.9 + +- Bump the minimum supported Rust version to 1.61. (#1037) +- Improve support for targets without atomic CAS. (#1037) +- Remove build script. (#1037) + # Version 0.3.8 - Fix build script bug introduced in 0.3.7. (#932) diff --git a/crossbeam-queue/Cargo.toml b/crossbeam-queue/Cargo.toml index 27c386c51..3d74523ee 100644 --- a/crossbeam-queue/Cargo.toml +++ b/crossbeam-queue/Cargo.toml @@ -4,9 +4,9 @@ name = "crossbeam-queue" # - Update CHANGELOG.md # - Update README.md # - Create "crossbeam-queue-X.Y.Z" git tag -version = "0.3.8" +version = "0.3.9" edition = "2018" -rust-version = "1.38" +rust-version = "1.61" license = "MIT OR Apache-2.0" repository = "https://github.com/crossbeam-rs/crossbeam" homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-queue" @@ -40,7 +40,7 @@ nightly = ["crossbeam-utils/nightly"] cfg-if = "1" [dependencies.crossbeam-utils] -version = "0.8.5" +version = "0.8.17" path = "../crossbeam-utils" default-features = false diff --git a/crossbeam-queue/README.md b/crossbeam-queue/README.md index 85671ef17..6b3372ce6 100644 --- a/crossbeam-queue/README.md +++ b/crossbeam-queue/README.md @@ -8,7 +8,7 @@ https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-queue#license) https://crates.io/crates/crossbeam-queue) [![Documentation](https://docs.rs/crossbeam-queue/badge.svg)]( https://docs.rs/crossbeam-queue) -[![Rust 1.38+](https://img.shields.io/badge/rust-1.38+-lightgray.svg)]( +[![Rust 1.61+](https://img.shields.io/badge/rust-1.61+-lightgray.svg)]( https://www.rust-lang.org) [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) @@ -36,7 +36,7 @@ crossbeam-queue = "0.3" Crossbeam Queue supports stable Rust releases going back at least six months, and every time the minimum supported Rust version is increased, a new minor -version is released. Currently, the minimum supported Rust version is 1.38. +version is released. Currently, the minimum supported Rust version is 1.61. ## License diff --git a/crossbeam-queue/build-common.rs b/crossbeam-queue/build-common.rs deleted file mode 120000 index 929510c73..000000000 --- a/crossbeam-queue/build-common.rs +++ /dev/null @@ -1 +0,0 @@ -../build-common.rs \ No newline at end of file diff --git a/crossbeam-queue/build.rs b/crossbeam-queue/build.rs deleted file mode 100644 index 6975dd8c2..000000000 --- a/crossbeam-queue/build.rs +++ /dev/null @@ -1,41 +0,0 @@ -// The rustc-cfg listed below are considered public API, but it is *unstable* -// and outside of the normal semver guarantees: -// -// - `crossbeam_no_atomic_cas` -// Assume the target does *not* support atomic CAS operations. -// This is usually detected automatically by the build script, but you may -// need to enable it manually when building for custom targets or using -// non-cargo build systems that don't run the build script. -// -// With the exceptions mentioned above, the rustc-cfg emitted by the build -// script are *not* public API. - -#![warn(rust_2018_idioms)] - -use std::env; - -include!("no_atomic.rs"); -include!("build-common.rs"); - -fn main() { - let target = match env::var("TARGET") { - Ok(target) => convert_custom_linux_target(target), - Err(e) => { - println!( - "cargo:warning={}: unable to get TARGET environment variable: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - // Note that this is `no_`*, not `has_*`. This allows treating as the latest - // stable rustc is used when the build script doesn't run. This is useful - // for non-cargo build systems that don't run the build script. - if NO_ATOMIC_CAS.contains(&&*target) { - println!("cargo:rustc-cfg=crossbeam_no_atomic_cas"); - } - - println!("cargo:rerun-if-changed=no_atomic.rs"); -} diff --git a/crossbeam-queue/no_atomic.rs b/crossbeam-queue/no_atomic.rs deleted file mode 120000 index 417886bb7..000000000 --- a/crossbeam-queue/no_atomic.rs +++ /dev/null @@ -1 +0,0 @@ -../no_atomic.rs \ No newline at end of file diff --git a/crossbeam-queue/src/lib.rs b/crossbeam-queue/src/lib.rs index 846d7c2e1..36687282a 100644 --- a/crossbeam-queue/src/lib.rs +++ b/crossbeam-queue/src/lib.rs @@ -20,7 +20,7 @@ )] #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(not(crossbeam_no_atomic_cas))] +#[cfg(target_has_atomic = "ptr")] cfg_if::cfg_if! { if #[cfg(feature = "alloc")] { extern crate alloc; diff --git a/crossbeam-skiplist/Cargo.toml b/crossbeam-skiplist/Cargo.toml index 9f3f17c68..405bd2f02 100644 --- a/crossbeam-skiplist/Cargo.toml +++ b/crossbeam-skiplist/Cargo.toml @@ -6,7 +6,7 @@ name = "crossbeam-skiplist" # - Create "crossbeam-skiplist-X.Y.Z" git tag version = "0.0.0" edition = "2018" -rust-version = "1.38" +rust-version = "1.61" license = "MIT OR Apache-2.0" repository = "https://github.com/crossbeam-rs/crossbeam" homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-skiplist" @@ -31,19 +31,15 @@ alloc = ["crossbeam-epoch/alloc"] cfg-if = "1" [dependencies.crossbeam-epoch] -version = "0.9.5" +version = "0.9.16" path = "../crossbeam-epoch" default-features = false optional = true [dependencies.crossbeam-utils] -version = "0.8.5" +version = "0.8.17" path = "../crossbeam-utils" default-features = false -[dependencies.scopeguard] -version = "1.1.0" -default-features = false - [dev-dependencies] rand = "0.8" diff --git a/crossbeam-skiplist/README.md b/crossbeam-skiplist/README.md index 4c9509061..2de167859 100644 --- a/crossbeam-skiplist/README.md +++ b/crossbeam-skiplist/README.md @@ -8,7 +8,7 @@ https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-skiplist#license https://crates.io/crates/crossbeam-skiplist) [![Documentation](https://docs.rs/crossbeam-skiplist/badge.svg)]( https://docs.rs/crossbeam-skiplist) -[![Rust 1.38+](https://img.shields.io/badge/rust-1.38+-lightgray.svg)]( +[![Rust 1.61+](https://img.shields.io/badge/rust-1.61+-lightgray.svg)]( https://www.rust-lang.org) [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) @@ -31,7 +31,7 @@ crossbeam-skiplist = "0.1" Crossbeam Skiplist supports stable Rust releases going back at least six months, and every time the minimum supported Rust version is increased, a new minor -version is released. Currently, the minimum supported Rust version is 1.38. +version is released. Currently, the minimum supported Rust version is 1.61. ## License diff --git a/crossbeam-skiplist/build-common.rs b/crossbeam-skiplist/build-common.rs deleted file mode 120000 index 929510c73..000000000 --- a/crossbeam-skiplist/build-common.rs +++ /dev/null @@ -1 +0,0 @@ -../build-common.rs \ No newline at end of file diff --git a/crossbeam-skiplist/build.rs b/crossbeam-skiplist/build.rs deleted file mode 100644 index 6975dd8c2..000000000 --- a/crossbeam-skiplist/build.rs +++ /dev/null @@ -1,41 +0,0 @@ -// The rustc-cfg listed below are considered public API, but it is *unstable* -// and outside of the normal semver guarantees: -// -// - `crossbeam_no_atomic_cas` -// Assume the target does *not* support atomic CAS operations. -// This is usually detected automatically by the build script, but you may -// need to enable it manually when building for custom targets or using -// non-cargo build systems that don't run the build script. -// -// With the exceptions mentioned above, the rustc-cfg emitted by the build -// script are *not* public API. - -#![warn(rust_2018_idioms)] - -use std::env; - -include!("no_atomic.rs"); -include!("build-common.rs"); - -fn main() { - let target = match env::var("TARGET") { - Ok(target) => convert_custom_linux_target(target), - Err(e) => { - println!( - "cargo:warning={}: unable to get TARGET environment variable: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - // Note that this is `no_`*, not `has_*`. This allows treating as the latest - // stable rustc is used when the build script doesn't run. This is useful - // for non-cargo build systems that don't run the build script. - if NO_ATOMIC_CAS.contains(&&*target) { - println!("cargo:rustc-cfg=crossbeam_no_atomic_cas"); - } - - println!("cargo:rerun-if-changed=no_atomic.rs"); -} diff --git a/crossbeam-skiplist/no_atomic.rs b/crossbeam-skiplist/no_atomic.rs deleted file mode 120000 index 417886bb7..000000000 --- a/crossbeam-skiplist/no_atomic.rs +++ /dev/null @@ -1 +0,0 @@ -../no_atomic.rs \ No newline at end of file diff --git a/crossbeam-skiplist/src/base.rs b/crossbeam-skiplist/src/base.rs index 6d5514d81..b63183650 100644 --- a/crossbeam-skiplist/src/base.rs +++ b/crossbeam-skiplist/src/base.rs @@ -36,7 +36,7 @@ impl Index for Tower { fn index(&self, index: usize) -> &Atomic> { // This implementation is actually unsafe since we don't check if the // index is in-bounds. But this is fine since this is only used internally. - unsafe { self.pointers.get_unchecked(index) } + unsafe { &*(&self.pointers as *const Atomic>).add(index) } } } @@ -937,9 +937,13 @@ where // We failed. Let's search for the key and try again. { // Create a guard that destroys the new node in case search panics. - let sg = scopeguard::guard((), |_| { - Node::finalize(node.as_raw()); - }); + struct ScopeGuard(*const Node); + impl Drop for ScopeGuard { + fn drop(&mut self) { + unsafe { Node::finalize(self.0) } + } + } + let sg = ScopeGuard(node.as_raw()); search = self.search_position(&n.key, guard); mem::forget(sg); } diff --git a/crossbeam-skiplist/src/lib.rs b/crossbeam-skiplist/src/lib.rs index cb5f0462d..194823800 100644 --- a/crossbeam-skiplist/src/lib.rs +++ b/crossbeam-skiplist/src/lib.rs @@ -245,7 +245,7 @@ use cfg_if::cfg_if; -#[cfg(not(crossbeam_no_atomic_cas))] +#[cfg(target_has_atomic = "ptr")] cfg_if! { if #[cfg(feature = "alloc")] { extern crate alloc; diff --git a/crossbeam-utils/CHANGELOG.md b/crossbeam-utils/CHANGELOG.md index 4dea4cc33..154169696 100644 --- a/crossbeam-utils/CHANGELOG.md +++ b/crossbeam-utils/CHANGELOG.md @@ -1,3 +1,13 @@ +# Version 0.8.17 + +- Bump the minimum supported Rust version to 1.61. (#1037) +- Improve support for targets without atomic CAS or 64-bit atomic. (#1037) +- Always implement `{,Ref}UnwindSafe` for AtomicCell. (#1045) +- Improve compatibility with Miri, TSan, and loom. (#995, #1003) +- Improve compatibility with unstable `oom=panic`. (#1045) +- Improve implementation of `CachePadded`. (#1014, #1025) +- Update `loom` dependency to 0.7. + # Version 0.8.16 - Improve implementation of `CachePadded`. (#967) diff --git a/crossbeam-utils/Cargo.toml b/crossbeam-utils/Cargo.toml index dc4a25ba4..cb16c7e4e 100644 --- a/crossbeam-utils/Cargo.toml +++ b/crossbeam-utils/Cargo.toml @@ -4,9 +4,9 @@ name = "crossbeam-utils" # - Update CHANGELOG.md # - Update README.md # - Create "crossbeam-utils-X.Y.Z" git tag -version = "0.8.16" +version = "0.8.17" edition = "2018" -rust-version = "1.38" +rust-version = "1.61" license = "MIT OR Apache-2.0" repository = "https://github.com/crossbeam-rs/crossbeam" homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils" @@ -38,8 +38,7 @@ cfg-if = "1" # NOTE: This feature is outside of the normal semver guarantees and minor or # patch versions of crossbeam may make breaking changes to them at any time. [target.'cfg(crossbeam_loom)'.dependencies] -loom = { version = "0.5", optional = true } +loom = { version = "0.7.1", optional = true } [dev-dependencies] rand = "0.8" -rustversion = "1" diff --git a/crossbeam-utils/README.md b/crossbeam-utils/README.md index c06ea601a..7f87fcb1d 100644 --- a/crossbeam-utils/README.md +++ b/crossbeam-utils/README.md @@ -8,7 +8,7 @@ https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils#license) https://crates.io/crates/crossbeam-utils) [![Documentation](https://docs.rs/crossbeam-utils/badge.svg)]( https://docs.rs/crossbeam-utils) -[![Rust 1.38+](https://img.shields.io/badge/rust-1.38+-lightgray.svg)]( +[![Rust 1.61+](https://img.shields.io/badge/rust-1.61+-lightgray.svg)]( https://www.rust-lang.org) [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) @@ -55,7 +55,7 @@ crossbeam-utils = "0.8" Crossbeam Utils supports stable Rust releases going back at least six months, and every time the minimum supported Rust version is increased, a new minor -version is released. Currently, the minimum supported Rust version is 1.38. +version is released. Currently, the minimum supported Rust version is 1.61. ## License diff --git a/crossbeam-utils/build.rs b/crossbeam-utils/build.rs index 617162fb5..c71c23136 100644 --- a/crossbeam-utils/build.rs +++ b/crossbeam-utils/build.rs @@ -1,24 +1,12 @@ // The rustc-cfg listed below are considered public API, but it is *unstable* // and outside of the normal semver guarantees: // -// - `crossbeam_no_atomic_cas` -// Assume the target does *not* support atomic CAS operations. -// This is usually detected automatically by the build script, but you may -// need to enable it manually when building for custom targets or using -// non-cargo build systems that don't run the build script. -// // - `crossbeam_no_atomic` // Assume the target does *not* support any atomic operations. // This is usually detected automatically by the build script, but you may // need to enable it manually when building for custom targets or using // non-cargo build systems that don't run the build script. // -// - `crossbeam_no_atomic_64` -// Assume the target does *not* support AtomicU64/AtomicI64. -// This is usually detected automatically by the build script, but you may -// need to enable it manually when building for custom targets or using -// non-cargo build systems that don't run the build script. -// // With the exceptions mentioned above, the rustc-cfg emitted by the build // script are *not* public API. @@ -30,6 +18,8 @@ include!("no_atomic.rs"); include!("build-common.rs"); fn main() { + println!("cargo:rerun-if-changed=no_atomic.rs"); + let target = match env::var("TARGET") { Ok(target) => convert_custom_linux_target(target), Err(e) => { @@ -45,17 +35,13 @@ fn main() { // Note that this is `no_`*, not `has_*`. This allows treating as the latest // stable rustc is used when the build script doesn't run. This is useful // for non-cargo build systems that don't run the build script. - if NO_ATOMIC_CAS.contains(&&*target) { - println!("cargo:rustc-cfg=crossbeam_no_atomic_cas"); - } if NO_ATOMIC.contains(&&*target) { println!("cargo:rustc-cfg=crossbeam_no_atomic"); - println!("cargo:rustc-cfg=crossbeam_no_atomic_64"); - } else if NO_ATOMIC_64.contains(&&*target) { - println!("cargo:rustc-cfg=crossbeam_no_atomic_64"); - } else { - // Otherwise, assuming `"max-atomic-width" == 64` or `"max-atomic-width" == 128`. } - println!("cargo:rerun-if-changed=no_atomic.rs"); + // `cfg(sanitize = "..")` is not stabilized. + let sanitize = env::var("CARGO_CFG_SANITIZE").unwrap_or_default(); + if sanitize.contains("thread") { + println!("cargo:rustc-cfg=crossbeam_sanitize_thread"); + } } diff --git a/crossbeam-utils/src/atomic/atomic_cell.rs b/crossbeam-utils/src/atomic/atomic_cell.rs index 7941c5c87..165faec76 100644 --- a/crossbeam-utils/src/atomic/atomic_cell.rs +++ b/crossbeam-utils/src/atomic/atomic_cell.rs @@ -1,18 +1,14 @@ // Necessary for implementing atomic methods for `AtomicUnit` #![allow(clippy::unit_arg)] -use crate::primitive::sync::atomic::{self, AtomicBool}; +use crate::primitive::sync::atomic::{self, Ordering}; use core::cell::UnsafeCell; use core::cmp; use core::fmt; use core::mem::{self, ManuallyDrop, MaybeUninit}; -use core::sync::atomic::Ordering; - +use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr; -#[cfg(feature = "std")] -use std::panic::{RefUnwindSafe, UnwindSafe}; - use super::seq_lock::SeqLock; /// A thread-safe mutable memory location. @@ -49,9 +45,7 @@ pub struct AtomicCell { unsafe impl Send for AtomicCell {} unsafe impl Sync for AtomicCell {} -#[cfg(feature = "std")] impl UnwindSafe for AtomicCell {} -#[cfg(feature = "std")] impl RefUnwindSafe for AtomicCell {} impl AtomicCell { @@ -322,6 +316,36 @@ impl Drop for AtomicCell { } } +macro_rules! atomic { + // If values of type `$t` can be transmuted into values of the primitive atomic type `$atomic`, + // declares variable `$a` of type `$atomic` and executes `$atomic_op`, breaking out of the loop. + (@check, $t:ty, $atomic:ty, $a:ident, $atomic_op:expr) => { + if can_transmute::<$t, $atomic>() { + let $a: &$atomic; + break $atomic_op; + } + }; + + // If values of type `$t` can be transmuted into values of a primitive atomic type, declares + // variable `$a` of that type and executes `$atomic_op`. Otherwise, just executes + // `$fallback_op`. + ($t:ty, $a:ident, $atomic_op:expr, $fallback_op:expr) => { + loop { + atomic!(@check, $t, AtomicUnit, $a, $atomic_op); + + atomic!(@check, $t, atomic::AtomicU8, $a, $atomic_op); + atomic!(@check, $t, atomic::AtomicU16, $a, $atomic_op); + atomic!(@check, $t, atomic::AtomicU32, $a, $atomic_op); + #[cfg(target_has_atomic = "64")] + atomic!(@check, $t, atomic::AtomicU64, $a, $atomic_op); + // TODO: AtomicU128 is unstable + // atomic!(@check, $t, atomic::AtomicU128, $a, $atomic_op); + + break $fallback_op; + } + }; +} + macro_rules! impl_arithmetic { ($t:ty, fallback, $example:tt) => { impl AtomicCell<$t> { @@ -500,7 +524,7 @@ macro_rules! impl_arithmetic { } } }; - ($t:ty, $atomic:ty, $example:tt) => { + ($t:ty, $atomic:ident, $example:tt) => { impl AtomicCell<$t> { /// Increments the current value by `val` and returns the previous value. /// @@ -518,15 +542,19 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_add(&self, val: $t) -> $t { - if can_transmute::<$t, $atomic>() { - let a = unsafe { &*(self.as_ptr() as *const $atomic) }; - a.fetch_add(val, Ordering::AcqRel) - } else { - let _guard = lock(self.as_ptr() as usize).write(); - let value = unsafe { &mut *(self.as_ptr()) }; - let old = *value; - *value = value.wrapping_add(val); - old + atomic! { + $t, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) }; + a.fetch_add(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value = value.wrapping_add(val); + old + } } } @@ -546,15 +574,19 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_sub(&self, val: $t) -> $t { - if can_transmute::<$t, $atomic>() { - let a = unsafe { &*(self.as_ptr() as *const $atomic) }; - a.fetch_sub(val, Ordering::AcqRel) - } else { - let _guard = lock(self.as_ptr() as usize).write(); - let value = unsafe { &mut *(self.as_ptr()) }; - let old = *value; - *value = value.wrapping_sub(val); - old + atomic! { + $t, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) }; + a.fetch_sub(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value = value.wrapping_sub(val); + old + } } } @@ -572,15 +604,19 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_and(&self, val: $t) -> $t { - if can_transmute::<$t, $atomic>() { - let a = unsafe { &*(self.as_ptr() as *const $atomic) }; - a.fetch_and(val, Ordering::AcqRel) - } else { - let _guard = lock(self.as_ptr() as usize).write(); - let value = unsafe { &mut *(self.as_ptr()) }; - let old = *value; - *value &= val; - old + atomic! { + $t, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) }; + a.fetch_and(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value &= val; + old + } } } @@ -598,15 +634,19 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_nand(&self, val: $t) -> $t { - if can_transmute::<$t, $atomic>() { - let a = unsafe { &*(self.as_ptr() as *const $atomic) }; - a.fetch_nand(val, Ordering::AcqRel) - } else { - let _guard = lock(self.as_ptr() as usize).write(); - let value = unsafe { &mut *(self.as_ptr()) }; - let old = *value; - *value = !(old & val); - old + atomic! { + $t, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) }; + a.fetch_nand(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value = !(old & val); + old + } } } @@ -624,15 +664,19 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_or(&self, val: $t) -> $t { - if can_transmute::<$t, $atomic>() { - let a = unsafe { &*(self.as_ptr() as *const $atomic) }; - a.fetch_or(val, Ordering::AcqRel) - } else { - let _guard = lock(self.as_ptr() as usize).write(); - let value = unsafe { &mut *(self.as_ptr()) }; - let old = *value; - *value |= val; - old + atomic! { + $t, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) }; + a.fetch_or(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value |= val; + old + } } } @@ -650,15 +694,19 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_xor(&self, val: $t) -> $t { - if can_transmute::<$t, $atomic>() { - let a = unsafe { &*(self.as_ptr() as *const $atomic) }; - a.fetch_xor(val, Ordering::AcqRel) - } else { - let _guard = lock(self.as_ptr() as usize).write(); - let value = unsafe { &mut *(self.as_ptr()) }; - let old = *value; - *value ^= val; - old + atomic! { + $t, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) }; + a.fetch_xor(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value ^= val; + old + } } } @@ -677,15 +725,19 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_max(&self, val: $t) -> $t { - if can_transmute::<$t, $atomic>() { - // TODO: Atomic*::fetch_max requires Rust 1.45. - self.fetch_update(|old| Some(cmp::max(old, val))).unwrap() - } else { - let _guard = lock(self.as_ptr() as usize).write(); - let value = unsafe { &mut *(self.as_ptr()) }; - let old = *value; - *value = cmp::max(old, val); - old + atomic! { + $t, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) }; + a.fetch_max(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value = cmp::max(old, val); + old + } } } @@ -704,51 +756,50 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_min(&self, val: $t) -> $t { - if can_transmute::<$t, $atomic>() { - // TODO: Atomic*::fetch_min requires Rust 1.45. - self.fetch_update(|old| Some(cmp::min(old, val))).unwrap() - } else { - let _guard = lock(self.as_ptr() as usize).write(); - let value = unsafe { &mut *(self.as_ptr()) }; - let old = *value; - *value = cmp::min(old, val); - old + atomic! { + $t, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) }; + a.fetch_min(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value = cmp::min(old, val); + old + } } } } }; } -impl_arithmetic!(u8, atomic::AtomicU8, "let a = AtomicCell::new(7u8);"); -impl_arithmetic!(i8, atomic::AtomicI8, "let a = AtomicCell::new(7i8);"); -impl_arithmetic!(u16, atomic::AtomicU16, "let a = AtomicCell::new(7u16);"); -impl_arithmetic!(i16, atomic::AtomicI16, "let a = AtomicCell::new(7i16);"); -impl_arithmetic!(u32, atomic::AtomicU32, "let a = AtomicCell::new(7u32);"); -impl_arithmetic!(i32, atomic::AtomicI32, "let a = AtomicCell::new(7i32);"); -#[cfg(not(crossbeam_no_atomic_64))] -impl_arithmetic!(u64, atomic::AtomicU64, "let a = AtomicCell::new(7u64);"); -#[cfg(not(crossbeam_no_atomic_64))] -impl_arithmetic!(i64, atomic::AtomicI64, "let a = AtomicCell::new(7i64);"); -#[cfg(crossbeam_no_atomic_64)] +impl_arithmetic!(u8, AtomicU8, "let a = AtomicCell::new(7u8);"); +impl_arithmetic!(i8, AtomicI8, "let a = AtomicCell::new(7i8);"); +impl_arithmetic!(u16, AtomicU16, "let a = AtomicCell::new(7u16);"); +impl_arithmetic!(i16, AtomicI16, "let a = AtomicCell::new(7i16);"); + +impl_arithmetic!(u32, AtomicU32, "let a = AtomicCell::new(7u32);"); +impl_arithmetic!(i32, AtomicI32, "let a = AtomicCell::new(7i32);"); + +#[cfg(target_has_atomic = "64")] +impl_arithmetic!(u64, AtomicU64, "let a = AtomicCell::new(7u64);"); +#[cfg(target_has_atomic = "64")] +impl_arithmetic!(i64, AtomicI64, "let a = AtomicCell::new(7i64);"); +#[cfg(not(target_has_atomic = "64"))] impl_arithmetic!(u64, fallback, "let a = AtomicCell::new(7u64);"); -#[cfg(crossbeam_no_atomic_64)] +#[cfg(not(target_has_atomic = "64"))] impl_arithmetic!(i64, fallback, "let a = AtomicCell::new(7i64);"); + // TODO: AtomicU128 is unstable -// impl_arithmetic!(u128, atomic::AtomicU128, "let a = AtomicCell::new(7u128);"); -// impl_arithmetic!(i128, atomic::AtomicI128, "let a = AtomicCell::new(7i128);"); +// impl_arithmetic!(u128, AtomicU128, "let a = AtomicCell::new(7u128);"); +// impl_arithmetic!(i128, AtomicI128, "let a = AtomicCell::new(7i128);"); impl_arithmetic!(u128, fallback, "let a = AtomicCell::new(7u128);"); impl_arithmetic!(i128, fallback, "let a = AtomicCell::new(7i128);"); -impl_arithmetic!( - usize, - atomic::AtomicUsize, - "let a = AtomicCell::new(7usize);" -); -impl_arithmetic!( - isize, - atomic::AtomicIsize, - "let a = AtomicCell::new(7isize);" -); +impl_arithmetic!(usize, AtomicUsize, "let a = AtomicCell::new(7usize);"); +impl_arithmetic!(isize, AtomicIsize, "let a = AtomicCell::new(7isize);"); impl AtomicCell { /// Applies logical "and" to the current value and returns the previous value. @@ -768,8 +819,20 @@ impl AtomicCell { /// ``` #[inline] pub fn fetch_and(&self, val: bool) -> bool { - let a = unsafe { &*(self.as_ptr() as *const AtomicBool) }; - a.fetch_and(val, Ordering::AcqRel) + atomic! { + bool, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::AtomicBool) }; + a.fetch_and(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value &= val; + old + } + } } /// Applies logical "nand" to the current value and returns the previous value. @@ -792,8 +855,20 @@ impl AtomicCell { /// ``` #[inline] pub fn fetch_nand(&self, val: bool) -> bool { - let a = unsafe { &*(self.as_ptr() as *const AtomicBool) }; - a.fetch_nand(val, Ordering::AcqRel) + atomic! { + bool, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::AtomicBool) }; + a.fetch_nand(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value = !(old & val); + old + } + } } /// Applies logical "or" to the current value and returns the previous value. @@ -813,8 +888,20 @@ impl AtomicCell { /// ``` #[inline] pub fn fetch_or(&self, val: bool) -> bool { - let a = unsafe { &*(self.as_ptr() as *const AtomicBool) }; - a.fetch_or(val, Ordering::AcqRel) + atomic! { + bool, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::AtomicBool) }; + a.fetch_or(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value |= val; + old + } + } } /// Applies logical "xor" to the current value and returns the previous value. @@ -834,8 +921,20 @@ impl AtomicCell { /// ``` #[inline] pub fn fetch_xor(&self, val: bool) -> bool { - let a = unsafe { &*(self.as_ptr() as *const AtomicBool) }; - a.fetch_xor(val, Ordering::AcqRel) + atomic! { + bool, _a, + { + let a = unsafe { &*(self.as_ptr() as *const atomic::AtomicBool) }; + a.fetch_xor(val, Ordering::AcqRel) + }, + { + let _guard = lock(self.as_ptr() as usize).write(); + let value = unsafe { &mut *(self.as_ptr()) }; + let old = *value; + *value ^= val; + old + } + } } } @@ -936,48 +1035,9 @@ impl AtomicUnit { } } -macro_rules! atomic { - // If values of type `$t` can be transmuted into values of the primitive atomic type `$atomic`, - // declares variable `$a` of type `$atomic` and executes `$atomic_op`, breaking out of the loop. - (@check, $t:ty, $atomic:ty, $a:ident, $atomic_op:expr) => { - if can_transmute::<$t, $atomic>() { - let $a: &$atomic; - break $atomic_op; - } - }; - - // If values of type `$t` can be transmuted into values of a primitive atomic type, declares - // variable `$a` of that type and executes `$atomic_op`. Otherwise, just executes - // `$fallback_op`. - ($t:ty, $a:ident, $atomic_op:expr, $fallback_op:expr) => { - loop { - atomic!(@check, $t, AtomicUnit, $a, $atomic_op); - - atomic!(@check, $t, atomic::AtomicU8, $a, $atomic_op); - atomic!(@check, $t, atomic::AtomicU16, $a, $atomic_op); - atomic!(@check, $t, atomic::AtomicU32, $a, $atomic_op); - #[cfg(not(crossbeam_no_atomic_64))] - atomic!(@check, $t, atomic::AtomicU64, $a, $atomic_op); - // TODO: AtomicU128 is unstable - // atomic!(@check, $t, atomic::AtomicU128, $a, $atomic_op); - - break $fallback_op; - } - }; -} - /// Returns `true` if operations on `AtomicCell` are lock-free. const fn atomic_is_lock_free() -> bool { - // HACK(taiki-e): This is equivalent to `atomic! { T, _a, true, false }`, but can be used in const fn even in our MSRV (Rust 1.38). - let is_lock_free = can_transmute::() - | can_transmute::() - | can_transmute::() - | can_transmute::(); - #[cfg(not(crossbeam_no_atomic_64))] - let is_lock_free = is_lock_free | can_transmute::(); - // TODO: AtomicU128 is unstable - // let is_lock_free = is_lock_free | can_transmute::(); - is_lock_free + atomic! { T, _a, true, false } } /// Atomically reads data from `src`. diff --git a/crossbeam-utils/src/atomic/consume.rs b/crossbeam-utils/src/atomic/consume.rs index 277b370a5..ff8e316b2 100644 --- a/crossbeam-utils/src/atomic/consume.rs +++ b/crossbeam-utils/src/atomic/consume.rs @@ -1,5 +1,3 @@ -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] -use crate::primitive::sync::atomic::compiler_fence; #[cfg(not(crossbeam_no_atomic))] use core::sync::atomic::Ordering; @@ -27,11 +25,21 @@ pub trait AtomicConsume { } #[cfg(not(crossbeam_no_atomic))] -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +// Miri and Loom don't support "consume" ordering and ThreadSanitizer doesn't treat +// load(Relaxed) + compiler_fence(Acquire) as "consume" load. +// LLVM generates machine code equivalent to fence(Acquire) in compiler_fence(Acquire) +// on PowerPC, MIPS, etc. (https://godbolt.org/z/hffvjvW7h), so for now the fence +// can be actually avoided here only on ARM and AArch64. See also +// https://github.com/rust-lang/rust/issues/62256. +#[cfg(all( + any(target_arch = "arm", target_arch = "aarch64"), + not(any(miri, crossbeam_loom, crossbeam_sanitize_thread)), +))] macro_rules! impl_consume { () => { #[inline] fn load_consume(&self) -> Self::Val { + use crate::primitive::sync::atomic::compiler_fence; let result = self.load(Ordering::Relaxed); compiler_fence(Ordering::Acquire); result @@ -40,7 +48,10 @@ macro_rules! impl_consume { } #[cfg(not(crossbeam_no_atomic))] -#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] +#[cfg(not(all( + any(target_arch = "arm", target_arch = "aarch64"), + not(any(miri, crossbeam_loom, crossbeam_sanitize_thread)), +)))] macro_rules! impl_consume { () => { #[inline] @@ -72,11 +83,19 @@ impl_atomic!(AtomicU8, u8); impl_atomic!(AtomicI8, i8); impl_atomic!(AtomicU16, u16); impl_atomic!(AtomicI16, i16); +#[cfg(any(target_has_atomic = "32", not(target_pointer_width = "16")))] impl_atomic!(AtomicU32, u32); +#[cfg(any(target_has_atomic = "32", not(target_pointer_width = "16")))] impl_atomic!(AtomicI32, i32); -#[cfg(not(crossbeam_no_atomic_64))] +#[cfg(any( + target_has_atomic = "64", + not(any(target_pointer_width = "16", target_pointer_width = "32")), +))] impl_atomic!(AtomicU64, u64); -#[cfg(not(crossbeam_no_atomic_64))] +#[cfg(any( + target_has_atomic = "64", + not(any(target_pointer_width = "16", target_pointer_width = "32")), +))] impl_atomic!(AtomicI64, i64); #[cfg(not(crossbeam_no_atomic))] diff --git a/crossbeam-utils/src/atomic/mod.rs b/crossbeam-utils/src/atomic/mod.rs index 38967859f..4332cc3bd 100644 --- a/crossbeam-utils/src/atomic/mod.rs +++ b/crossbeam-utils/src/atomic/mod.rs @@ -3,7 +3,7 @@ //! * [`AtomicCell`], a thread-safe mutable memory location. //! * [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering. -#[cfg(not(crossbeam_no_atomic_cas))] +#[cfg(target_has_atomic = "ptr")] #[cfg(not(crossbeam_loom))] cfg_if::cfg_if! { // Use "wide" sequence lock if the pointer width <= 32 for preventing its counter against wrap @@ -23,7 +23,7 @@ cfg_if::cfg_if! { } } -#[cfg(not(crossbeam_no_atomic_cas))] +#[cfg(target_has_atomic = "ptr")] // We cannot provide AtomicCell under cfg(crossbeam_loom) because loom's atomic // types have a different in-memory representation than the underlying type. // TODO: The latest loom supports fences, so fallback using seqlock may be available. @@ -31,7 +31,7 @@ cfg_if::cfg_if! { mod atomic_cell; mod consume; -#[cfg(not(crossbeam_no_atomic_cas))] +#[cfg(target_has_atomic = "ptr")] #[cfg(not(crossbeam_loom))] pub use self::atomic_cell::AtomicCell; pub use self::consume::AtomicConsume; diff --git a/crossbeam-utils/src/backoff.rs b/crossbeam-utils/src/backoff.rs index 9e256aaf2..7a505ed61 100644 --- a/crossbeam-utils/src/backoff.rs +++ b/crossbeam-utils/src/backoff.rs @@ -1,4 +1,4 @@ -use crate::primitive::sync::atomic; +use crate::primitive::hint; use core::cell::Cell; use core::fmt; @@ -145,10 +145,7 @@ impl Backoff { #[inline] pub fn spin(&self) { for _ in 0..1 << self.step.get().min(SPIN_LIMIT) { - // TODO(taiki-e): once we bump the minimum required Rust version to 1.49+, - // use [`core::hint::spin_loop`] instead. - #[allow(deprecated)] - atomic::spin_loop_hint(); + hint::spin_loop(); } if self.step.get() <= SPIN_LIMIT { @@ -209,18 +206,12 @@ impl Backoff { pub fn snooze(&self) { if self.step.get() <= SPIN_LIMIT { for _ in 0..1 << self.step.get() { - // TODO(taiki-e): once we bump the minimum required Rust version to 1.49+, - // use [`core::hint::spin_loop`] instead. - #[allow(deprecated)] - atomic::spin_loop_hint(); + hint::spin_loop(); } } else { #[cfg(not(feature = "std"))] for _ in 0..1 << self.step.get() { - // TODO(taiki-e): once we bump the minimum required Rust version to 1.49+, - // use [`core::hint::spin_loop`] instead. - #[allow(deprecated)] - atomic::spin_loop_hint(); + hint::spin_loop(); } #[cfg(feature = "std")] diff --git a/crossbeam-utils/src/cache_padded.rs b/crossbeam-utils/src/cache_padded.rs index 7fce7cc1b..f44f2d7b4 100644 --- a/crossbeam-utils/src/cache_padded.rs +++ b/crossbeam-utils/src/cache_padded.rs @@ -14,7 +14,7 @@ use core::ops::{Deref, DerefMut}; /// Cache lines are assumed to be N bytes long, depending on the architecture: /// /// * On x86-64, aarch64, and powerpc64, N = 128. -/// * On arm, mips, mips64, riscv32, riscv64, sparc, and hexagon, N = 32. +/// * On arm, mips, mips64, sparc, and hexagon, N = 32. /// * On m68k, N = 16. /// * On s390x, N = 256. /// * On all others, N = 64. @@ -76,6 +76,7 @@ use core::ops::{Deref, DerefMut}; // // Sources: // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9 +// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/powerpc/include/asm/cache.h#L26 #[cfg_attr( any( target_arch = "x86_64", @@ -84,25 +85,22 @@ use core::ops::{Deref, DerefMut}; ), repr(align(128)) )] -// arm, mips, mips64, riscv64, sparc, and hexagon have 32-byte cache line size. +// arm, mips, mips64, sparc, and hexagon have 32-byte cache line size. // // Sources: // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7 // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7 // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7 // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9 -// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_riscv64.go#L7 // - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/sparc/include/asm/cache.h#L17 // - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/hexagon/include/asm/cache.h#L12 -// -// riscv32 is assumed not to exceed the cache line size of riscv64. #[cfg_attr( any( target_arch = "arm", target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", - target_arch = "riscv32", - target_arch = "riscv64", + target_arch = "mips64r6", target_arch = "sparc", target_arch = "hexagon", ), @@ -119,11 +117,12 @@ use core::ops::{Deref, DerefMut}; // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7 // - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/s390/include/asm/cache.h#L13 #[cfg_attr(target_arch = "s390x", repr(align(256)))] -// x86, wasm, and sparc64 have 64-byte cache line size. +// x86, wasm, riscv, and sparc64 have 64-byte cache line size. // // Sources: // - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9 // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7 +// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/riscv/include/asm/cache.h#L10 // - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/sparc/include/asm/cache.h#L19 // // All others are assumed to have 64-byte cache line size. @@ -134,9 +133,9 @@ use core::ops::{Deref, DerefMut}; target_arch = "powerpc64", target_arch = "arm", target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", - target_arch = "riscv32", - target_arch = "riscv64", + target_arch = "mips64r6", target_arch = "sparc", target_arch = "hexagon", target_arch = "m68k", diff --git a/crossbeam-utils/src/lib.rs b/crossbeam-utils/src/lib.rs index 191c5a17d..6ab748f34 100644 --- a/crossbeam-utils/src/lib.rs +++ b/crossbeam-utils/src/lib.rs @@ -42,12 +42,14 @@ #[cfg(crossbeam_loom)] #[allow(unused_imports)] mod primitive { + pub(crate) mod hint { + pub(crate) use loom::hint::spin_loop; + } pub(crate) mod sync { pub(crate) mod atomic { - pub(crate) use loom::sync::atomic::spin_loop_hint; pub(crate) use loom::sync::atomic::{ AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, - AtomicU32, AtomicU64, AtomicU8, AtomicUsize, + AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering, }; // FIXME: loom does not support compiler_fence at the moment. @@ -63,19 +65,24 @@ mod primitive { #[cfg(not(crossbeam_loom))] #[allow(unused_imports)] mod primitive { + pub(crate) mod hint { + pub(crate) use core::hint::spin_loop; + } pub(crate) mod sync { pub(crate) mod atomic { - pub(crate) use core::sync::atomic::compiler_fence; - // TODO(taiki-e): once we bump the minimum required Rust version to 1.49+, - // use [`core::hint::spin_loop`] instead. - #[allow(deprecated)] - pub(crate) use core::sync::atomic::spin_loop_hint; + pub(crate) use core::sync::atomic::{compiler_fence, Ordering}; #[cfg(not(crossbeam_no_atomic))] pub(crate) use core::sync::atomic::{ - AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, - AtomicU8, AtomicUsize, + AtomicBool, AtomicI16, AtomicI8, AtomicIsize, AtomicU16, AtomicU8, AtomicUsize, }; - #[cfg(not(crossbeam_no_atomic_64))] + #[cfg(not(crossbeam_no_atomic))] + #[cfg(any(target_has_atomic = "32", not(target_pointer_width = "16")))] + pub(crate) use core::sync::atomic::{AtomicI32, AtomicU32}; + #[cfg(not(crossbeam_no_atomic))] + #[cfg(any( + target_has_atomic = "64", + not(any(target_pointer_width = "16", target_pointer_width = "32")), + ))] pub(crate) use core::sync::atomic::{AtomicI64, AtomicU64}; } diff --git a/crossbeam-utils/src/sync/once_lock.rs b/crossbeam-utils/src/sync/once_lock.rs index c1fefc96c..761851b01 100644 --- a/crossbeam-utils/src/sync/once_lock.rs +++ b/crossbeam-utils/src/sync/once_lock.rs @@ -4,13 +4,10 @@ use core::cell::UnsafeCell; use core::mem::MaybeUninit; -use core::sync::atomic::{AtomicBool, Ordering}; use std::sync::Once; pub(crate) struct OnceLock { once: Once, - // Once::is_completed requires Rust 1.43, so use this to track of whether they have been initialized. - is_initialized: AtomicBool, value: UnsafeCell>, // Unlike std::sync::OnceLock, we don't need PhantomData here because // we don't use #[may_dangle]. @@ -25,7 +22,6 @@ impl OnceLock { pub(crate) const fn new() -> Self { Self { once: Once::new(), - is_initialized: AtomicBool::new(false), value: UnsafeCell::new(MaybeUninit::uninit()), } } @@ -50,37 +46,28 @@ impl OnceLock { F: FnOnce() -> T, { // Fast path check - if self.is_initialized() { + if self.once.is_completed() { // SAFETY: The inner value has been initialized return unsafe { self.get_unchecked() }; } self.initialize(f); - debug_assert!(self.is_initialized()); - // SAFETY: The inner value has been initialized unsafe { self.get_unchecked() } } - #[inline] - fn is_initialized(&self) -> bool { - self.is_initialized.load(Ordering::Acquire) - } - #[cold] fn initialize(&self, f: F) where F: FnOnce() -> T, { let slot = self.value.get().cast::(); - let is_initialized = &self.is_initialized; self.once.call_once(|| { let value = f(); unsafe { slot.write(value); } - is_initialized.store(true, Ordering::Release); }); } @@ -88,14 +75,14 @@ impl OnceLock { /// /// The value must be initialized unsafe fn get_unchecked(&self) -> &T { - debug_assert!(self.is_initialized()); + debug_assert!(self.once.is_completed()); &*self.value.get().cast::() } } impl Drop for OnceLock { fn drop(&mut self) { - if self.is_initialized() { + if self.once.is_completed() { // SAFETY: The inner value has been initialized unsafe { self.value.get().cast::().drop_in_place() }; } diff --git a/crossbeam-utils/src/sync/parker.rs b/crossbeam-utils/src/sync/parker.rs index 9cb3a2601..971981d2b 100644 --- a/crossbeam-utils/src/sync/parker.rs +++ b/crossbeam-utils/src/sync/parker.rs @@ -1,6 +1,5 @@ -use crate::primitive::sync::atomic::AtomicUsize; +use crate::primitive::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use crate::primitive::sync::{Arc, Condvar, Mutex}; -use core::sync::atomic::Ordering::SeqCst; use std::fmt; use std::marker::PhantomData; use std::time::{Duration, Instant}; diff --git a/crossbeam-utils/src/thread.rs b/crossbeam-utils/src/thread.rs index 74464544a..f71fdea21 100644 --- a/crossbeam-utils/src/thread.rs +++ b/crossbeam-utils/src/thread.rs @@ -152,6 +152,15 @@ pub fn scope<'env, F, R>(f: F) -> thread::Result where F: FnOnce(&Scope<'env>) -> R, { + struct AbortOnPanic; + impl Drop for AbortOnPanic { + fn drop(&mut self) { + if thread::panicking() { + std::process::abort(); + } + } + } + let wg = WaitGroup::new(); let scope = Scope::<'env> { handles: SharedVec::default(), @@ -162,6 +171,10 @@ where // Execute the scoped function, but catch any panics. let result = panic::catch_unwind(panic::AssertUnwindSafe(|| f(&scope))); + // If an unwinding panic occurs before all threads are joined + // promote it to an aborting panic to prevent any threads from escaping the scope. + let guard = AbortOnPanic; + // Wait until all nested scopes are dropped. drop(scope.wait_group); wg.wait(); @@ -177,6 +190,8 @@ where .filter_map(|handle| handle.join().err()) .collect(); + mem::forget(guard); + // If `f` has panicked, resume unwinding. // If any of the child threads have panicked, return the panic errors. // Otherwise, everything is OK and return the result of `f`. diff --git a/crossbeam-utils/tests/atomic_cell.rs b/crossbeam-utils/tests/atomic_cell.rs index edb7a4bc0..7a2689d99 100644 --- a/crossbeam-utils/tests/atomic_cell.rs +++ b/crossbeam-utils/tests/atomic_cell.rs @@ -35,13 +35,13 @@ fn is_lock_free() { // of `AtomicU64` is `8`, so `AtomicCell` is not lock-free. assert_eq!( AtomicCell::::is_lock_free(), - cfg!(not(crossbeam_no_atomic_64)) && std::mem::align_of::() == 8 + cfg!(target_has_atomic = "64") && std::mem::align_of::() == 8 ); assert_eq!(mem::size_of::(), 8); assert_eq!(mem::align_of::(), 8); assert_eq!( AtomicCell::::is_lock_free(), - cfg!(not(crossbeam_no_atomic_64)) + cfg!(target_has_atomic = "64") ); // AtomicU128 is unstable @@ -307,7 +307,6 @@ test_arithmetic!(arithmetic_i128, i128); // https://github.com/crossbeam-rs/crossbeam/issues/748 #[cfg_attr(miri, ignore)] // TODO -#[rustversion::since(1.37)] // #[repr(align(N))] requires Rust 1.37 #[test] fn issue_748() { #[allow(dead_code)] @@ -321,14 +320,13 @@ fn issue_748() { assert_eq!(mem::size_of::(), 8); assert_eq!( AtomicCell::::is_lock_free(), - cfg!(not(crossbeam_no_atomic_64)) + cfg!(target_has_atomic = "64") ); let x = AtomicCell::new(Test::FieldLess); assert_eq!(x.load(), Test::FieldLess); } // https://github.com/crossbeam-rs/crossbeam/issues/833 -#[rustversion::since(1.40)] // const_constructor requires Rust 1.40 #[test] fn issue_833() { use std::num::NonZeroU128; diff --git a/crossbeam-utils/tests/wait_group.rs b/crossbeam-utils/tests/wait_group.rs index 0ec4a729c..5b549b849 100644 --- a/crossbeam-utils/tests/wait_group.rs +++ b/crossbeam-utils/tests/wait_group.rs @@ -36,25 +36,27 @@ fn wait() { } #[test] -#[cfg_attr(miri, ignore)] // this test makes timing assumptions, but Miri is so slow it violates them fn wait_and_drop() { let wg = WaitGroup::new(); + let wg2 = WaitGroup::new(); let (tx, rx) = mpsc::channel(); for _ in 0..THREADS { let wg = wg.clone(); + let wg2 = wg2.clone(); let tx = tx.clone(); thread::spawn(move || { - thread::sleep(Duration::from_millis(100)); + wg2.wait(); tx.send(()).unwrap(); drop(wg); }); } - // At this point, all spawned threads should be in `thread::sleep`, so we shouldn't get anything - // from the channel. + // At this point, no thread has gotten past `wg2.wait()`, so we shouldn't get anything from the + // channel. assert!(rx.try_recv().is_err()); + drop(wg2); wg.wait(); diff --git a/no_atomic.rs b/no_atomic.rs index 3e19722fc..b97f39706 100644 --- a/no_atomic.rs +++ b/no_atomic.rs @@ -1,75 +1,7 @@ // This file is @generated by no_atomic.sh. // It is not intended for manual editing. -const NO_ATOMIC_CAS: &[&str] = &[ - "armv4t-none-eabi", - "armv5te-none-eabi", - "avr-unknown-gnu-atmega328", - "bpfeb-unknown-none", - "bpfel-unknown-none", - "msp430-none-elf", - "riscv32i-unknown-none-elf", - "riscv32im-unknown-none-elf", - "riscv32imc-unknown-none-elf", - "thumbv4t-none-eabi", - "thumbv5te-none-eabi", - "thumbv6m-none-eabi", -]; - -#[allow(dead_code)] // Only crossbeam-utils uses this. -const NO_ATOMIC_64: &[&str] = &[ - "arm-linux-androideabi", - "armv4t-none-eabi", - "armv4t-unknown-linux-gnueabi", - "armv5te-none-eabi", - "armv5te-unknown-linux-gnueabi", - "armv5te-unknown-linux-musleabi", - "armv5te-unknown-linux-uclibceabi", - "armv6k-nintendo-3ds", - "avr-unknown-gnu-atmega328", - "hexagon-unknown-linux-musl", - "m68k-unknown-linux-gnu", - "mips-unknown-linux-gnu", - "mips-unknown-linux-musl", - "mips-unknown-linux-uclibc", - "mipsel-sony-psp", - "mipsel-sony-psx", - "mipsel-unknown-linux-gnu", - "mipsel-unknown-linux-musl", - "mipsel-unknown-linux-uclibc", - "mipsel-unknown-none", - "mipsisa32r6-unknown-linux-gnu", - "mipsisa32r6el-unknown-linux-gnu", - "msp430-none-elf", - "powerpc-unknown-freebsd", - "powerpc-unknown-linux-gnu", - "powerpc-unknown-linux-gnuspe", - "powerpc-unknown-linux-musl", - "powerpc-unknown-netbsd", - "powerpc-unknown-openbsd", - "powerpc-wrs-vxworks", - "powerpc-wrs-vxworks-spe", - "riscv32gc-unknown-linux-gnu", - "riscv32gc-unknown-linux-musl", - "riscv32i-unknown-none-elf", - "riscv32im-unknown-none-elf", - "riscv32imac-unknown-none-elf", - "riscv32imac-unknown-xous-elf", - "riscv32imc-unknown-none-elf", - "thumbv4t-none-eabi", - "thumbv5te-none-eabi", - "thumbv6m-none-eabi", - "thumbv7em-none-eabi", - "thumbv7em-none-eabihf", - "thumbv7m-none-eabi", - "thumbv8m.base-none-eabi", - "thumbv8m.main-none-eabi", - "thumbv8m.main-none-eabihf", -]; - -#[allow(dead_code)] // Only crossbeam-utils uses this. const NO_ATOMIC: &[&str] = &[ - "avr-unknown-gnu-atmega328", "bpfeb-unknown-none", "bpfel-unknown-none", "mipsel-sony-psx",