From 41784ae907d98886f082544a4b9500dbf3794adf Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Mon, 3 Apr 2023 08:09:11 +0200 Subject: Upgrade spin to 0.9.7 This project was upgraded with external_updater. Usage: tools/external_updater/updater.sh update rust/crates/spin For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md Test: TreeHugger Change-Id: I7edc209bfb9d09b9b697534e1ef175428ffa942d --- .cargo_vcs_info.json | 2 +- .github/workflows/rust.yml | 2 + Android.bp | 13 ++--- CHANGELOG.md | 12 +++++ Cargo.toml | 12 ++++- Cargo.toml.orig | 23 ++++++-- METADATA | 8 +-- README.md | 12 +++-- benches/mutex.rs | 128 +++++++++++++++++++++++++++++++++++++++++++++ cargo2android.json | 2 +- src/mutex/fair.rs | 3 ++ src/mutex/spin.rs | 3 ++ src/once.rs | 34 +++++++++++- src/rwlock.rs | 9 ++++ 14 files changed, 238 insertions(+), 25 deletions(-) create mode 100644 benches/mutex.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 6f677e3..fd12933 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "5087c8ddb5d080b5bd6c898f95e239bcb3512c22" + "sha1": "a080ab5a952290e32bc455213631ffddb4d794e4" }, "path_in_vcs": "" } \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 04280c5..ed2b6ce 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,6 +9,8 @@ on: env: CARGO_TERM_COLOR: always +permissions: read-all + jobs: test: runs-on: ubuntu-latest diff --git a/Android.bp b/Android.bp index 23b4dae..e2e6eb4 100644 --- a/Android.bp +++ b/Android.bp @@ -36,7 +36,7 @@ rust_library { host_supported: true, crate_name: "spin", cargo_env_compat: true, - cargo_pkg_version: "0.9.5", + cargo_pkg_version: "0.9.7", srcs: ["src/lib.rs"], edition: "2015", features: [ @@ -59,7 +59,7 @@ rust_test { host_supported: true, crate_name: "spin", cargo_env_compat: true, - cargo_pkg_version: "0.9.5", + cargo_pkg_version: "0.9.7", srcs: ["src/lib.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -71,6 +71,9 @@ rust_test { "once", "std", ], + rustlibs: [ + "libcriterion", + ], } rust_library_rlib { @@ -91,9 +94,3 @@ rust_library_rlib { ], min_sdk_version: "29", } - - -// Errors when listing tests: -// error[E0433]: failed to resolve: could not find `Mutex` in `spin` -// error[E0433]: failed to resolve: could not find `RwLock` in `spin` -// error: could not compile `spin` due to 2 previous errors diff --git a/CHANGELOG.md b/CHANGELOG.md index 5093ea9..e62adfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +# [0.9.7] - 2023-03-27 + +### Fixed + +- Relaxed accidentally restricted `Send`/`Sync` bounds for `Mutex` guards + +# [0.9.6] - 2023-03-13 + +### Fixed + +- Relaxed accidentally restricted `Send`/`Sync` bounds for `RwLock` guards + # [0.9.5] - 2023-02-07 ### Added diff --git a/Cargo.toml b/Cargo.toml index 33ea628..0a4f8cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ [package] rust-version = "1.38" name = "spin" -version = "0.9.5" +version = "0.9.7" authors = [ "Mathijs van de Nes ", "John Ericson ", @@ -35,16 +35,24 @@ rustdoc-args = [ "docsrs", ] +[[bench]] +name = "mutex" +harness = false +required-features = ["ticket_mutex"] + [dependencies.lock_api_crate] version = "0.4" optional = true package = "lock_api" [dependencies.portable-atomic] -version = "0.3" +version = "1" optional = true default-features = false +[dev-dependencies.criterion] +version = "0.4" + [features] barrier = ["mutex"] default = [ diff --git a/Cargo.toml.orig b/Cargo.toml.orig index cb9df1d..ca2fdc3 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "spin" -version = "0.9.5" +version = "0.9.7" authors = [ "Mathijs van de Nes ", "John Ericson ", @@ -14,7 +14,7 @@ rust-version = "1.38" [dependencies] lock_api_crate = { package = "lock_api", version = "0.4", optional = true } -portable-atomic = { version = "0.3", optional = true, default-features = false } +portable-atomic = { version = "1", optional = true, default-features = false } [features] default = ["lock_api", "mutex", "spin_mutex", "rwlock", "once", "lazy", "barrier"] @@ -52,11 +52,24 @@ lock_api = ["lock_api_crate"] # Enables std-only features such as yield-relaxing. std = [] -# Use the portable_atomic crate to support platforms without native atomic operations -# cfg 'portable_atomic_unsafe_assume_single_core' must also be set by the final binary crate. -# This cfg is unsafe and enabling it for multicore systems is unsound. +# Use the portable_atomic crate to support platforms without native atomic operations. +# The `portable_atomic_unsafe_assume_single_core` cfg or `critical-section` feature +# of `portable-atomic` crate must also be set by the final binary crate. +# When using the cfg, note that it is unsafe and enabling it for multicore systems is unsound. +# When using the `critical-section` feature, you need to implement the critical-section +# implementation that sound for your system by implementing an unsafe trait. +# See the documentation for the `portable-atomic` crate for more information: +# https://docs.rs/portable-atomic/latest/portable_atomic/#optional-cfg portable_atomic = ["portable-atomic"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[dev-dependencies] +criterion = "0.4" + +[[bench]] +name = "mutex" +harness = false +required-features = ["ticket_mutex"] diff --git a/METADATA b/METADATA index bb8ee2c..7de7cf6 100644 --- a/METADATA +++ b/METADATA @@ -11,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/spin/spin-0.9.5.crate" + value: "https://static.crates.io/crates/spin/spin-0.9.7.crate" } - version: "0.9.5" + version: "0.9.7" license_type: NOTICE last_upgrade_date { year: 2023 - month: 2 - day: 17 + month: 4 + day: 3 } } diff --git a/README.md b/README.md index 4af6cf9..7fd3780 100644 --- a/README.md +++ b/README.md @@ -94,15 +94,21 @@ The crate comes with a few feature flags that you may wish to use. - `portable_atomic` enables usage of the `portable-atomic` crate to support platforms without native atomic operations (Cortex-M0, etc.). - The `portable_atomic_unsafe_assume_single_core` cfg flag - must also be set by the final binary crate. - This can be done by adapting the following snippet to the `.cargo/config` file: + The `portable_atomic_unsafe_assume_single_core` cfg or `critical-section` feature + of `portable-atomic` crate must also be set by the final binary crate. + + When using the cfg, this can be done by adapting the following snippet to the `.cargo/config` file: ``` [target.] rustflags = [ "--cfg", "portable_atomic_unsafe_assume_single_core" ] ``` Note that this cfg is unsafe by nature, and enabling it for multicore systems is unsound. + When using the `critical-section` feature, you need to implement the critical-section + implementation that sound for your system by implementing an unsafe trait. + See [the documentation for the `portable-atomic` crate](https://docs.rs/portable-atomic/latest/portable_atomic/#optional-cfg) + for more information. + ## Remarks It is often desirable to have a lock shared between threads. Wrapping the lock in an diff --git a/benches/mutex.rs b/benches/mutex.rs new file mode 100644 index 0000000..5201145 --- /dev/null +++ b/benches/mutex.rs @@ -0,0 +1,128 @@ +#![feature(generic_associated_types)] + +#[macro_use] +extern crate criterion; + +use criterion::{Criterion, Bencher, black_box}; +use std::{ + ops::DerefMut, + sync::Arc, +}; + +trait Mutex: Send + Sync + 'static { + type Guard<'a>: DerefMut where Self: 'a; + fn new(x: T) -> Self; + fn lock(&self) -> Self::Guard<'_>; +} + +impl Mutex for spin::mutex::SpinMutex { + type Guard<'a> = spin::mutex::SpinMutexGuard<'a, T> where Self: 'a; + fn new(x: T) -> Self { spin::mutex::SpinMutex::new(x) } + fn lock(&self) -> Self::Guard<'_> { self.lock() } +} + +impl Mutex for spin::mutex::TicketMutex { + type Guard<'a> = spin::mutex::TicketMutexGuard<'a, T> where Self: 'a; + fn new(x: T) -> Self { spin::mutex::TicketMutex::new(x) } + fn lock(&self) -> Self::Guard<'_> { self.lock() } +} + +impl Mutex for std::sync::Mutex { + type Guard<'a> = std::sync::MutexGuard<'a, T> where Self: 'a; + fn new(x: T) -> Self { std::sync::Mutex::new(x) } + fn lock(&self) -> Self::Guard<'_> { self.lock().unwrap() } +} + +fn gen_create>(b: &mut Bencher) { + b.iter(|| { + let n = black_box(42); + M::new(n) + }); +} + +fn gen_lock_unlock>(b: &mut Bencher) { + let m = M::new(0); + b.iter(|| { + let mut m = m.lock(); + *m = m.wrapping_add(1); + drop(m); + }); +} + +fn gen_lock_unlock_read_contention>(b: &mut Bencher) { + let m = Arc::new(M::new(0)); + let thread = std::thread::spawn({ + let m = m.clone(); + move || { + while Arc::strong_count(&m) > 1 { + for _ in 0..1000 { + black_box(*m.lock()); + } + } + } + }); + b.iter(|| { + let mut m = m.lock(); + *m = m.wrapping_add(1); + drop(m); + }); + drop(m); + thread.join().unwrap(); +} + +fn gen_lock_unlock_write_contention>(b: &mut Bencher) { + let m = Arc::new(M::new(0)); + let thread = std::thread::spawn({ + let m = m.clone(); + move || { + while Arc::strong_count(&m) > 1 { + for _ in 0..1000 { + let mut m = m.lock(); + *m = m.wrapping_add(1); + drop(m); + } + } + } + }); + b.iter(|| { + let mut m = m.lock(); + *m = m.wrapping_add(1); + drop(m); + }); + drop(m); + thread.join().unwrap(); +} + +fn create(b: &mut Criterion) { + b.bench_function("create-spin-spinmutex", |b| gen_create::>(b)); + b.bench_function("create-spin-ticketmutex", |b| gen_create::>(b)); + b.bench_function("create-std", |b| gen_create::>(b)); +} + +fn lock_unlock(b: &mut Criterion) { + b.bench_function("lock_unlock-spin-spinmutex", |b| gen_lock_unlock::>(b)); + b.bench_function("lock_unlock-spin-ticketmutex", |b| gen_lock_unlock::>(b)); + b.bench_function("lock_unlock-std", |b| gen_lock_unlock::>(b)); +} + +fn lock_unlock_read_contention(b: &mut Criterion) { + b.bench_function("lock_unlock_read_contention-spin-spinmutex", |b| gen_lock_unlock_read_contention::>(b)); + b.bench_function("lock_unlock_read_contention-spin-ticketmutex", |b| gen_lock_unlock_read_contention::>(b)); + b.bench_function("lock_unlock_read_contention-std", |b| gen_lock_unlock_read_contention::>(b)); +} + +fn lock_unlock_write_contention(b: &mut Criterion) { + b.bench_function("lock_unlock_write_contention-spin-spinmutex", |b| gen_lock_unlock_write_contention::>(b)); + b.bench_function("lock_unlock_write_contention-spin-ticketmutex", |b| gen_lock_unlock_write_contention::>(b)); + b.bench_function("lock_unlock_write_contention-std", |b| gen_lock_unlock_write_contention::>(b)); +} + +criterion_group!( + mutex, + create, + lock_unlock, + lock_unlock_read_contention, + lock_unlock_write_contention, +); + +criterion_main!(mutex); diff --git a/cargo2android.json b/cargo2android.json index 086d38a..0be577a 100644 --- a/cargo2android.json +++ b/cargo2android.json @@ -12,4 +12,4 @@ "min-sdk-version": "29", "run": true, "tests": true -} \ No newline at end of file +} diff --git a/src/mutex/fair.rs b/src/mutex/fair.rs index dde3994..db07ad6 100644 --- a/src/mutex/fair.rs +++ b/src/mutex/fair.rs @@ -107,6 +107,9 @@ pub enum LockRejectReason { unsafe impl Sync for FairMutex {} unsafe impl Send for FairMutex {} +unsafe impl Sync for FairMutexGuard<'_, T> {} +unsafe impl Send for FairMutexGuard<'_, T> {} + impl FairMutex { /// Creates a new [`FairMutex`] wrapping the supplied data. /// diff --git a/src/mutex/spin.rs b/src/mutex/spin.rs index 1ee572d..561d765 100644 --- a/src/mutex/spin.rs +++ b/src/mutex/spin.rs @@ -87,6 +87,9 @@ pub struct SpinMutexGuard<'a, T: ?Sized + 'a> { unsafe impl Sync for SpinMutex {} unsafe impl Send for SpinMutex {} +unsafe impl Sync for SpinMutexGuard<'_, T> {} +unsafe impl Send for SpinMutexGuard<'_, T> {} + impl SpinMutex { /// Creates a new [`SpinMutex`] wrapping the supplied data. /// diff --git a/src/once.rs b/src/once.rs index 0b4a30c..5f0186d 100644 --- a/src/once.rs +++ b/src/once.rs @@ -1,4 +1,3 @@ - //! Synchronization primitives for one-time evaluation. use crate::{ @@ -455,6 +454,23 @@ impl Once { } } + /// Returns a mutable reference to the inner value + /// + /// # Safety + /// + /// This is *extremely* unsafe if the `Once` has not already been initialized because a reference to uninitialized + /// memory will be returned, immediately triggering undefined behaviour (even if the reference goes unused). + /// However, this can be useful in some instances for exposing the `Once` to FFI or when the overhead of atomically + /// checking initialization is unacceptable and the `Once` has already been initialized. + pub unsafe fn get_mut_unchecked(&mut self) -> &mut T { + debug_assert_eq!( + self.status.load(Ordering::SeqCst), + Status::Complete, + "Attempted to access an unintialized Once. If this was to run without debug checks, this would be undefined behavior. This is a serious bug and you must fix it.", + ); + self.force_get_mut() + } + /// Returns a the inner value if the [`Once`] has been initialized. /// /// Because this method requires ownership of the [`Once`], no synchronization overhead @@ -466,6 +482,22 @@ impl Once { } } + /// Returns a the inner value if the [`Once`] has been initialized. + /// # Safety + /// + /// This is *extremely* unsafe if the `Once` has not already been initialized because a reference to uninitialized + /// memory will be returned, immediately triggering undefined behaviour (even if the reference goes unused) + /// This can be useful, if `Once` has already been initialized, and you want to bypass an + /// option check. + pub unsafe fn into_inner_unchecked(self) -> T { + debug_assert_eq!( + self.status.load(Ordering::SeqCst), + Status::Complete, + "Attempted to access an unintialized Once. If this was to run without debug checks, this would be undefined behavior. This is a serious bug and you must fix it.", + ); + self.force_into_inner() + } + /// Checks whether the value has been initialized. /// /// This is done using [`Acquire`](core::sync::atomic::Ordering::Acquire) ordering, and diff --git a/src/rwlock.rs b/src/rwlock.rs index ab5fbf3..beae5c1 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -111,6 +111,15 @@ pub struct RwLockUpgradableGuard<'a, T: 'a + ?Sized, R = Spin> { unsafe impl Send for RwLock {} unsafe impl Sync for RwLock {} +unsafe impl Send for RwLockWriteGuard<'_, T, R> {} +unsafe impl Sync for RwLockWriteGuard<'_, T, R> {} + +unsafe impl Send for RwLockReadGuard<'_, T> {} +unsafe impl Sync for RwLockReadGuard<'_, T> {} + +unsafe impl Send for RwLockUpgradableGuard<'_, T, R> {} +unsafe impl Sync for RwLockUpgradableGuard<'_, T, R> {} + impl RwLock { /// Creates a new spinlock wrapping the supplied data. /// -- cgit v1.2.3