Skip to content

Commit e797da0

Browse files
committed
feat(UnsafeCell): add as_mut to mutably access owned or mutably borrowed UnsafeCell safely.
1 parent 850faca commit e797da0

File tree

3 files changed

+55
-2
lines changed

3 files changed

+55
-2
lines changed

src/cell/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ mod cell;
55
mod unsafe_cell;
66

77
pub use self::cell::Cell;
8-
pub use self::unsafe_cell::{ConstPtr, MutPtr, UnsafeCell};
8+
pub use self::unsafe_cell::{ConstPtr, MutPtr, MutRef, UnsafeCell};

src/cell/unsafe_cell.rs

+45-1
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,26 @@ pub struct ConstPtr<T: ?Sized> {
9696
/// [here]: #correct-usage
9797
#[derive(Debug)]
9898
pub struct MutPtr<T: ?Sized> {
99-
/// Drop guard representing the lifetime of the `ConstPtr`'s access.
99+
/// Drop guard representing the lifetime of the `MutPtr`'s access.
100100
_guard: rt::cell::Writing,
101101
ptr: *mut T,
102102
}
103103

104+
/// A checked mutable reference to an [`UnsafeCell`].
105+
///
106+
/// This type is essentially a [`&mut T`], but with the added ability to
107+
/// participate in Loom's [`UnsafeCell`] access tracking. While a `MutRef` to a
108+
/// given [`UnsafeCell`] exists, Loom will track that the [`UnsafeCell`] is
109+
/// being accessed mutably.
110+
///
111+
/// [`MutRef`]s are produced by [`UnsafeCell::as_mut`].
112+
#[derive(Debug)]
113+
pub struct MutRef<'a, T: ?Sized> {
114+
/// Drop guard representing the lifetime of the `MutRef`'s access.
115+
_guard: rt::cell::Writing,
116+
ptr: &'a mut T,
117+
}
118+
104119
impl<T> UnsafeCell<T> {
105120
/// Constructs a new instance of `UnsafeCell` which will wrap the specified value.
106121
#[track_caller]
@@ -200,6 +215,21 @@ impl<T: ?Sized> UnsafeCell<T> {
200215
ptr: self.data.get(),
201216
}
202217
}
218+
219+
/// Get a mutable reference to the wrapped value.
220+
///
221+
/// This function returns a [`MutRef`] guard, which is analogous to a
222+
/// `&mut T`, but tracked by Loom. As long as the returned `MutRef`
223+
/// exists, Loom will consider the cell to be accessed mutably.
224+
///
225+
/// This is analogous to [`core::cell::UnsafeCell::get_mut`].
226+
#[track_caller]
227+
pub fn as_mut(&mut self) -> MutRef<'_, T> {
228+
MutRef {
229+
_guard: self.state.start_write(location!()),
230+
ptr: self.data.get_mut(),
231+
}
232+
}
203233
}
204234

205235
impl<T: Default> Default for UnsafeCell<T> {
@@ -382,6 +412,20 @@ impl<T: ?Sized> ConstPtr<T> {
382412
}
383413
}
384414

415+
impl<T: ?Sized> core::ops::Deref for MutRef<'_, T> {
416+
type Target = T;
417+
418+
fn deref(&self) -> &Self::Target {
419+
self.ptr
420+
}
421+
}
422+
423+
impl<T: ?Sized> core::ops::DerefMut for MutRef<'_, T> {
424+
fn deref_mut(&mut self) -> &mut Self::Target {
425+
self.ptr
426+
}
427+
}
428+
385429
impl<T: ?Sized> MutPtr<T> {
386430
/// Dereference the raw pointer.
387431
///

tests/unsafe_cell.rs

+9
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,12 @@ fn unsafe_cell_access_after_sync() {
333333
}
334334
});
335335
}
336+
337+
#[test]
338+
fn as_mut_is_ergonomical_and_safe() {
339+
loom::model(|| {
340+
let mut value = UnsafeCell::new(-3i32);
341+
let mut mut_ref = value.as_mut();
342+
*mut_ref = mut_ref.abs();
343+
});
344+
}

0 commit comments

Comments
 (0)