Skip to content

Commit d529514

Browse files
committed
Make OnceCell pass drock
This fixes a regression from 1.3.1 to 1.4.0. See rust-lang/rust#75555 (comment) and https://doc.rust-lang.org/nomicon/dropck.html Arrrrrrrrrrrrrrrrrrrrrr!
1 parent b340c76 commit d529514

File tree

2 files changed

+39
-47
lines changed

2 files changed

+39
-47
lines changed

src/imp_pl.rs

Lines changed: 19 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use std::{
22
cell::UnsafeCell,
3-
mem::{self, MaybeUninit},
3+
hint,
44
panic::{RefUnwindSafe, UnwindSafe},
5-
ptr,
65
sync::atomic::{AtomicBool, Ordering},
76
};
87

@@ -11,7 +10,7 @@ use parking_lot::Mutex;
1110
pub(crate) struct OnceCell<T> {
1211
mutex: Mutex<()>,
1312
is_initialized: AtomicBool,
14-
value: UnsafeCell<MaybeUninit<T>>,
13+
value: UnsafeCell<Option<T>>,
1514
}
1615

1716
// Why do we need `T: Send`?
@@ -30,7 +29,7 @@ impl<T> OnceCell<T> {
3029
OnceCell {
3130
mutex: parking_lot::const_mutex(()),
3231
is_initialized: AtomicBool::new(false),
33-
value: UnsafeCell::new(MaybeUninit::uninit()),
32+
value: UnsafeCell::new(None),
3433
}
3534
}
3635

@@ -60,7 +59,9 @@ impl<T> OnceCell<T> {
6059
let value = f()?;
6160
// Safe b/c we have a unique access and no panic may happen
6261
// until the cell is marked as initialized.
63-
unsafe { self.as_mut_ptr().write(value) };
62+
let slot: &mut Option<T> = unsafe { &mut *self.value.get() };
63+
debug_assert!(slot.is_none());
64+
*slot = Some(value);
6465
self.is_initialized.store(true, Ordering::Release);
6566
}
6667
Ok(())
@@ -75,58 +76,29 @@ impl<T> OnceCell<T> {
7576
/// the contents are acquired by (synchronized to) this thread.
7677
pub(crate) unsafe fn get_unchecked(&self) -> &T {
7778
debug_assert!(self.is_initialized());
78-
&*self.as_ptr()
79+
let slot: &Option<T> = &*self.value.get();
80+
match slot {
81+
Some(value) => value,
82+
// This unsafe does improve performance, see `examples/bench`.
83+
None => {
84+
debug_assert!(false);
85+
hint::unreachable_unchecked()
86+
}
87+
}
7988
}
8089

8190
/// Gets the mutable reference to the underlying value.
8291
/// Returns `None` if the cell is empty.
8392
pub(crate) fn get_mut(&mut self) -> Option<&mut T> {
84-
if self.is_initialized() {
85-
// Safe b/c we have a unique access and value is initialized.
86-
Some(unsafe { &mut *self.as_mut_ptr() })
87-
} else {
88-
None
89-
}
93+
// Safe b/c we have an exclusive access
94+
let slot: &mut Option<T> = unsafe { &mut *self.value.get() };
95+
slot.as_mut()
9096
}
9197

9298
/// Consumes this `OnceCell`, returning the wrapped value.
9399
/// Returns `None` if the cell was empty.
94100
pub(crate) fn into_inner(self) -> Option<T> {
95-
if !self.is_initialized() {
96-
return None;
97-
}
98-
99-
// Safe b/c we have a unique access and value is initialized.
100-
let value: T = unsafe { ptr::read(self.as_ptr()) };
101-
102-
// It's OK to `mem::forget` without dropping, because both `self.mutex`
103-
// and `self.is_initialized` are not heap-allocated.
104-
mem::forget(self);
105-
106-
Some(value)
107-
}
108-
109-
fn as_ptr(&self) -> *const T {
110-
unsafe {
111-
let slot: &MaybeUninit<T> = &*self.value.get();
112-
slot.as_ptr()
113-
}
114-
}
115-
116-
fn as_mut_ptr(&self) -> *mut T {
117-
unsafe {
118-
let slot: &mut MaybeUninit<T> = &mut *self.value.get();
119-
slot.as_mut_ptr()
120-
}
121-
}
122-
}
123-
124-
impl<T> Drop for OnceCell<T> {
125-
fn drop(&mut self) {
126-
if self.is_initialized() {
127-
// Safe b/c we have a unique access and value is initialized.
128-
unsafe { ptr::drop_in_place(self.as_mut_ptr()) };
129-
}
101+
self.value.into_inner()
130102
}
131103
}
132104

tests/test.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,16 @@ mod unsync {
186186
});
187187
eprintln!("use after free: {:?}", dangling_ref.get().unwrap());
188188
}
189+
190+
#[test]
191+
// https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669
192+
fn arrrrrrrrrrrrrrrrrrrrrr() {
193+
let cell = OnceCell::new();
194+
{
195+
let s = String::new();
196+
cell.set(&s).unwrap();
197+
}
198+
}
189199
}
190200

191201
#[cfg(feature = "std")]
@@ -572,4 +582,14 @@ mod sync {
572582
.unwrap();
573583
assert_eq!(cell.get(), Some(&"hello".to_string()));
574584
}
585+
586+
#[test]
587+
// https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669
588+
fn arrrrrrrrrrrrrrrrrrrrrr() {
589+
let cell = OnceCell::new();
590+
{
591+
let s = String::new();
592+
cell.set(&s).unwrap();
593+
}
594+
}
575595
}

0 commit comments

Comments
 (0)