Skip to content

Commit 123406c

Browse files
authored
Rollup merge of rust-lang#66705 - pitdicker:atomic_mut_ptr, r=KodrAus
Atomic as_mut_ptr I encountered the following pattern a few times: In Rust we use some atomic type like `AtomicI32`, and an FFI interface exposes this as `*mut i32` (or some similar `libc` type). It was not obvious to me if a just transmuting a pointer to the atomic was acceptable, or if this should use a cast that goes through an `UnsafeCell`. See rust-lang#66136 (comment) Transmuting the pointer directly: ```rust let atomic = AtomicI32::new(1); let ptr = &atomic as *const AtomicI32 as *mut i32; unsafe { ffi(ptr); } ``` A dance with `UnsafeCell`: ```rust let atomic = AtomicI32::new(1); unsafe { let ptr = (&*(&atomic as *const AtomicI32 as *const UnsafeCell<i32>)).get(); ffi(ptr); } ``` Maybe in the end both ways could be valid. But why not expose a direct method to get a pointer from the standard library? An `as_mut_ptr` method on atomics can be safe, because only the use of the resulting pointer is where things can get unsafe. I documented its use for FFI, and "Doing non-atomic reads and writes on the resulting integer can be a data race." The standard library could make use this method in a few places in the WASM module. cc @RalfJung as you answered my original question.
2 parents 3af14f9 + d34090a commit 123406c

File tree

5 files changed

+80
-5
lines changed

5 files changed

+80
-5
lines changed

src/libcore/sync/atomic.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,43 @@ impl AtomicBool {
802802
pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool {
803803
unsafe { atomic_xor(self.v.get(), val as u8, order) != 0 }
804804
}
805+
806+
/// Returns a mutable pointer to the underlying [`bool`].
807+
///
808+
/// Doing non-atomic reads and writes on the resulting integer can be a data race.
809+
/// This method is mostly useful for FFI, where the function signature may use
810+
/// `*mut bool` instead of `&AtomicBool`.
811+
///
812+
/// Returning an `*mut` pointer from a shared reference to this atomic is safe because the
813+
/// atomic types work with interior mutability. All modifications of an atomic change the value
814+
/// through a shared reference, and can do so safely as long as they use atomic operations. Any
815+
/// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same
816+
/// restriction: operations on it must be atomic.
817+
///
818+
/// [`bool`]: ../../../std/primitive.bool.html
819+
///
820+
/// # Examples
821+
///
822+
/// ```ignore (extern-declaration)
823+
/// # fn main() {
824+
/// use std::sync::atomic::AtomicBool;
825+
/// extern {
826+
/// fn my_atomic_op(arg: *mut bool);
827+
/// }
828+
///
829+
/// let mut atomic = AtomicBool::new(true);
830+
/// unsafe {
831+
/// my_atomic_op(atomic.as_mut_ptr());
832+
/// }
833+
/// # }
834+
/// ```
835+
#[inline]
836+
#[unstable(feature = "atomic_mut_ptr",
837+
reason = "recently added",
838+
issue = "66893")]
839+
pub fn as_mut_ptr(&self) -> *mut bool {
840+
self.v.get() as *mut bool
841+
}
805842
}
806843

807844
#[cfg(target_has_atomic_load_store = "ptr")]
@@ -1891,6 +1928,43 @@ assert_eq!(min_foo, 12);
18911928
}
18921929
}
18931930

1931+
doc_comment! {
1932+
concat!("Returns a mutable pointer to the underlying integer.
1933+
1934+
Doing non-atomic reads and writes on the resulting integer can be a data race.
1935+
This method is mostly useful for FFI, where the function signature may use
1936+
`*mut ", stringify!($int_type), "` instead of `&", stringify!($atomic_type), "`.
1937+
1938+
Returning an `*mut` pointer from a shared reference to this atomic is safe because the
1939+
atomic types work with interior mutability. All modifications of an atomic change the value
1940+
through a shared reference, and can do so safely as long as they use atomic operations. Any
1941+
use of the returned raw pointer requires an `unsafe` block and still has to uphold the same
1942+
restriction: operations on it must be atomic.
1943+
1944+
# Examples
1945+
1946+
```ignore (extern-declaration)
1947+
# fn main() {
1948+
", $extra_feature, "use std::sync::atomic::", stringify!($atomic_type), ";
1949+
1950+
extern {
1951+
fn my_atomic_op(arg: *mut ", stringify!($int_type), ");
1952+
}
1953+
1954+
let mut atomic = ", stringify!($atomic_type), "::new(1);
1955+
unsafe {
1956+
my_atomic_op(atomic.as_mut_ptr());
1957+
}
1958+
# }
1959+
```"),
1960+
#[inline]
1961+
#[unstable(feature = "atomic_mut_ptr",
1962+
reason = "recently added",
1963+
issue = "66893")]
1964+
pub fn as_mut_ptr(&self) -> *mut $int_type {
1965+
self.v.get()
1966+
}
1967+
}
18941968
}
18951969
}
18961970
}

src/libstd/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@
234234
#![feature(allocator_internals)]
235235
#![feature(allow_internal_unsafe)]
236236
#![feature(allow_internal_unstable)]
237+
#![feature(atomic_mut_ptr)]
237238
#![feature(arbitrary_self_types)]
238239
#![feature(array_error_internals)]
239240
#![feature(asm)]

src/libstd/sys/wasm/alloc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ mod lock {
6767
//
6868
// unsafe {
6969
// let r = core::arch::wasm32::i32_atomic_wait(
70-
// &LOCKED as *const AtomicI32 as *mut i32,
70+
// LOCKED.as_mut_ptr(),
7171
// 1, // expected value
7272
// -1, // timeout
7373
// );
@@ -143,7 +143,7 @@ mod lock {
143143
//
144144
// unsafe {
145145
// core::arch::wasm32::atomic_notify(
146-
// &LOCKED as *const AtomicI32 as *mut i32,
146+
// LOCKED.as_mut_ptr(),
147147
// 1, // only one thread
148148
// );
149149
// }

src/libstd/sys/wasm/condvar_atomics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,6 @@ impl Condvar {
8989
#[inline]
9090
fn ptr(&self) -> *mut i32 {
9191
assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>());
92-
&self.cnt as *const AtomicUsize as *mut i32
92+
self.cnt.as_mut_ptr() as *mut i32
9393
}
9494
}

src/libstd/sys/wasm/mutex_atomics.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ impl Mutex {
5656
#[inline]
5757
fn ptr(&self) -> *mut i32 {
5858
assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>());
59-
&self.locked as *const AtomicUsize as *mut isize as *mut i32
59+
self.locked.as_mut_ptr() as *mut i32
6060
}
6161
}
6262

@@ -145,6 +145,6 @@ impl ReentrantMutex {
145145

146146
#[inline]
147147
fn ptr(&self) -> *mut i32 {
148-
&self.owner as *const AtomicU32 as *mut i32
148+
self.owner.as_mut_ptr() as *mut i32
149149
}
150150
}

0 commit comments

Comments
 (0)