mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-12-03 22:33:03 -05:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user