Skip to content

Commit

Permalink
Rename to LazyPtr and move to the lazy module
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov committed May 21, 2024
1 parent cbd5142 commit 105ce26
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 70 deletions.
63 changes: 0 additions & 63 deletions src/cached_ptr.rs

This file was deleted.

66 changes: 63 additions & 3 deletions src/lazy.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(dead_code)]
use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};

// This structure represents a lazily initialized static usize value. Useful
Expand All @@ -21,13 +22,13 @@ use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
pub(crate) struct LazyUsize(AtomicUsize);

impl LazyUsize {
// The initialization is not completed.
const UNINIT: usize = usize::max_value();

pub const fn new() -> Self {
Self(AtomicUsize::new(Self::UNINIT))
}

// The initialization is not completed.
pub const UNINIT: usize = usize::max_value();

// Runs the init() function at most once, returning the value of some run of
// init(). Multiple callers can run their init() functions in parallel.
// init() should always return the same value, if it succeeds.
Expand All @@ -54,3 +55,62 @@ impl LazyBool {
self.0.unsync_init(|| init() as usize) != 0
}
}

use core::{
ffi::c_void,
sync::atomic::{fence, AtomicPtr, Ordering},
};

// This structure represents a lazily initialized static pointer value.
///
/// It's intended to be used for weak linking of a C function that may
/// or may not be present at runtime.
///
/// Based off of the DlsymWeak struct in libstd:
/// https://github.com/rust-lang/rust/blob/1.61.0/library/std/src/sys/unix/weak.rs#L84
/// except that the caller must manually cast self.ptr() to a function pointer.
pub struct LazyPtr {
addr: AtomicPtr<c_void>,
}

impl LazyPtr {
/// A non-null pointer value which indicates we are uninitialized.
///
/// This constant should ideally not be a valid pointer.
/// However, if by chance initialization function passed to the `get`
/// method does return UNINIT, there will not be undefined behavior.
/// The initialization function will just be called each time `get()`
/// is called. This would be inefficient, but correct.
const UNINIT: *mut c_void = !0usize as *mut c_void;

/// Construct new `LazyPtr` in uninitialized state.
pub const fn new() -> Self {
Self {
addr: AtomicPtr::new(Self::UNINIT),
}
}

// Runs the init() function at most once, returning the value of some run of
// init(). Multiple callers can run their init() functions in parallel.
// init() should always return the same value, if it succeeds.
pub fn unsync_init(&self, f: impl Fn() -> *mut c_void) -> *mut c_void {
// Despite having only a single atomic variable (self.addr), we still
// cannot always use Ordering::Relaxed, as we need to make sure a
// successful call to `f` is "ordered before" any data read through
// the returned pointer (which occurs when the function is called).
// Our implementation mirrors that of the one in libstd, meaning that
// the use of non-Relaxed operations is probably unnecessary.
match self.addr.load(Ordering::Relaxed) {
Self::UNINIT => {
let addr = f();
// Synchronizes with the Acquire fence below
self.addr.store(addr, Ordering::Release);
addr
}
addr => {
fence(Ordering::Acquire);
addr
}
}
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ cfg_if! {
#[path = "solaris.rs"] mod imp;
} else if #[cfg(target_os = "netbsd")] {
mod util_libc;
mod cached_ptr;
mod lazy;
#[path = "netbsd.rs"] mod imp;
} else if #[cfg(target_os = "fuchsia")] {
#[path = "fuchsia.rs"] mod imp;
Expand Down
7 changes: 4 additions & 3 deletions src/netbsd.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Implementation for NetBSD
use crate::{cached_ptr::CachedPtr, util_libc::sys_fill_exact, Error};
use crate::{lazy::LazyPtr, util_libc::sys_fill_exact, Error};
use core::{ffi::c_void, mem::MaybeUninit, ptr};

fn kern_arnd(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
Expand All @@ -25,7 +25,7 @@ fn kern_arnd(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;

// getrandom(2) was introduced in NetBSD 10.0
static GETRANDOM: CachedPtr = CachedPtr::new();
static GETRANDOM: LazyPtr = LazyPtr::new();

fn dlsym_getrandom() -> *mut c_void {
static NAME: &[u8] = b"getrandom\0";
Expand All @@ -34,7 +34,8 @@ fn dlsym_getrandom() -> *mut c_void {
}

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
if let Some(fptr) = GETRANDOM.get(dlsym_getrandom) {
let fptr = GETRANDOM.unsync_init(dlsym_getrandom);
if !fptr.is_null() {
let func: GetRandomFn = unsafe { core::mem::transmute(fptr) };
return sys_fill_exact(dest, |buf| unsafe {
func(buf.as_mut_ptr() as *mut u8, buf.len(), 0)
Expand Down

0 comments on commit 105ce26

Please sign in to comment.