Skip to content

Commit c0f53bf

Browse files
committed
Use AtomicPtr
1 parent caebb56 commit c0f53bf

File tree

3 files changed

+60
-63
lines changed

3 files changed

+60
-63
lines changed

src/backends/linux_android_with_fallback.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@ type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint)
1313

1414
/// Sentinel value which indicates that `libc::getrandom` either not available,
1515
/// or not supported by kernel.
16-
const NOT_AVAILABLE: usize = usize::MAX;
16+
const NOT_AVAILABLE: *mut c_void = usize::MAX as *mut c_void;
1717

1818
#[cold]
1919
#[inline(never)]
20-
fn init() -> usize {
20+
fn init() -> *mut c_void {
2121
// Use static linking to `libc::getrandom` on MUSL targets and `dlsym` everywhere else
2222
#[cfg(not(target_env = "musl"))]
23-
let fptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"getrandom".as_ptr()) } as usize;
23+
let fptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"getrandom".as_ptr()) };
2424
#[cfg(target_env = "musl")]
2525
let fptr = {
2626
let fptr: GetRandomFn = libc::getrandom;
27-
unsafe { transmute::<GetRandomFn, usize>(fptr) }
27+
unsafe { transmute::<GetRandomFn, *mut c_void>(fptr) }
2828
};
2929

