aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-04-04 02:59:46 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-04-04 02:59:46 +0000
commit95b260d177e13ae9f32386d1fa659686d64a9c86 (patch)
tree30fab8e942b35661e92e0f597cb0da429f72430d
parent26f7d7977ac12a7177fcc31dc95afeb0ce76a9fd (diff)
parentd2677e11395fe3b4f2d663d30bdc3527e5e00ec7 (diff)
downloadspin-android14-release.tar.gz
Change-Id: I4f53639ac1362f0ad461f00be4abe5496a65b4dd
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/rust.yml2
-rw-r--r--Android.bp13
-rw-r--r--CHANGELOG.md12
-rw-r--r--Cargo.toml12
-rw-r--r--Cargo.toml.orig23
-rw-r--r--METADATA8
-rw-r--r--README.md12
-rw-r--r--benches/mutex.rs128
-rw-r--r--cargo2android.json2
-rw-r--r--src/mutex/fair.rs3
-rw-r--r--src/mutex/spin.rs3
-rw-r--r--src/once.rs34
-rw-r--r--src/rwlock.rs9
14 files changed, 238 insertions, 25 deletions
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 <git@mathijs.vd-nes.nl>",
"John Ericson <git@JohnEricson.me>",
@@ -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 <git@mathijs.vd-nes.nl>",
"John Ericson <git@JohnEricson.me>",
@@ -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.<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<T>: Send + Sync + 'static {
+ type Guard<'a>: DerefMut<Target = T> where Self: 'a;
+ fn new(x: T) -> Self;
+ fn lock(&self) -> Self::Guard<'_>;
+}
+
+impl<T: Send + 'static> Mutex<T> for spin::mutex::SpinMutex<T> {
+ 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<T: Send + 'static> Mutex<T> for spin::mutex::TicketMutex<T> {
+ 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<T: Send + 'static> Mutex<T> for std::sync::Mutex<T> {
+ 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<M: Mutex<u32>>(b: &mut Bencher) {
+ b.iter(|| {
+ let n = black_box(42);
+ M::new(n)
+ });
+}
+
+fn gen_lock_unlock<M: Mutex<u32>>(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<M: Mutex<u32>>(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<M: Mutex<u32>>(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::<spin::mutex::SpinMutex<u32>>(b));
+ b.bench_function("create-spin-ticketmutex", |b| gen_create::<spin::mutex::TicketMutex<u32>>(b));
+ b.bench_function("create-std", |b| gen_create::<std::sync::Mutex<u32>>(b));
+}
+
+fn lock_unlock(b: &mut Criterion) {
+ b.bench_function("lock_unlock-spin-spinmutex", |b| gen_lock_unlock::<spin::mutex::SpinMutex<u32>>(b));
+ b.bench_function("lock_unlock-spin-ticketmutex", |b| gen_lock_unlock::<spin::mutex::TicketMutex<u32>>(b));
+ b.bench_function("lock_unlock-std", |b| gen_lock_unlock::<std::sync::Mutex<u32>>(b));
+}
+
+fn lock_unlock_read_contention(b: &mut Criterion) {
+ b.bench_function("lock_unlock_read_contention-spin-spinmutex", |b| gen_lock_unlock_read_contention::<spin::mutex::SpinMutex<u32>>(b));
+ b.bench_function("lock_unlock_read_contention-spin-ticketmutex", |b| gen_lock_unlock_read_contention::<spin::mutex::TicketMutex<u32>>(b));
+ b.bench_function("lock_unlock_read_contention-std", |b| gen_lock_unlock_read_contention::<std::sync::Mutex<u32>>(b));
+}
+
+fn lock_unlock_write_contention(b: &mut Criterion) {
+ b.bench_function("lock_unlock_write_contention-spin-spinmutex", |b| gen_lock_unlock_write_contention::<spin::mutex::SpinMutex<u32>>(b));
+ b.bench_function("lock_unlock_write_contention-spin-ticketmutex", |b| gen_lock_unlock_write_contention::<spin::mutex::TicketMutex<u32>>(b));
+ b.bench_function("lock_unlock_write_contention-std", |b| gen_lock_unlock_write_contention::<std::sync::Mutex<u32>>(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<T: ?Sized + Send, R> Sync for FairMutex<T, R> {}
unsafe impl<T: ?Sized + Send, R> Send for FairMutex<T, R> {}
+unsafe impl<T: ?Sized + Sync> Sync for FairMutexGuard<'_, T> {}
+unsafe impl<T: ?Sized + Send> Send for FairMutexGuard<'_, T> {}
+
impl<T, R> FairMutex<T, R> {
/// 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<T: ?Sized + Send, R> Sync for SpinMutex<T, R> {}
unsafe impl<T: ?Sized + Send, R> Send for SpinMutex<T, R> {}
+unsafe impl<T: ?Sized + Sync> Sync for SpinMutexGuard<'_, T> {}
+unsafe impl<T: ?Sized + Send> Send for SpinMutexGuard<'_, T> {}
+
impl<T, R> SpinMutex<T, R> {
/// 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<T, R> Once<T, R> {
}
}
+ /// 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<T, R> Once<T, R> {
}
}
+ /// 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<T: ?Sized + Send, R> Send for RwLock<T, R> {}
unsafe impl<T: ?Sized + Send + Sync, R> Sync for RwLock<T, R> {}
+unsafe impl<T: ?Sized + Send + Sync, R> Send for RwLockWriteGuard<'_, T, R> {}
+unsafe impl<T: ?Sized + Send + Sync, R> Sync for RwLockWriteGuard<'_, T, R> {}
+
+unsafe impl<T: ?Sized + Sync> Send for RwLockReadGuard<'_, T> {}
+unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {}
+
+unsafe impl<T: ?Sized + Send + Sync, R> Send for RwLockUpgradableGuard<'_, T, R> {}
+unsafe impl<T: ?Sized + Send + Sync, R> Sync for RwLockUpgradableGuard<'_, T, R> {}
+
impl<T, R> RwLock<T, R> {
/// Creates a new spinlock wrapping the supplied data.
///