Skip to content

Commit 78ab172

Browse files
bors[bot]Imberflur
andauthored
Merge #219
219: Use AtomicPtr for race::OnceRef to avoid ptr-int-ptr r=matklad a=Imberflur Same rationale as #185 Potentially, a `OncePtr` abstraction could be factored out and used in both `OnceBox` and `OnceRef`. Co-authored-by: Imbris <imbrisf@gmail.com>
2 parents d706539 + 6e351e5 commit 78ab172

File tree

1 file changed

+35
-12
lines changed

1 file changed

+35
-12
lines changed

src/race.rs

+35-12
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ use atomic_polyfill as atomic;
2424
#[cfg(not(feature = "critical-section"))]
2525
use core::sync::atomic;
2626

27-
use atomic::{AtomicUsize, Ordering};
27+
use atomic::{AtomicPtr, AtomicUsize, Ordering};
2828
use core::cell::UnsafeCell;
2929
use core::marker::PhantomData;
3030
use core::num::NonZeroUsize;
31+
use core::ptr;
3132

3233
/// A thread-safe cell which can be written to only once.
3334
#[derive(Default, Debug)]
@@ -176,7 +177,7 @@ impl OnceBool {
176177

177178
/// A thread-safe cell which can be written to only once.
178179
pub struct OnceRef<'a, T> {
179-
inner: OnceNonZeroUsize,
180+
inner: AtomicPtr<T>,
180181
ghost: PhantomData<UnsafeCell<&'a T>>,
181182
}
182183

@@ -198,21 +199,27 @@ impl<'a, T> Default for OnceRef<'a, T> {
198199
impl<'a, T> OnceRef<'a, T> {
199200
/// Creates a new empty cell.
200201
pub const fn new() -> OnceRef<'a, T> {
201-
OnceRef { inner: OnceNonZeroUsize::new(), ghost: PhantomData }
202+
OnceRef { inner: AtomicPtr::new(ptr::null_mut()), ghost: PhantomData }
202203
}
203204

204205
/// Gets a reference to the underlying value.
205206
pub fn get(&self) -> Option<&'a T> {
206-
self.inner.get().map(|ptr| unsafe { &*(ptr.get() as *const T) })
207+
let ptr = self.inner.load(Ordering::Acquire);
208+
unsafe { ptr.as_ref() }
207209
}
208210

209211
/// Sets the contents of this cell to `value`.
210212
///
211213
/// Returns `Ok(())` if the cell was empty and `Err(value)` if it was
212214
/// full.
213215
pub fn set(&self, value: &'a T) -> Result<(), ()> {
214-
let ptr = NonZeroUsize::new(value as *const T as usize).unwrap();
215-
self.inner.set(ptr)
216+
let ptr = value as *const T as *mut T;
217+
let exchange =
218+
self.inner.compare_exchange(ptr::null_mut(), ptr, Ordering::AcqRel, Ordering::Acquire);
219+
match exchange {
220+
Ok(_) => Ok(()),
221+
Err(_) => Err(()),
222+
}
216223
}
217224

218225
/// Gets the contents of the cell, initializing it with `f` if the cell was
@@ -225,9 +232,11 @@ impl<'a, T> OnceRef<'a, T> {
225232
where
226233
F: FnOnce() -> &'a T,
227234
{
228-
let f = || NonZeroUsize::new(f() as *const T as usize).unwrap();
229-
let ptr = self.inner.get_or_init(f);
230-
unsafe { &*(ptr.get() as *const T) }
235+
enum Void {}
236+
match self.get_or_try_init(|| Ok::<&'a T, Void>(f())) {
237+
Ok(val) => val,
238+
Err(void) => match void {},
239+
}
231240
}
232241

233242
/// Gets the contents of the cell, initializing it with `f` if
@@ -241,9 +250,23 @@ impl<'a, T> OnceRef<'a, T> {
241250
where
242251
F: FnOnce() -> Result<&'a T, E>,
243252
{
244-
let f = || f().map(|value| NonZeroUsize::new(value as *const T as usize).unwrap());
245-
let ptr = self.inner.get_or_try_init(f)?;
246-
unsafe { Ok(&*(ptr.get() as *const T)) }
253+
let mut ptr = self.inner.load(Ordering::Acquire);
254+
255+
if ptr.is_null() {
256+
// TODO replace with `cast_mut` when MSRV reaches 1.65.0 (also in `set`)
257+
ptr = f()? as *const T as *mut T;
258+
let exchange = self.inner.compare_exchange(
259+
ptr::null_mut(),
260+
ptr,
261+
Ordering::AcqRel,
262+
Ordering::Acquire,
263+
);
264+
if let Err(old) = exchange {
265+
ptr = old;
266+
}
267+
}
268+
269+
Ok(unsafe { &*ptr })
247270
}
248271

249272
/// ```compile_fail

0 commit comments

Comments
 (0)