Skip to content

Commit

Permalink
Merge #135
Browse files Browse the repository at this point in the history
135: Release 1.6 r=matklad a=matklad



Co-authored-by: Aleksey Kladov <[email protected]>
  • Loading branch information
bors[bot] and matklad authored Feb 22, 2021
2 parents 6e30f21 + fb50ede commit 1d2ddbb
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 22 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 1.6.0

- Stabilize `once_cell::race` module for "first one wins" no_std-compatible initialization flavor.
- Migrate from deprecated `compare_and_swap` to `compare_exchange`.

## 1.5.2

- `OnceBox` API uses `Box<T>`.
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ regex = "1.2.0"
default = ["std"]
# Enables `once_cell::sync` module.
std = ["alloc"]
# Enables `once_cell::race::OnceBox` type.
alloc = []
# Enables semver-exempt APIs of this crate
# Enables semver-exempt APIs of this crate.
# At the moment, this feature is unused.
unstable = []

[[example]]
Expand Down
14 changes: 10 additions & 4 deletions src/imp_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,13 @@ fn initialize_inner(my_state_and_queue: &AtomicUsize, init: &mut dyn FnMut() ->
match state_and_queue {
COMPLETE => return true,
INCOMPLETE => {
let old = my_state_and_queue.compare_and_swap(
let exchange = my_state_and_queue.compare_exchange(
state_and_queue,
RUNNING,
Ordering::Acquire,
Ordering::Acquire,
);
if old != state_and_queue {
if let Err(old) = exchange {
state_and_queue = old;
continue;
}
Expand Down Expand Up @@ -193,8 +194,13 @@ fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) {
};
let me = &node as *const Waiter as usize;

let old = state_and_queue.compare_and_swap(current_state, me | RUNNING, Ordering::Release);
if old != current_state {
let exchange = state_and_queue.compare_exchange(
current_state,
me | RUNNING,
Ordering::Release,
Ordering::Relaxed,
);
if let Err(old) = exchange {
current_state = old;
continue;
}
Expand Down
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,6 @@
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "unstable")]
#[cfg(feature = "alloc")]
extern crate alloc;

Expand Down Expand Up @@ -1044,5 +1043,4 @@ pub mod sync {
fn _dummy() {}
}

#[cfg(feature = "unstable")]
pub mod race;
53 changes: 38 additions & 15 deletions src/race.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ pub struct OnceNonZeroUsize {

impl OnceNonZeroUsize {
/// Creates a new empty cell.
#[inline]
pub const fn new() -> OnceNonZeroUsize {
OnceNonZeroUsize { inner: AtomicUsize::new(0) }
}

/// Gets the underlying value.
#[inline]
pub fn get(&self) -> Option<NonZeroUsize> {
let val = self.inner.load(Ordering::Acquire);
NonZeroUsize::new(val)
Expand All @@ -33,12 +35,13 @@ impl OnceNonZeroUsize {
///
/// Returns `Ok(())` if the cell was empty and `Err(())` if it was
/// full.
#[inline]
pub fn set(&self, value: NonZeroUsize) -> Result<(), ()> {
let val = self.inner.compare_and_swap(0, value.get(), Ordering::AcqRel);
if val == 0 {
Ok(())
} else {
Err(())
let exchange =
self.inner.compare_exchange(0, value.get(), Ordering::AcqRel, Ordering::Acquire);
match exchange {
Ok(_) => Ok(()),
Err(_) => Err(()),
}
}

Expand Down Expand Up @@ -75,9 +78,10 @@ impl OnceNonZeroUsize {
Some(it) => it,
None => {
let mut val = f()?.get();
let old_val = self.inner.compare_and_swap(0, val, Ordering::AcqRel);
if old_val != 0 {
val = old_val;
let exchange =
self.inner.compare_exchange(0, val, Ordering::AcqRel, Ordering::Acquire);
if let Err(old) = exchange {
val = old;
}
unsafe { NonZeroUsize::new_unchecked(val) }
}
Expand All @@ -94,11 +98,13 @@ pub struct OnceBool {

impl OnceBool {
/// Creates a new empty cell.
#[inline]
pub const fn new() -> OnceBool {
OnceBool { inner: OnceNonZeroUsize::new() }
}

/// Gets the underlying value.
#[inline]
pub fn get(&self) -> Option<bool> {
self.inner.get().map(OnceBool::from_usize)
}
Expand All @@ -107,6 +113,7 @@ impl OnceBool {
///
/// Returns `Ok(())` if the cell was empty and `Err(())` if it was
/// full.
#[inline]
pub fn set(&self, value: bool) -> Result<(), ()> {
self.inner.set(OnceBool::to_usize(value))
}
Expand Down Expand Up @@ -138,25 +145,29 @@ impl OnceBool {
self.inner.get_or_try_init(|| f().map(OnceBool::to_usize)).map(OnceBool::from_usize)
}

#[inline]
fn from_usize(value: NonZeroUsize) -> bool {
value.get() == 1
}
#[inline]
fn to_usize(value: bool) -> NonZeroUsize {
unsafe { NonZeroUsize::new_unchecked(if value { 1 } else { 2 }) }
}
}

#[cfg(feature = "alloc")]
pub use self::once_box::OnceBox;

#[cfg(feature = "alloc")]
mod once_box {
use alloc::boxed::Box;
use core::{
marker::PhantomData,
ptr,
sync::atomic::{AtomicPtr, Ordering},
};

use alloc::boxed::Box;

/// A thread-safe cell which can be written to only once.
#[derive(Default, Debug)]
pub struct OnceBox<T> {
Expand Down Expand Up @@ -194,8 +205,13 @@ mod once_box {
/// full.
pub fn set(&self, value: Box<T>) -> Result<(), Box<T>> {
let ptr = Box::into_raw(value);
let old_ptr = self.inner.compare_and_swap(ptr::null_mut(), ptr, Ordering::AcqRel);
if !old_ptr.is_null() {
let exchange = self.inner.compare_exchange(
ptr::null_mut(),
ptr,
Ordering::AcqRel,
Ordering::Acquire,
);
if let Err(_) = exchange {
let value = unsafe { Box::from_raw(ptr) };
return Err(value);
}
Expand Down Expand Up @@ -235,22 +251,29 @@ mod once_box {
if ptr.is_null() {
let val = f()?;
ptr = Box::into_raw(val);
let old_ptr = self.inner.compare_and_swap(ptr::null_mut(), ptr, Ordering::AcqRel);
if !old_ptr.is_null() {
let exchange = self.inner.compare_exchange(
ptr::null_mut(),
ptr,
Ordering::AcqRel,
Ordering::Acquire,
);
if let Err(old) = exchange {
drop(unsafe { Box::from_raw(ptr) });
ptr = old_ptr;
ptr = old;
}
};
Ok(unsafe { &*ptr })
}
}

unsafe impl<T: Sync + Send> Sync for OnceBox<T> {}

/// ```compile_fail
/// struct S(*mut ());
/// unsafe impl Sync for S {}
///
/// fn share<T: Sync>(_: &T) {}
/// share(&once_cell::race::OnceBox::<S>::new());
/// ```
unsafe impl<T: Sync + Send> Sync for OnceBox<T> {}
fn _dummy() {}
}

0 comments on commit 1d2ddbb

Please sign in to comment.