Skip to content

Commit

Permalink
use_file: std::sync::Mutex, dropping all libpthread use.
Browse files Browse the repository at this point in the history
pthreads mutexes are not safe to move. While it is very unlikely that
the mutex we create will ever be moved, we don't actively do anything
to actively prevent it from being moved. (libstd, when it used/uses
pthreads mutexes, would box them to prevent them from being moved.)

Also, now on Linux and Android (and many other targets for which we
don't use use_std), libstd uses futexes instead of pthreads mutexes.
Thus using libstd's Mutex will be more efficient and avoid adding an
often-otherwise-unnecessary libpthreads dependency on these targets.

  * Linux, Android: Futex [1].
  * Haiku, Redox, NTO, AIX: pthreads [2].
  * others: not using `use_file`.

This will not affect our plans for *-*-linux-none, since we don't
plan to use `use_file` for it.

This breaks 32-bit x86 QNX Neutrino, which doesn't have libstd
because the target itself is abandoned [3]. the other QNX Neutrino
targets didn't get libstd support until Rust 1.69, so this
effectively raises the MSRV for them to 1.69.

I tried to use `Once` to avoid the MSRV increase but it doesn't support
fallible initialization. `OnceLock` wasn't added until 1.70 but even
then, `OnceLock::get_or_try_init` is still unstable.

On x86_64 Linux, this change removes all libpthreads dependencies:

```diff
-       pthread_mutex_lock
-       pthread_mutex_unlock
```

and adds these libstd dependencies:

```diff
+       std::panicking::panic_count::GLOBAL_PANIC_COUNT
+       std::panicking::panic_count::is_zero_slow_path
+       std::sys::sync::mutex::futex::Mutex::lock_contended
+       std::sys::sync::mutex::futex::Mutex::wake
```

as measured using `cargo asm`.

[1] https://github.com/rust-lang/rust/blob/c1dba09f263cbff6170f130aa418e28bdf22bd96/library/std/src/sys/sync/mutex/mod.rs#L4-L10
[2] https://github.com/rust-lang/rust/blob/c1dba09f263cbff6170f130aa418e28bdf22bd96/library/std/src/sys/sync/mutex/mod.rs#L17-L20
[3] rust-random#453 (comment)
  • Loading branch information
briansmith committed Jun 18, 2024
1 parent 7ac1473 commit 2d9f884
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 30 deletions.
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ impl Error {
pub const NODE_ES_MODULE: Error = internal_error(14);
/// Calling Windows ProcessPrng failed.
pub const WINDOWS_PROCESS_PRNG: Error = internal_error(15);
/// The mutex used when opening the random file was poisoned.
pub const UNEXPECTED_FILE_MUTEX_POISONED: Error = internal_error(16);

/// Codes below this point represent OS Errors (i.e. positive i32 values).
/// Codes at or above this point, but below [`Error::CUSTOM_START`] are
Expand Down Expand Up @@ -175,6 +177,7 @@ fn internal_desc(error: Error) -> Option<&'static str> {
Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"),
Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"),
Error::WINDOWS_PROCESS_PRNG => Some("ProcessPrng: Windows system function failure"),
Error::UNEXPECTED_FILE_MUTEX_POISONED => Some("File: Initialization panicked, poisoning the mutex"),
_ => None,
}
}
Expand Down
44 changes: 14 additions & 30 deletions src/use_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ extern crate std;

use crate::{util_libc::sys_fill_exact, Error};
use core::{
cell::UnsafeCell,
ffi::c_void,
mem::MaybeUninit,
sync::atomic::{AtomicI32, Ordering},
Expand All @@ -14,6 +13,7 @@ use std::{
io,
// TODO(MSRV 1.66): use `std::os::fd` instead of `std::unix::io`.
os::unix::io::{AsRawFd as _, BorrowedFd, IntoRawFd as _, RawFd},
sync::{Mutex, PoisonError},
};

/// For all platforms, we use `/dev/urandom` rather than `/dev/random`.
Expand Down Expand Up @@ -55,6 +55,9 @@ fn get_rng_fd() -> Result<BorrowedFd<'static>, Error> {
// process.) `get_fd_locked` stores into FD using `Ordering::Release` to
// ensure any such state is synchronized. `get_fd` loads from `FD` with
// `Ordering::Acquire` to synchronize with it.
//
// TODO(MSRV feature(once_cell_try)): Use `OnceLock::get_or_try_init`
// instead.
static FD: AtomicI32 = AtomicI32::new(FD_UNINIT);

fn get_fd() -> Option<BorrowedFd<'static>> {
Expand All @@ -70,9 +73,16 @@ fn get_rng_fd() -> Result<BorrowedFd<'static>, Error> {
// descriptors concurrently, which could run into the limit on the
// number of open file descriptors. Our goal is to have no more than one
// file descriptor open, ever.
static MUTEX: Mutex = Mutex::new();
unsafe { MUTEX.lock() };
let _guard = DropGuard(|| unsafe { MUTEX.unlock() });
//
// We assume any call to `Mutex::lock` synchronizes-with
// (Ordering::Acquire) the preceding dropping of a `MutexGuard` that
// unlocks the mutex (Ordering::Release) and that `Mutex` doesn't have
// any special treatment for what's "inside" the mutex (the `T` in
// `Mutex<T>`). See `https://github.com/rust-lang/rust/issues/126239.
static MUTEX: Mutex<()> = Mutex::new(());
let _guard = MUTEX
.lock()
.map_err(|_: PoisonError<_>| Error::UNEXPECTED_FILE_MUTEX_POISONED)?;

if let Some(fd) = get_fd() {
return Ok(fd);
Expand Down Expand Up @@ -168,29 +178,3 @@ fn map_io_error(err: io::Error) -> Error {
}
})
}

struct Mutex(UnsafeCell<libc::pthread_mutex_t>);

impl Mutex {
const fn new() -> Self {
Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))
}
unsafe fn lock(&self) {
let r = libc::pthread_mutex_lock(self.0.get());
debug_assert_eq!(r, 0);
}
unsafe fn unlock(&self) {
let r = libc::pthread_mutex_unlock(self.0.get());
debug_assert_eq!(r, 0);
}
}

unsafe impl Sync for Mutex {}

struct DropGuard<F: FnMut()>(F);

impl<F: FnMut()> Drop for DropGuard<F> {
fn drop(&mut self) {
self.0()
}
}

0 comments on commit 2d9f884

Please sign in to comment.