diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 06:56:47 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 06:56:47 +0000 |
commit | 01b3d50ad1ff4a4cf6f27547761691fd60f3247f (patch) | |
tree | 9920d6c25aa95c5ccff5cf61965f1a8d34973523 | |
parent | f85f5d224d731630716bfd9a48489056a68919dc (diff) | |
parent | cff857dd15d4e02379b3dba00804c84bf8c4049f (diff) | |
download | crossbeam-utils-01b3d50ad1ff4a4cf6f27547761691fd60f3247f.tar.gz |
Snap for 8564071 from cff857dd15d4e02379b3dba00804c84bf8c4049f to mainline-documentsui-releaseaml_doc_331120000
Change-Id: Ie0c7c550a31bfa1902addcfa1766cc88009f9057
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | Android.bp | 134 | ||||
-rw-r--r-- | CHANGELOG.md | 20 | ||||
-rw-r--r-- | Cargo.toml | 38 | ||||
-rw-r--r-- | Cargo.toml.orig | 22 | ||||
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | TEST_MAPPING | 64 | ||||
-rw-r--r-- | build.rs | 62 | ||||
-rw-r--r-- | cargo2android.json | 9 | ||||
-rw-r--r-- | no_atomic.rs | 71 | ||||
-rw-r--r-- | patches/disable_panic_tests_on_android.patch | 8 | ||||
-rw-r--r-- | src/atomic/atomic_cell.rs | 566 | ||||
-rw-r--r-- | src/atomic/consume.rs | 24 | ||||
-rw-r--r-- | src/atomic/mod.rs | 8 | ||||
-rw-r--r-- | src/lib.rs | 27 | ||||
-rw-r--r-- | src/sync/parker.rs | 2 | ||||
-rw-r--r-- | src/thread.rs | 2 | ||||
-rw-r--r-- | tests/atomic_cell.rs | 120 | ||||
-rw-r--r-- | tests/cache_padded.rs | 1 | ||||
-rw-r--r-- | tests/sharded_lock.rs | 5 |
21 files changed, 837 insertions, 363 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 1d9c34d..0d40a83 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "d841a2028dc72b4e09739116f07e865db60f3690" - } -} + "sha1": "2988f873f87d2263a7fd2b9465fb9c28f43a6490" + }, + "path_in_vcs": "crossbeam-utils" +}
\ No newline at end of file @@ -1,4 +1,4 @@ -// This file is generated by cargo2android.py --run --device --dependencies --tests. +// This file is generated by cargo2android.py --config cargo2android.json. // Do not modify this file as changes will be overridden on upgrade. package { @@ -39,47 +39,37 @@ license { ], } -rust_defaults { - name: "crossbeam-utils_defaults", +rust_test { + name: "crossbeam-utils_test_src_lib", + host_supported: true, crate_name: "crossbeam_utils", + cargo_env_compat: true, + cargo_pkg_version: "0.8.7", srcs: ["src/lib.rs"], test_suites: ["general-tests"], auto_gen_config: true, + test_options: { + unit_test: true, + }, edition: "2018", features: [ "default", "lazy_static", "std", ], - cfgs: [ - "has_atomic_u16", - "has_atomic_u32", - "has_atomic_u64", - "has_atomic_u8", - ], rustlibs: [ "libcfg_if", "liblazy_static", "librand", ], -} - -rust_test_host { - name: "crossbeam-utils_host_test_src_lib", - defaults: ["crossbeam-utils_defaults"], - test_options: { - unit_test: true, - }, -} - -rust_test { - name: "crossbeam-utils_device_test_src_lib", - defaults: ["crossbeam-utils_defaults"], + proc_macros: ["librustversion"], } rust_defaults { - name: "crossbeam-utils_defaults_crossbeam_utils", + name: "crossbeam-utils_test_defaults", crate_name: "crossbeam_utils", + cargo_env_compat: true, + cargo_pkg_version: "0.8.7", test_suites: ["general-tests"], auto_gen_config: true, edition: "2018", @@ -88,23 +78,19 @@ rust_defaults { "lazy_static", "std", ], - cfgs: [ - "has_atomic_u16", - "has_atomic_u32", - "has_atomic_u64", - "has_atomic_u8", - ], rustlibs: [ "libcfg_if", "libcrossbeam_utils", "liblazy_static", "librand", ], + proc_macros: ["librustversion"], } -rust_test_host { - name: "crossbeam-utils_host_test_tests_atomic_cell", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], +rust_test { + name: "crossbeam-utils_test_tests_atomic_cell", + defaults: ["crossbeam-utils_test_defaults"], + host_supported: true, srcs: ["tests/atomic_cell.rs"], test_options: { unit_test: true, @@ -112,14 +98,9 @@ rust_test_host { } rust_test { - name: "crossbeam-utils_device_test_tests_atomic_cell", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], - srcs: ["tests/atomic_cell.rs"], -} - -rust_test_host { - name: "crossbeam-utils_host_test_tests_cache_padded", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], + name: "crossbeam-utils_test_tests_cache_padded", + defaults: ["crossbeam-utils_test_defaults"], + host_supported: true, srcs: ["tests/cache_padded.rs"], test_options: { unit_test: true, @@ -127,14 +108,9 @@ rust_test_host { } rust_test { - name: "crossbeam-utils_device_test_tests_cache_padded", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], - srcs: ["tests/cache_padded.rs"], -} - -rust_test_host { - name: "crossbeam-utils_host_test_tests_parker", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], + name: "crossbeam-utils_test_tests_parker", + defaults: ["crossbeam-utils_test_defaults"], + host_supported: true, srcs: ["tests/parker.rs"], test_options: { unit_test: true, @@ -142,14 +118,9 @@ rust_test_host { } rust_test { - name: "crossbeam-utils_device_test_tests_parker", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], - srcs: ["tests/parker.rs"], -} - -rust_test_host { - name: "crossbeam-utils_host_test_tests_sharded_lock", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], + name: "crossbeam-utils_test_tests_sharded_lock", + defaults: ["crossbeam-utils_test_defaults"], + host_supported: true, srcs: ["tests/sharded_lock.rs"], test_options: { unit_test: true, @@ -157,14 +128,9 @@ rust_test_host { } rust_test { - name: "crossbeam-utils_device_test_tests_sharded_lock", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], - srcs: ["tests/sharded_lock.rs"], -} - -rust_test_host { - name: "crossbeam-utils_host_test_tests_thread", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], + name: "crossbeam-utils_test_tests_thread", + defaults: ["crossbeam-utils_test_defaults"], + host_supported: true, srcs: ["tests/thread.rs"], test_options: { unit_test: true, @@ -172,30 +138,21 @@ rust_test_host { } rust_test { - name: "crossbeam-utils_device_test_tests_thread", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], - srcs: ["tests/thread.rs"], -} - -rust_test_host { - name: "crossbeam-utils_host_test_tests_wait_group", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], + name: "crossbeam-utils_test_tests_wait_group", + defaults: ["crossbeam-utils_test_defaults"], + host_supported: true, srcs: ["tests/wait_group.rs"], test_options: { unit_test: true, }, } -rust_test { - name: "crossbeam-utils_device_test_tests_wait_group", - defaults: ["crossbeam-utils_defaults_crossbeam_utils"], - srcs: ["tests/wait_group.rs"], -} - rust_library { name: "libcrossbeam_utils", host_supported: true, crate_name: "crossbeam_utils", + cargo_env_compat: true, + cargo_pkg_version: "0.8.7", srcs: ["src/lib.rs"], edition: "2018", features: [ @@ -203,25 +160,12 @@ rust_library { "lazy_static", "std", ], - cfgs: [ - "has_atomic_u16", - "has_atomic_u32", - "has_atomic_u64", - "has_atomic_u8", - ], rustlibs: [ "libcfg_if", "liblazy_static", ], + apex_available: [ + "//apex_available:platform", + "com.android.virt", + ], } - -// dependent_library ["feature_list"] -// autocfg-1.0.1 -// cfg-if-1.0.0 -// getrandom-0.2.2 "std" -// lazy_static-1.4.0 -// libc-0.2.93 -// ppv-lite86-0.2.10 "simd,std" -// rand-0.8.3 "alloc,default,getrandom,libc,rand_chacha,rand_hc,std,std_rng" -// rand_chacha-0.3.0 "std" -// rand_core-0.6.2 "alloc,getrandom,std" diff --git a/CHANGELOG.md b/CHANGELOG.md index c4a92bf..98088c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +# Version 0.8.7 + +- Add `AtomicCell<{i*,u*}>::{fetch_max,fetch_min}`. (#785) +- Add `AtomicCell<{i*,u*,bool}>::fetch_nand`. (#785) +- Fix unsoundness of `AtomicCell<{i,u}64>` arithmetics on 32-bit targets that support `Atomic{I,U}64` (#781) + +# Version 0.8.6 + +- Re-add `AtomicCell<{i,u}64>::{fetch_add,fetch_sub,fetch_and,fetch_or,fetch_xor}` that were accidentally removed in 0.8.0 on targets that do not support `Atomic{I,U}64`. (#767) +- Re-add `AtomicCell<{i,u}128>::{fetch_add,fetch_sub,fetch_and,fetch_or,fetch_xor}` that were accidentally removed in 0.8.0. (#767) + +# Version 0.8.5 + +- Add `AtomicCell::fetch_update`. (#704) +- Support targets that do not have atomic CAS on stable Rust. (#698) + +# Version 0.8.4 + +- Bump `loom` dependency to version 0.5. (#686) + # Version 0.8.3 - Make `loom` dependency optional. (#666) @@ -3,40 +3,52 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] edition = "2018" +rust-version = "1.36" name = "crossbeam-utils" -version = "0.8.3" -authors = ["The Crossbeam Project Developers"] +version = "0.8.7" description = "Utilities for concurrent programming" homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils" -documentation = "https://docs.rs/crossbeam-utils" -keywords = ["scoped", "thread", "atomic", "cache"] -categories = ["algorithms", "concurrency", "data-structures", "no-std"] +keywords = [ + "scoped", + "thread", + "atomic", + "cache", +] +categories = [ + "algorithms", + "concurrency", + "data-structures", + "no-std", +] license = "MIT OR Apache-2.0" repository = "https://github.com/crossbeam-rs/crossbeam" + [dependencies.cfg-if] version = "1" [dependencies.lazy_static] version = "1.4.0" optional = true + [dev-dependencies.rand] version = "0.8" -[build-dependencies.autocfg] -version = "1.0.0" + +[dev-dependencies.rustversion] +version = "1" [features] default = ["std"] nightly = [] std = ["lazy_static"] + [target."cfg(crossbeam_loom)".dependencies.loom] -version = "0.4" +version = "0.5" optional = true diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 30697d2..73508a9 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -4,13 +4,12 @@ name = "crossbeam-utils" # - Update CHANGELOG.md # - Update README.md # - Create "crossbeam-utils-X.Y.Z" git tag -version = "0.8.3" -authors = ["The Crossbeam Project Developers"] +version = "0.8.7" edition = "2018" +rust-version = "1.36" license = "MIT OR Apache-2.0" repository = "https://github.com/crossbeam-rs/crossbeam" homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils" -documentation = "https://docs.rs/crossbeam-utils" description = "Utilities for concurrent programming" keywords = ["scoped", "thread", "atomic", "cache"] categories = ["algorithms", "concurrency", "data-structures", "no-std"] @@ -22,10 +21,13 @@ default = ["std"] # This is enabled by default. std = ["lazy_static"] +# These features are no longer used. +# TODO: remove in the next major version. # Enable to use of unstable functionality. # This is disabled by default and requires recent nightly compiler. -# Note that this is outside of the normal semver guarantees and minor versions -# of crossbeam may make breaking changes to them at any time. +# +# 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. nightly = [] [dependencies] @@ -34,13 +36,11 @@ lazy_static = { version = "1.4.0", optional = true } # Enable the use of loom for concurrency testing. # -# This configuration option is outside of the normal semver guarantees: minor -# versions of crossbeam may make breaking changes to it at any time. +# 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.4", optional = true } - -[build-dependencies] -autocfg = "1.0.0" +loom = { version = "0.5", optional = true } [dev-dependencies] rand = "0.8" +rustversion = "1" @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/crossbeam-utils/crossbeam-utils-0.8.3.crate" + value: "https://static.crates.io/crates/crossbeam-utils/crossbeam-utils-0.8.7.crate" } - version: "0.8.3" + version: "0.8.7" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 4 + year: 2022 + month: 3 day: 1 } } @@ -10,7 +10,7 @@ https://crates.io/crates/crossbeam-utils) https://docs.rs/crossbeam-utils) [![Rust 1.36+](https://img.shields.io/badge/rust-1.36+-lightgray.svg)]( https://www.rust-lang.org) -[![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.gg/BBYwKq) +[![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) This crate provides miscellaneous tools for concurrent programming: diff --git a/TEST_MAPPING b/TEST_MAPPING index a68b776..eb8da39 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,32 +1,78 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { + "imports": [ + { + "path": "external/rust/crates/base64" + }, + { + "path": "external/rust/crates/crossbeam-deque" + }, + { + "path": "external/rust/crates/crossbeam-epoch" + }, + { + "path": "external/rust/crates/crossbeam-queue" + }, + { + "path": "external/rust/crates/once_cell" + }, + { + "path": "external/rust/crates/tinytemplate" + }, + { + "path": "external/rust/crates/tinyvec" + }, + { + "path": "external/rust/crates/unicode-xid" + }, + { + "path": "external/rust/crates/vulkano" + } + ], "presubmit": [ { - "name": "crossbeam-epoch_device_test_src_lib" + "name": "crossbeam-utils_test_src_lib" + }, + { + "name": "crossbeam-utils_test_tests_atomic_cell" + }, + { + "name": "crossbeam-utils_test_tests_cache_padded" + }, + { + "name": "crossbeam-utils_test_tests_parker" }, { - "name": "crossbeam-epoch_device_test_tests_loom" + "name": "crossbeam-utils_test_tests_sharded_lock" }, { - "name": "crossbeam-utils_device_test_src_lib" + "name": "crossbeam-utils_test_tests_thread" + }, + { + "name": "crossbeam-utils_test_tests_wait_group" + } + ], + "presubmit-rust": [ + { + "name": "crossbeam-utils_test_src_lib" }, { - "name": "crossbeam-utils_device_test_tests_atomic_cell" + "name": "crossbeam-utils_test_tests_atomic_cell" }, { - "name": "crossbeam-utils_device_test_tests_cache_padded" + "name": "crossbeam-utils_test_tests_cache_padded" }, { - "name": "crossbeam-utils_device_test_tests_parker" + "name": "crossbeam-utils_test_tests_parker" }, { - "name": "crossbeam-utils_device_test_tests_sharded_lock" + "name": "crossbeam-utils_test_tests_sharded_lock" }, { - "name": "crossbeam-utils_device_test_tests_thread" + "name": "crossbeam-utils_test_tests_thread" }, { - "name": "crossbeam-utils_device_test_tests_wait_group" + "name": "crossbeam-utils_test_tests_wait_group" } ] } @@ -1,23 +1,61 @@ -use autocfg::AutoCfg; +// 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. + +#![warn(rust_2018_idioms)] + +use std::env; + +include!("no_atomic.rs"); -// The rustc-cfg strings below are *not* public API. Please let us know by -// opening a GitHub issue if your build environment requires some way to enable -// these cfgs other than by executing our build script. fn main() { - let cfg = match AutoCfg::new() { - Ok(cfg) => cfg, + let target = match env::var("TARGET") { + Ok(target) => target, Err(e) => { println!( - "cargo:warning=crossbeam-utils: unable to determine rustc version: {}", + "cargo:warning={}: unable to get TARGET environment variable: {}", + env!("CARGO_PKG_NAME"), e ); return; } }; - cfg.emit_type_cfg("core::sync::atomic::AtomicU8", "has_atomic_u8"); - cfg.emit_type_cfg("core::sync::atomic::AtomicU16", "has_atomic_u16"); - cfg.emit_type_cfg("core::sync::atomic::AtomicU32", "has_atomic_u32"); - cfg.emit_type_cfg("core::sync::atomic::AtomicU64", "has_atomic_u64"); - cfg.emit_type_cfg("core::sync::atomic::AtomicU128", "has_atomic_u128"); + // Note that this is `no_*`, not `has_*`. This allows treating + // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't + // run. This is needed for compatibility with 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"); } diff --git a/cargo2android.json b/cargo2android.json new file mode 100644 index 0000000..6e516e0 --- /dev/null +++ b/cargo2android.json @@ -0,0 +1,9 @@ +{ + "apex-available": [ + "//apex_available:platform", + "com.android.virt" + ], + "device": true, + "run": true, + "tests": true +}
\ No newline at end of file diff --git a/no_atomic.rs b/no_atomic.rs new file mode 100644 index 0000000..90ac60a --- /dev/null +++ b/no_atomic.rs @@ -0,0 +1,71 @@ +// This file is @generated by no_atomic.sh. +// It is not intended for manual editing. + +const NO_ATOMIC_CAS: &[&str] = &[ + "avr-unknown-gnu-atmega328", + "bpfeb-unknown-none", + "bpfel-unknown-none", + "msp430-none-elf", + "riscv32i-unknown-none-elf", + "riscv32imc-unknown-none-elf", + "thumbv4t-none-eabi", + "thumbv6m-none-eabi", +]; + +#[allow(dead_code)] // Only crossbeam-utils uses this. +const NO_ATOMIC_64: &[&str] = &[ + "arm-linux-androideabi", + "armebv7r-none-eabi", + "armebv7r-none-eabihf", + "armv4t-unknown-linux-gnueabi", + "armv5te-unknown-linux-gnueabi", + "armv5te-unknown-linux-musleabi", + "armv5te-unknown-linux-uclibceabi", + "armv6k-nintendo-3ds", + "armv7r-none-eabi", + "armv7r-none-eabihf", + "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-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", + "riscv32imac-unknown-none-elf", + "riscv32imc-esp-espidf", + "riscv32imc-unknown-none-elf", + "thumbv4t-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", + "msp430-none-elf", + "riscv32i-unknown-none-elf", + "riscv32imc-unknown-none-elf", +]; diff --git a/patches/disable_panic_tests_on_android.patch b/patches/disable_panic_tests_on_android.patch index 49e4758..3306735 100644 --- a/patches/disable_panic_tests_on_android.patch +++ b/patches/disable_panic_tests_on_android.patch @@ -1,5 +1,5 @@ ---- tests/sharded_lock.rs -+++ tests/sharded_lock.rs +--- a/tests/sharded_lock.rs ++++ b/tests/sharded_lock.rs @@ -46,6 +46,8 @@ fn frob() { } @@ -63,8 +63,8 @@ fn test_get_mut_poison() { let m = Arc::new(ShardedLock::new(NonCopy(10))); let m2 = m.clone(); ---- tests/thread.rs -+++ tests/thread.rs +--- a/tests/thread.rs ++++ b/tests/thread.rs @@ -9,6 +9,8 @@ const THREADS: usize = 10; const SMALL_STACK_SIZE: usize = 20; diff --git a/src/atomic/atomic_cell.rs b/src/atomic/atomic_cell.rs index ad094b2..8a49464 100644 --- a/src/atomic/atomic_cell.rs +++ b/src/atomic/atomic_cell.rs @@ -1,9 +1,9 @@ // Necessary for implementing atomic methods for `AtomicUnit` #![allow(clippy::unit_arg)] -#![allow(clippy::let_unit_value)] use crate::primitive::sync::atomic::{self, AtomicBool}; use core::cell::UnsafeCell; +use core::cmp; use core::fmt; use core::mem; use core::sync::atomic::Ordering; @@ -258,10 +258,44 @@ impl<T: Copy + Eq> AtomicCell<T> { pub fn compare_exchange(&self, current: T, new: T) -> Result<T, T> { unsafe { atomic_compare_exchange_weak(self.value.get(), current, new) } } + + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else + /// `Err(previous_value)`. + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied + /// only once to the stored value. + /// + /// # Examples + /// + /// ```rust + /// use crossbeam_utils::atomic::AtomicCell; + /// + /// let a = AtomicCell::new(7); + /// assert_eq!(a.fetch_update(|_| None), Err(7)); + /// assert_eq!(a.fetch_update(|a| Some(a + 1)), Ok(7)); + /// assert_eq!(a.fetch_update(|a| Some(a + 1)), Ok(8)); + /// assert_eq!(a.load(), 9); + /// ``` + #[inline] + pub fn fetch_update<F>(&self, mut f: F) -> Result<T, T> + where + F: FnMut(T) -> Option<T>, + { + let mut prev = self.load(); + while let Some(next) = f(prev) { + match self.compare_exchange(prev, next) { + x @ Ok(_) => return x, + Err(next_prev) => prev = next_prev, + } + } + Err(prev) + } } macro_rules! impl_arithmetic { - ($t:ty, $example:tt) => { + ($t:ty, fallback, $example:tt) => { impl AtomicCell<$t> { /// Increments the current value by `val` and returns the previous value. /// @@ -279,10 +313,13 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_add(&self, val: $t) -> $t { - if can_transmute::<$t, atomic::AtomicUsize>() { - let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) }; - a.fetch_add(val as usize, Ordering::AcqRel) as $t - } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { let _guard = lock(self.value.get() as usize).write(); let value = unsafe { &mut *(self.value.get()) }; let old = *value; @@ -307,10 +344,13 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_sub(&self, val: $t) -> $t { - if can_transmute::<$t, atomic::AtomicUsize>() { - let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) }; - a.fetch_sub(val as usize, Ordering::AcqRel) as $t - } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { let _guard = lock(self.value.get() as usize).write(); let value = unsafe { &mut *(self.value.get()) }; let old = *value; @@ -333,10 +373,13 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_and(&self, val: $t) -> $t { - if can_transmute::<$t, atomic::AtomicUsize>() { - let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) }; - a.fetch_and(val as usize, Ordering::AcqRel) as $t - } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { let _guard = lock(self.value.get() as usize).write(); let value = unsafe { &mut *(self.value.get()) }; let old = *value; @@ -345,6 +388,35 @@ macro_rules! impl_arithmetic { } } + /// Applies bitwise "nand" to the current value and returns the previous value. + /// + /// # Examples + /// + /// ``` + /// use crossbeam_utils::atomic::AtomicCell; + /// + #[doc = $example] + /// + /// assert_eq!(a.fetch_nand(3), 7); + /// assert_eq!(a.load(), !(7 & 3)); + /// ``` + #[inline] + pub fn fetch_nand(&self, val: $t) -> $t { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value = !(old & val); + old + } + } + /// Applies bitwise "or" to the current value and returns the previous value. /// /// # Examples @@ -359,10 +431,13 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_or(&self, val: $t) -> $t { - if can_transmute::<$t, atomic::AtomicUsize>() { - let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) }; - a.fetch_or(val as usize, Ordering::AcqRel) as $t - } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { let _guard = lock(self.value.get() as usize).write(); let value = unsafe { &mut *(self.value.get()) }; let old = *value; @@ -385,10 +460,13 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_xor(&self, val: $t) -> $t { - if can_transmute::<$t, atomic::AtomicUsize>() { - let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) }; - a.fetch_xor(val as usize, Ordering::AcqRel) as $t - } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { let _guard = lock(self.value.get() as usize).write(); let value = unsafe { &mut *(self.value.get()) }; let old = *value; @@ -396,6 +474,66 @@ macro_rules! impl_arithmetic { old } } + + /// Compares and sets the maximum of the current value and `val`, + /// and returns the previous value. + /// + /// # Examples + /// + /// ``` + /// use crossbeam_utils::atomic::AtomicCell; + /// + #[doc = $example] + /// + /// assert_eq!(a.fetch_max(2), 7); + /// assert_eq!(a.load(), 7); + /// ``` + #[inline] + pub fn fetch_max(&self, val: $t) -> $t { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value = cmp::max(old, val); + old + } + } + + /// Compares and sets the minimum of the current value and `val`, + /// and returns the previous value. + /// + /// # Examples + /// + /// ``` + /// use crossbeam_utils::atomic::AtomicCell; + /// + #[doc = $example] + /// + /// assert_eq!(a.fetch_min(2), 7); + /// assert_eq!(a.load(), 2); + /// ``` + #[inline] + pub fn fetch_min(&self, val: $t) -> $t { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value = cmp::min(old, val); + old + } + } } }; ($t:ty, $atomic:ty, $example:tt) => { @@ -416,8 +554,24 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_add(&self, val: $t) -> $t { - let a = unsafe { &*(self.value.get() as *const $atomic) }; - a.fetch_add(val, Ordering::AcqRel) + if can_transmute::<$t, $atomic>() { + let a = unsafe { &*(self.value.get() as *const $atomic) }; + a.fetch_add(val, Ordering::AcqRel) + } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value = value.wrapping_add(val); + old + } + } } /// Decrements the current value by `val` and returns the previous value. @@ -436,8 +590,24 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_sub(&self, val: $t) -> $t { - let a = unsafe { &*(self.value.get() as *const $atomic) }; - a.fetch_sub(val, Ordering::AcqRel) + if can_transmute::<$t, $atomic>() { + let a = unsafe { &*(self.value.get() as *const $atomic) }; + a.fetch_sub(val, Ordering::AcqRel) + } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value = value.wrapping_sub(val); + old + } + } } /// Applies bitwise "and" to the current value and returns the previous value. @@ -454,8 +624,58 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_and(&self, val: $t) -> $t { - let a = unsafe { &*(self.value.get() as *const $atomic) }; - a.fetch_and(val, Ordering::AcqRel) + if can_transmute::<$t, $atomic>() { + let a = unsafe { &*(self.value.get() as *const $atomic) }; + a.fetch_and(val, Ordering::AcqRel) + } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value &= val; + old + } + } + } + + /// Applies bitwise "nand" to the current value and returns the previous value. + /// + /// # Examples + /// + /// ``` + /// use crossbeam_utils::atomic::AtomicCell; + /// + #[doc = $example] + /// + /// assert_eq!(a.fetch_nand(3), 7); + /// assert_eq!(a.load(), !(7 & 3)); + /// ``` + #[inline] + pub fn fetch_nand(&self, val: $t) -> $t { + if can_transmute::<$t, $atomic>() { + let a = unsafe { &*(self.value.get() as *const $atomic) }; + a.fetch_nand(val, Ordering::AcqRel) + } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value = !(old & val); + old + } + } } /// Applies bitwise "or" to the current value and returns the previous value. @@ -472,8 +692,24 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_or(&self, val: $t) -> $t { - let a = unsafe { &*(self.value.get() as *const $atomic) }; - a.fetch_or(val, Ordering::AcqRel) + if can_transmute::<$t, $atomic>() { + let a = unsafe { &*(self.value.get() as *const $atomic) }; + a.fetch_or(val, Ordering::AcqRel) + } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value |= val; + old + } + } } /// Applies bitwise "xor" to the current value and returns the previous value. @@ -490,40 +726,124 @@ macro_rules! impl_arithmetic { /// ``` #[inline] pub fn fetch_xor(&self, val: $t) -> $t { - let a = unsafe { &*(self.value.get() as *const $atomic) }; - a.fetch_xor(val, Ordering::AcqRel) + if can_transmute::<$t, $atomic>() { + let a = unsafe { &*(self.value.get() as *const $atomic) }; + a.fetch_xor(val, Ordering::AcqRel) + } else { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value ^= val; + old + } + } + } + + /// Compares and sets the maximum of the current value and `val`, + /// and returns the previous value. + /// + /// # Examples + /// + /// ``` + /// use crossbeam_utils::atomic::AtomicCell; + /// + #[doc = $example] + /// + /// assert_eq!(a.fetch_max(9), 7); + /// assert_eq!(a.load(), 9); + /// ``` + #[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 { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value = cmp::max(old, val); + old + } + } + } + + /// Compares and sets the minimum of the current value and `val`, + /// and returns the previous value. + /// + /// # Examples + /// + /// ``` + /// use crossbeam_utils::atomic::AtomicCell; + /// + #[doc = $example] + /// + /// assert_eq!(a.fetch_min(2), 7); + /// assert_eq!(a.load(), 2); + /// ``` + #[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 { + #[cfg(crossbeam_loom)] + { + let _ = val; + unimplemented!("loom does not support non-atomic atomic ops"); + } + #[cfg(not(crossbeam_loom))] + { + let _guard = lock(self.value.get() as usize).write(); + let value = unsafe { &mut *(self.value.get()) }; + let old = *value; + *value = cmp::min(old, val); + old + } + } } } }; } -#[cfg(has_atomic_u8)] impl_arithmetic!(u8, atomic::AtomicU8, "let a = AtomicCell::new(7u8);"); -#[cfg(all(has_atomic_u8, not(crossbeam_loom)))] impl_arithmetic!(i8, atomic::AtomicI8, "let a = AtomicCell::new(7i8);"); -#[cfg(has_atomic_u16)] impl_arithmetic!(u16, atomic::AtomicU16, "let a = AtomicCell::new(7u16);"); -#[cfg(all(has_atomic_u16, not(crossbeam_loom)))] impl_arithmetic!(i16, atomic::AtomicI16, "let a = AtomicCell::new(7i16);"); -#[cfg(has_atomic_u32)] impl_arithmetic!(u32, atomic::AtomicU32, "let a = AtomicCell::new(7u32);"); -#[cfg(all(has_atomic_u32, not(crossbeam_loom)))] impl_arithmetic!(i32, atomic::AtomicI32, "let a = AtomicCell::new(7i32);"); -#[cfg(has_atomic_u64)] +#[cfg(not(crossbeam_no_atomic_64))] impl_arithmetic!(u64, atomic::AtomicU64, "let a = AtomicCell::new(7u64);"); -#[cfg(all(has_atomic_u64, not(crossbeam_loom)))] +#[cfg(not(crossbeam_no_atomic_64))] impl_arithmetic!(i64, atomic::AtomicI64, "let a = AtomicCell::new(7i64);"); -#[cfg(all(has_atomic_u128, not(crossbeam_loom)))] -impl_arithmetic!(u128, atomic::AtomicU128, "let a = AtomicCell::new(7u128);"); -#[cfg(all(has_atomic_u128, not(crossbeam_loom)))] -impl_arithmetic!(i128, atomic::AtomicI128, "let a = AtomicCell::new(7i128);"); +#[cfg(crossbeam_no_atomic_64)] +impl_arithmetic!(u64, fallback, "let a = AtomicCell::new(7u64);"); +#[cfg(crossbeam_no_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, 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);" ); -#[cfg(not(crossbeam_loom))] impl_arithmetic!( isize, atomic::AtomicIsize, @@ -552,6 +872,30 @@ impl AtomicCell<bool> { a.fetch_and(val, Ordering::AcqRel) } + /// Applies logical "nand" to the current value and returns the previous value. + /// + /// # Examples + /// + /// ``` + /// use crossbeam_utils::atomic::AtomicCell; + /// + /// let a = AtomicCell::new(true); + /// + /// assert_eq!(a.fetch_nand(false), true); + /// assert_eq!(a.load(), true); + /// + /// assert_eq!(a.fetch_nand(true), true); + /// assert_eq!(a.load(), false); + /// + /// assert_eq!(a.fetch_nand(false), false); + /// assert_eq!(a.load(), true); + /// ``` + #[inline] + pub fn fetch_nand(&self, val: bool) -> bool { + let a = unsafe { &*(self.value.get() as *const AtomicBool) }; + a.fetch_nand(val, Ordering::AcqRel) + } + /// Applies logical "or" to the current value and returns the previous value. /// /// # Examples @@ -657,105 +1001,13 @@ fn lock(addr: usize) -> &'static SeqLock { // stored at addresses that are multiples of 3. It'd be too bad if `LEN` was divisible by 3. // In order to protect from such cases, we simply choose a large prime number for `LEN`. const LEN: usize = 97; - + #[allow(clippy::declare_interior_mutable_const)] + const L: SeqLock = SeqLock::new(); static LOCKS: [SeqLock; LEN] = [ - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), - SeqLock::new(), + L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, + L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, + L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, + L, L, L, L, L, L, L, ]; // If the modulus is a constant number, the compiler will use crazy math to transform this into @@ -778,7 +1030,6 @@ impl AtomicUnit { #[inline] fn swap(&self, _val: (), _order: Ordering) {} - #[allow(clippy::unnecessary_wraps)] // This is intentional. #[inline] fn compare_exchange_weak( &self, @@ -807,18 +1058,14 @@ macro_rules! atomic { ($t:ty, $a:ident, $atomic_op:expr, $fallback_op:expr) => { loop { atomic!(@check, $t, AtomicUnit, $a, $atomic_op); - atomic!(@check, $t, atomic::AtomicUsize, $a, $atomic_op); - #[cfg(has_atomic_u8)] atomic!(@check, $t, atomic::AtomicU8, $a, $atomic_op); - #[cfg(has_atomic_u16)] atomic!(@check, $t, atomic::AtomicU16, $a, $atomic_op); - #[cfg(has_atomic_u32)] atomic!(@check, $t, atomic::AtomicU32, $a, $atomic_op); - #[cfg(has_atomic_u64)] + #[cfg(not(crossbeam_no_atomic_64))] atomic!(@check, $t, atomic::AtomicU64, $a, $atomic_op); - #[cfg(has_atomic_u128)] - atomic!(@check, $t, atomic::AtomicU128, $a, $atomic_op); + // TODO: AtomicU128 is unstable + // atomic!(@check, $t, atomic::AtomicU128, $a, $atomic_op); #[cfg(crossbeam_loom)] unimplemented!("loom does not support non-atomic atomic ops"); @@ -831,17 +1078,14 @@ macro_rules! atomic { /// Returns `true` if operations on `AtomicCell<T>` are lock-free. const fn atomic_is_lock_free<T>() -> bool { // HACK(taiki-e): This is equivalent to `atomic! { T, _a, true, false }`, but can be used in const fn even in Rust 1.36. - let is_lock_free = can_transmute::<T, AtomicUnit>() | can_transmute::<T, atomic::AtomicUsize>(); - #[cfg(has_atomic_u8)] - let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU8>(); - #[cfg(has_atomic_u16)] - let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU16>(); - #[cfg(has_atomic_u32)] - let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU32>(); - #[cfg(has_atomic_u64)] + let is_lock_free = can_transmute::<T, AtomicUnit>() + | can_transmute::<T, atomic::AtomicU8>() + | can_transmute::<T, atomic::AtomicU16>() + | can_transmute::<T, atomic::AtomicU32>(); + #[cfg(not(crossbeam_no_atomic_64))] let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU64>(); - #[cfg(has_atomic_u128)] - let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU128>(); + // TODO: AtomicU128 is unstable + // let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU128>(); is_lock_free } diff --git a/src/atomic/consume.rs b/src/atomic/consume.rs index 0fbd93e..277b370 100644 --- a/src/atomic/consume.rs +++ b/src/atomic/consume.rs @@ -1,5 +1,6 @@ #[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; /// Trait which allows reading from primitive atomic types with "consume" ordering. @@ -25,6 +26,7 @@ pub trait AtomicConsume { fn load_consume(&self) -> Self::Val; } +#[cfg(not(crossbeam_no_atomic))] #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] macro_rules! impl_consume { () => { @@ -37,6 +39,7 @@ macro_rules! impl_consume { }; } +#[cfg(not(crossbeam_no_atomic))] #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] macro_rules! impl_consume { () => { @@ -49,12 +52,13 @@ macro_rules! impl_consume { macro_rules! impl_atomic { ($atomic:ident, $val:ty) => { - impl AtomicConsume for ::core::sync::atomic::$atomic { + #[cfg(not(crossbeam_no_atomic))] + impl AtomicConsume for core::sync::atomic::$atomic { type Val = $val; impl_consume!(); } #[cfg(crossbeam_loom)] - impl AtomicConsume for ::loom::sync::atomic::$atomic { + impl AtomicConsume for loom::sync::atomic::$atomic { type Val = $val; impl_consume!(); } @@ -63,32 +67,26 @@ macro_rules! impl_atomic { impl_atomic!(AtomicBool, bool); impl_atomic!(AtomicUsize, usize); -#[cfg(not(crossbeam_loom))] impl_atomic!(AtomicIsize, isize); -#[cfg(has_atomic_u8)] impl_atomic!(AtomicU8, u8); -#[cfg(has_atomic_u8)] impl_atomic!(AtomicI8, i8); -#[cfg(has_atomic_u16)] impl_atomic!(AtomicU16, u16); -#[cfg(has_atomic_u16)] impl_atomic!(AtomicI16, i16); -#[cfg(has_atomic_u32)] impl_atomic!(AtomicU32, u32); -#[cfg(has_atomic_u32)] impl_atomic!(AtomicI32, i32); -#[cfg(has_atomic_u64)] +#[cfg(not(crossbeam_no_atomic_64))] impl_atomic!(AtomicU64, u64); -#[cfg(has_atomic_u64)] +#[cfg(not(crossbeam_no_atomic_64))] impl_atomic!(AtomicI64, i64); -impl<T> AtomicConsume for ::core::sync::atomic::AtomicPtr<T> { +#[cfg(not(crossbeam_no_atomic))] +impl<T> AtomicConsume for core::sync::atomic::AtomicPtr<T> { type Val = *mut T; impl_consume!(); } #[cfg(crossbeam_loom)] -impl<T> AtomicConsume for ::loom::sync::atomic::AtomicPtr<T> { +impl<T> AtomicConsume for loom::sync::atomic::AtomicPtr<T> { type Val = *mut T; impl_consume!(); } diff --git a/src/atomic/mod.rs b/src/atomic/mod.rs index 874eaf2..fc713fc 100644 --- a/src/atomic/mod.rs +++ b/src/atomic/mod.rs @@ -3,11 +3,9 @@ //! * [`AtomicCell`], a thread-safe mutable memory location. //! * [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering. +#[cfg(not(crossbeam_no_atomic_cas))] #[cfg(not(crossbeam_loom))] -use cfg_if::cfg_if; - -#[cfg(not(crossbeam_loom))] -cfg_if! { +cfg_if::cfg_if! { // Use "wide" sequence lock if the pointer width <= 32 for preventing its counter against wrap // around. // @@ -25,8 +23,10 @@ cfg_if! { } } +#[cfg(not(crossbeam_no_atomic_cas))] mod atomic_cell; mod consume; +#[cfg(not(crossbeam_no_atomic_cas))] pub use self::atomic_cell::AtomicCell; pub use self::consume::AtomicConsume; @@ -38,7 +38,6 @@ unreachable_pub )] #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(cfg_target_has_atomic))] #[cfg(crossbeam_loom)] #[allow(unused_imports)] @@ -47,7 +46,8 @@ mod primitive { pub(crate) mod atomic { pub(crate) use loom::sync::atomic::spin_loop_hint; pub(crate) use loom::sync::atomic::{ - AtomicBool, AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, + AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, + AtomicU32, AtomicU64, AtomicU8, AtomicUsize, }; // FIXME: loom does not support compiler_fence at the moment. @@ -70,15 +70,13 @@ mod primitive { // use [`core::hint::spin_loop`] instead. #[allow(deprecated)] pub(crate) use core::sync::atomic::spin_loop_hint; - pub(crate) use core::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize}; - #[cfg(has_atomic_u16)] - pub(crate) use core::sync::atomic::{AtomicI16, AtomicU16}; - #[cfg(has_atomic_u32)] - pub(crate) use core::sync::atomic::{AtomicI32, AtomicU32}; - #[cfg(has_atomic_u64)] + #[cfg(not(crossbeam_no_atomic))] + pub(crate) use core::sync::atomic::{ + AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, + AtomicU8, AtomicUsize, + }; + #[cfg(not(crossbeam_no_atomic_64))] pub(crate) use core::sync::atomic::{AtomicI64, AtomicU64}; - #[cfg(has_atomic_u8)] - pub(crate) use core::sync::atomic::{AtomicI8, AtomicU8}; } #[cfg(feature = "std")] @@ -86,15 +84,6 @@ mod primitive { } } -cfg_if! { - if #[cfg(feature = "alloc")] { - extern crate alloc; - } else if #[cfg(feature = "std")] { - extern crate std as alloc; - } -} - -#[cfg_attr(feature = "nightly", cfg(target_has_atomic = "ptr"))] pub mod atomic; mod cache_padded; diff --git a/src/sync/parker.rs b/src/sync/parker.rs index aefa515..531f5a5 100644 --- a/src/sync/parker.rs +++ b/src/sync/parker.rs @@ -175,6 +175,7 @@ impl Parker { /// /// let p = Parker::new(); /// let raw = Parker::into_raw(p); + /// # let _ = unsafe { Parker::from_raw(raw) }; /// ``` pub fn into_raw(this: Parker) -> *const () { Unparker::into_raw(this.unparker) @@ -258,6 +259,7 @@ impl Unparker { /// let p = Parker::new(); /// let u = p.unparker().clone(); /// let raw = Unparker::into_raw(u); + /// # let _ = unsafe { Unparker::from_raw(raw) }; /// ``` pub fn into_raw(this: Unparker) -> *const () { Arc::into_raw(this.inner) as *const () diff --git a/src/thread.rs b/src/thread.rs index c57d076..a59a4f5 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -446,7 +446,7 @@ impl<'scope, 'env> ScopedThreadBuilder<'scope, 'env> { unsafe { mem::transmute(closure) }; // Finally, spawn the closure. - self.builder.spawn(move || closure())? + self.builder.spawn(closure)? }; let thread = handle.thread().clone(); diff --git a/tests/atomic_cell.rs b/tests/atomic_cell.rs index 3d91d81..da7a6e1 100644 --- a/tests/atomic_cell.rs +++ b/tests/atomic_cell.rs @@ -1,3 +1,4 @@ +use std::mem; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; @@ -8,18 +9,47 @@ fn is_lock_free() { struct UsizeWrap(usize); struct U8Wrap(bool); struct I16Wrap(i16); - - assert_eq!(AtomicCell::<usize>::is_lock_free(), true); - assert_eq!(AtomicCell::<isize>::is_lock_free(), true); - assert_eq!(AtomicCell::<UsizeWrap>::is_lock_free(), true); - - assert_eq!(AtomicCell::<u8>::is_lock_free(), cfg!(has_atomic_u8)); - assert_eq!(AtomicCell::<bool>::is_lock_free(), cfg!(has_atomic_u8)); - assert_eq!(AtomicCell::<U8Wrap>::is_lock_free(), cfg!(has_atomic_u8)); - - assert_eq!(AtomicCell::<I16Wrap>::is_lock_free(), cfg!(has_atomic_u16)); - - assert_eq!(AtomicCell::<u128>::is_lock_free(), cfg!(has_atomic_u128)); + #[repr(align(8))] + struct U64Align8(u64); + + assert!(AtomicCell::<usize>::is_lock_free()); + assert!(AtomicCell::<isize>::is_lock_free()); + assert!(AtomicCell::<UsizeWrap>::is_lock_free()); + + assert!(AtomicCell::<()>::is_lock_free()); + + assert!(AtomicCell::<u8>::is_lock_free()); + assert!(AtomicCell::<i8>::is_lock_free()); + assert!(AtomicCell::<bool>::is_lock_free()); + assert!(AtomicCell::<U8Wrap>::is_lock_free()); + + assert!(AtomicCell::<u16>::is_lock_free()); + assert!(AtomicCell::<i16>::is_lock_free()); + assert!(AtomicCell::<I16Wrap>::is_lock_free()); + + assert!(AtomicCell::<u32>::is_lock_free()); + assert!(AtomicCell::<i32>::is_lock_free()); + + // Sizes of both types must be equal, and the alignment of `u64` must be greater or equal than + // that of `AtomicU64`. In i686-unknown-linux-gnu, the alignment of `u64` is `4` and alignment + // of `AtomicU64` is `8`, so `AtomicCell<u64>` is not lock-free. + assert_eq!( + AtomicCell::<u64>::is_lock_free(), + cfg!(not(crossbeam_no_atomic_64)) + && cfg!(any( + target_pointer_width = "64", + target_pointer_width = "128" + )) + ); + assert_eq!(mem::size_of::<U64Align8>(), 8); + assert_eq!(mem::align_of::<U64Align8>(), 8); + assert_eq!( + AtomicCell::<U64Align8>::is_lock_free(), + cfg!(not(crossbeam_no_atomic_64)) + ); + + // AtomicU128 is unstable + assert!(!AtomicCell::<u128>::is_lock_free()); } #[test] @@ -234,3 +264,69 @@ fn const_atomic_cell_new() { CELL.store(1); assert_eq!(CELL.load(), 1); } + +// https://github.com/crossbeam-rs/crossbeam/pull/767 +macro_rules! test_arithmetic { + ($test_name:ident, $ty:ident) => { + #[test] + fn $test_name() { + let a: AtomicCell<$ty> = AtomicCell::new(7); + + assert_eq!(a.fetch_add(3), 7); + assert_eq!(a.load(), 10); + + assert_eq!(a.fetch_sub(3), 10); + assert_eq!(a.load(), 7); + + assert_eq!(a.fetch_and(3), 7); + assert_eq!(a.load(), 3); + + assert_eq!(a.fetch_or(16), 3); + assert_eq!(a.load(), 19); + + assert_eq!(a.fetch_xor(2), 19); + assert_eq!(a.load(), 17); + + assert_eq!(a.fetch_max(18), 17); + assert_eq!(a.load(), 18); + + assert_eq!(a.fetch_min(17), 18); + assert_eq!(a.load(), 17); + + assert_eq!(a.fetch_nand(7), 17); + assert_eq!(a.load(), !(17 & 7)); + } + }; +} +test_arithmetic!(arithmetic_u8, u8); +test_arithmetic!(arithmetic_i8, i8); +test_arithmetic!(arithmetic_u16, u16); +test_arithmetic!(arithmetic_i16, i16); +test_arithmetic!(arithmetic_u32, u32); +test_arithmetic!(arithmetic_i32, i32); +test_arithmetic!(arithmetic_u64, u64); +test_arithmetic!(arithmetic_i64, i64); +test_arithmetic!(arithmetic_u128, u128); +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)] + #[repr(align(8))] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + enum Test { + Field(u32), + FieldLess, + } + + assert_eq!(mem::size_of::<Test>(), 8); + assert_eq!( + AtomicCell::<Test>::is_lock_free(), + cfg!(not(crossbeam_no_atomic_64)) + ); + let x = AtomicCell::new(Test::FieldLess); + assert_eq!(x.load(), Test::FieldLess); +} diff --git a/tests/cache_padded.rs b/tests/cache_padded.rs index c9e7687..86e9a77 100644 --- a/tests/cache_padded.rs +++ b/tests/cache_padded.rs @@ -85,6 +85,7 @@ fn drops() { assert_eq!(count.get(), 2); } +#[allow(clippy::clone_on_copy)] // This is intentional. #[test] fn clone() { let a = CachePadded::new(17); diff --git a/tests/sharded_lock.rs b/tests/sharded_lock.rs index b4b8565..0718b44 100644 --- a/tests/sharded_lock.rs +++ b/tests/sharded_lock.rs @@ -21,6 +21,9 @@ fn smoke() { #[test] fn frob() { const N: u32 = 10; + #[cfg(miri)] + const M: usize = 100; + #[cfg(not(miri))] const M: usize = 1000; let r = Arc::new(ShardedLock::new(())); @@ -148,7 +151,7 @@ fn arc() { fn arc_access_in_unwind() { let arc = Arc::new(ShardedLock::new(1)); let arc2 = arc.clone(); - let _ = thread::spawn(move || -> () { + let _ = thread::spawn(move || { struct Unwinder { i: Arc<ShardedLock<isize>>, } |