wrap Mutex and Condvar to handle poison

This centralizes a major source of `.unwrap()` throughout the code,
and one that would otherwise grow with upcoming changes. The new
error message should be more clear.
This commit is contained in:
Scott Lamb
2025-04-03 08:36:56 -07:00
parent 2903b680df
commit 0ccc6d0769
8 changed files with 120 additions and 62 deletions

View File

@@ -7,8 +7,8 @@
//! Note these types are in a more standard nanosecond-based format, where
//! [`crate::time`] uses Moonfire's 90 kHz time base.
use crate::Mutex;
use nix::sys::time::{TimeSpec, TimeValLike as _};
use std::sync::Mutex;
use std::sync::{mpsc, Arc};
use std::thread;
pub use std::time::Duration;
@@ -220,15 +220,15 @@ impl SimulatedClocks {
impl Clocks for SimulatedClocks {
fn realtime(&self) -> SystemTime {
self.0.boot + *self.0.uptime.lock().unwrap()
self.0.boot + *self.0.uptime.lock()
}
fn monotonic(&self) -> Instant {
Instant(TimeSpec::from(*self.0.uptime.lock().unwrap()))
Instant(TimeSpec::from(*self.0.uptime.lock()))
}
/// Advances the clock by the specified amount without actually sleeping.
fn sleep(&self, how_long: Duration) {
let mut l = self.0.uptime.lock().unwrap();
let mut l = self.0.uptime.lock();
*l += how_long;
}

View File

@@ -14,3 +14,64 @@ pub use crate::error::{Error, ErrorBuilder, ErrorKind, ResultExt};
pub use ahash::RandomState;
pub type FastHashMap<K, V> = std::collections::HashMap<K, V, ahash::RandomState>;
pub type FastHashSet<K> = std::collections::HashSet<K, ahash::RandomState>;
const NOT_POISONED: &str =
"not poisoned; this is a consequence of an earlier panic while holding this mutex; see logs.";
/// [`std::sync::Mutex`] wrapper which always panics on encountering poison.
#[derive(Default)]
pub struct Mutex<T>(std::sync::Mutex<T>);
impl<T> Mutex<T> {
#[inline]
pub const fn new(value: T) -> Self {
Mutex(std::sync::Mutex::new(value))
}
#[track_caller]
#[inline]
pub fn lock(&self) -> std::sync::MutexGuard<T> {
self.0.lock().expect(NOT_POISONED)
}
#[track_caller]
#[inline]
pub fn into_inner(self) -> T {
self.0.into_inner().expect(NOT_POISONED)
}
}
/// [`std::sync::Condvar`] wrapper which always panics on encountering poison.
#[derive(Default)]
pub struct Condvar(std::sync::Condvar);
impl Condvar {
#[inline]
pub const fn new() -> Self {
Self(std::sync::Condvar::new())
}
#[track_caller]
#[inline]
pub fn wait_timeout_while<'a, T, F>(
&self,
guard: std::sync::MutexGuard<'a, T>,
dur: std::time::Duration,
condition: F,
) -> (std::sync::MutexGuard<'a, T>, std::sync::WaitTimeoutResult)
where
F: FnMut(&mut T) -> bool,
{
self.0
.wait_timeout_while(guard, dur, condition)
.expect(NOT_POISONED)
}
}
impl std::ops::Deref for Condvar {
type Target = std::sync::Condvar;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@@ -15,9 +15,10 @@ use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll, Waker};
use crate::Condvar;
use crate::Mutex;
use futures::Future;
use slab::Slab;
use std::sync::{Condvar, Mutex};
#[derive(Debug)]
pub struct ShutdownError;
@@ -47,7 +48,6 @@ impl Drop for Sender {
.0
.wakers
.lock()
.unwrap()
.take()
.expect("only the single Sender takes the slab");
for w in wakers.drain() {
@@ -78,7 +78,7 @@ const NO_WAKER: usize = usize::MAX;
impl Receiver {
pub fn check(&self) -> Result<(), ShutdownError> {
if self.0.wakers.lock().unwrap().is_none() {
if self.0.wakers.lock().is_none() {
Err(ShutdownError)
} else {
Ok(())
@@ -107,12 +107,11 @@ impl Receiver {
}
pub fn wait_for(&self, timeout: std::time::Duration) -> Result<(), ShutdownError> {
let l = self.0.wakers.lock().unwrap();
let l = self.0.wakers.lock();
let result = self
.0
.condvar
.wait_timeout_while(l, timeout, |wakers| wakers.is_some())
.unwrap();
.wait_timeout_while(l, timeout, |wakers| wakers.is_some());
if result.1.timed_out() {
Ok(())
} else {
@@ -122,7 +121,7 @@ impl Receiver {
}
fn poll_impl(inner: &Inner, waker_i: &mut usize, cx: &mut Context<'_>) -> Poll<()> {
let mut l = inner.wakers.lock().unwrap();
let mut l = inner.wakers.lock();
let wakers = match &mut *l {
None => return Poll::Ready(()),
Some(w) => w,
@@ -152,7 +151,7 @@ impl Drop for ReceiverRefFuture<'_> {
if self.waker_i == NO_WAKER {
return;
}
let mut l = self.receiver.0.wakers.lock().unwrap();
let mut l = self.receiver.0.wakers.lock();
if let Some(wakers) = &mut *l {
wakers.remove(self.waker_i);
}
@@ -173,7 +172,7 @@ impl Drop for ReceiverFuture {
if self.waker_i == NO_WAKER {
return;
}
let mut l = self.receiver.wakers.lock().unwrap();
let mut l = self.receiver.wakers.lock();
if let Some(wakers) = &mut *l {
wakers.remove(self.waker_i);
}