30-
let res_ptr = if fptr != 0 {
31-
let getrandom_fn = unsafe { transmute::<usize, GetRandomFn>(fptr) };
30+
let res_ptr = if !fptr.is_null() {
31+
let getrandom_fn = unsafe { transmute::<*mut c_void, GetRandomFn>(fptr) };
3232
// Check that `getrandom` syscall is supported by kernel
3333
let res = unsafe { getrandom_fn(ptr::dangling_mut(), 0, 0) };
3434
if cfg!(getrandom_test_linux_fallback) {
@@ -66,14 +66,14 @@ fn use_file_fallback(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
6666

6767
#[inline]
6868
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
69-
static GETRANDOM_FN: lazy::LazyUsize = lazy::LazyUsize::new();
69+
static GETRANDOM_FN: lazy::LazyPtr<c_void> = lazy::LazyPtr::new();
7070
let fptr = GETRANDOM_FN.unsync_init(init);
7171

7272
if fptr == NOT_AVAILABLE {
7373
use_file_fallback(dest)
7474
} else {
7575
// note: `transmute` is currently the only way to convert a pointer into a function reference
76-
let getrandom_fn = unsafe { transmute::<usize, GetRandomFn>(fptr) };
76+
let getrandom_fn = unsafe { transmute::<*mut c_void, GetRandomFn>(fptr) };
7777
util_libc::sys_fill_exact(dest, |buf| unsafe {
7878
let ret = getrandom_fn(buf.as_mut_ptr().cast(), buf.len(), 0);
7979
sanitizer::unpoison_linux_getrandom_result(buf, ret);

src/backends/netbsd.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,23 @@ type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint)
4040

4141
#[cold]
4242
#[inline(never)]
43-
fn init() -> usize {
44-
let mut ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"getrandom".as_ptr()) };
43+
fn init() -> *mut c_void {
44+
let ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"getrandom".as_ptr()) };
4545
if ptr.is_null() || cfg!(getrandom_test_netbsd_fallback) {
4646
// Verify `polyfill_using_kern_arand` has the right signature.
4747
const POLYFILL: GetRandomFn = polyfill_using_kern_arand;
48-
ptr = POLYFILL as *mut c_void;
48+
POLYFILL as *mut c_void
49+
} else {
50+
ptr
4951
}
50-
ptr as usize
5152
}
5253

5354
#[inline]
5455
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
55-
static GETRANDOM_FN: lazy::LazyUsize = lazy::LazyUsize::new();
56+
static GETRANDOM_FN: lazy::LazyPtr<c_void> = lazy::LazyPtr::new();
5657

5758
let fptr = GETRANDOM_FN.unsync_init(init);
58-
let fptr = unsafe { mem::transmute::<usize, GetRandomFn>(fptr) };
59+
let fptr = unsafe { mem::transmute::<*mut c_void, GetRandomFn>(fptr) };
5960
util_libc::sys_fill_exact(dest, |buf| unsafe {
6061
fptr(buf.as_mut_ptr().cast::<c_void>(), buf.len(), 0)
6162
})

src/lazy.rs

Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,52 @@
11
//! Helpers built around pointer-sized atomics.
2-
use core::sync::atomic::{AtomicUsize, Ordering};
3-
4-
// This structure represents a lazily initialized static usize value. Useful
5-
// when it is preferable to just rerun initialization instead of locking.
6-
// unsync_init will invoke an init() function until it succeeds, then return the
7-
// cached value for future calls.
8-
//
9-
// unsync_init supports init() "failing". If the init() method returns UNINIT,
10-
// that value will be returned as normal, but will not be cached.
11-
//
12-
// Users should only depend on the _value_ returned by init() functions.
13-
// Specifically, for the following init() function:
14-
// fn init() -> usize {
15-
// a();
16-
// let v = b();
17-
// c();
18-
// v
19-
// }
20-
// the effects of c() or writes to shared memory will not necessarily be
21-
// observed and additional synchronization methods may be needed.
22-
pub(crate) struct LazyUsize(AtomicUsize);
23-
24-
impl LazyUsize {
25-
// The initialization is not completed.
26-
const UNINIT: usize = usize::MAX;
27-
28-
pub const fn new() -> Self {
29-
Self(AtomicUsize::new(Self::UNINIT))
30-
}
31-
32-
// Runs the init() function at most once, returning the value of some run of
33-
// init(). Multiple callers can run their init() functions in parallel.
34-
// init() should always return the same value, if it succeeds.
35-
pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize {
36-
#[cold]
37-
fn do_init(this: &LazyUsize, init: impl FnOnce() -> usize) -> usize {
38-
let val = init();
39-
this.0.store(val, Ordering::Relaxed);
40-
val
41-
}
42-
43-
// Relaxed ordering is fine, as we only have a single atomic variable.
44-
let val = self.0.load(Ordering::Relaxed);
45-
if val != Self::UNINIT {
46-
val
47-
} else {
48-
do_init(self, init)
2+
use core::{
3+
ptr,
4+
sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
5+
};
6+
7+
macro_rules! lazy_atomic {
8+
($name:ident $(<$($gen:ident),+>)?, $atomic:ty, $value:ty, $uninit:expr) => {
9+
/// Lazily initialized static value backed by a single atomic.
10+
///
11+
/// `unsync_init` will invoke `init` until it returns a value other than
12+
/// the sentinel `UNINIT`, then cache that value for subsequent calls.
13+
/// Multiple callers may race to run `init`; only the returned value is
14+
/// guaranteed to be observed, not any side effects.
15+
pub(crate) struct $name$(<$($gen),+>)?($atomic);
16+
17+
impl<$($($gen),+)? > $name$(<$($gen),+>)? {
18+
const UNINIT: $value = $uninit;
19+
20+
pub const fn new() -> Self {
21+
Self(<$atomic>::new(Self::UNINIT))
22+
}
23+
24+
#[cold]
25+
fn do_init(&self, init: impl FnOnce() -> $value) -> $value {
26+
let val = init();
27+
self.0.store(val, Ordering::Relaxed);
28+
val
29+
}
30+
31+
#[inline]
32+
pub fn unsync_init(&self, init: impl FnOnce() -> $value) -> $value {
33+
// Relaxed ordering is fine, as we only have a single atomic variable.
34+
let val = self.0.load(Ordering::Relaxed);
35+
if val != Self::UNINIT {
36+
val
37+
} else {
38+
self.do_init(init)
39+
}
40+
}
4941
}
50-
}
42+
};
5143
}
5244

53-
// Identical to LazyUsize except with bool instead of usize.
45+
lazy_atomic!(LazyUsize, AtomicUsize, usize, usize::MAX);
46+
lazy_atomic!(LazyPtr<T>, AtomicPtr<T>, *mut T, ptr::dangling_mut());
47+
48+
/// Lazily initializes a cached bool; reuses `LazyUsize` to avoid sentinel
49+
/// issues with `AtomicBool`.
5450
pub(crate) struct LazyBool(LazyUsize);
5551

5652
impl LazyBool {

0 commit comments

Comments
 (0)