stratum_apps/
custom_mutex.rs

1//! # Collection of Helper Primitives
2//!
3//! Provides a collection of utilities and helper structures used throughout the Stratum V2
4//! protocol implementation. These utilities simplify common tasks, such as ID generation and
5//! management, mutex management, difficulty target calculations, merkle root calculations, and
6//! more.
7
8use std::sync::{Mutex as Mutex_, MutexGuard, PoisonError};
9
10/// Custom synchronization primitive for managing shared mutable state.
11///
12/// This custom mutex implementation builds on [`std::sync::Mutex`] to enhance usability and safety
13/// in concurrent environments. It provides ergonomic methods to safely access and modify inner
14/// values while reducing the risk of deadlocks and panics. It is used throughout SRI applications
15/// to managed shared state across multiple threads, such as tracking active mining sessions,
16/// routing jobs, and managing connections safely and efficiently.
17///
18/// ## Advantages
19/// - **Closure-Based Locking:** The `safe_lock` method encapsulates the locking process, ensuring
20///   the lock is automatically released after the closure completes.
21/// - **Error Handling:** `safe_lock` enforces explicit handling of potential [`PoisonError`]
22///   conditions, reducing the risk of panics caused by poisoned locks.
23/// - **Panic-Safe Option:** The `super_safe_lock` method provides an alternative that unwraps the
24///   result of `safe_lock`, with optional runtime safeguards against panics.
25/// - **Extensibility:** Includes feature-gated functionality to customize behavior, such as
26///   stricter runtime checks using external tools like
27///   [`no-panic`](https://github.com/dtolnay/no-panic).
28#[derive(Debug)]
29pub struct Mutex<T: ?Sized>(Mutex_<T>);
30
31impl<T> Mutex<T> {
32    /// Mutex safe lock.
33    ///
34    /// Safely locks the `Mutex` and executes a closer (`thunk`) with a mutable reference to the
35    /// inner value. This ensures that the lock is automatically released after the closure
36    /// completes, preventing deadlocks. It explicitly returns a [`PoisonError`] containing a
37    /// [`MutexGuard`] to the inner value in cases where the lock is poisoned.
38    ///
39    /// To prevent poison lock errors, unwraps should never be used within the closure. The result
40    /// should always be returned and handled outside of the sage lock.
41    pub fn safe_lock<F, Ret>(&self, thunk: F) -> Result<Ret, PoisonError<MutexGuard<'_, T>>>
42    where
43        F: FnOnce(&mut T) -> Ret,
44    {
45        let mut lock = self.0.lock()?;
46        let return_value = thunk(&mut *lock);
47        drop(lock);
48        Ok(return_value)
49    }
50
51    /// Mutex super safe lock.
52    ///
53    /// Locks the `Mutex` and executes a closure (`thunk`) with a mutable reference to the inner
54    /// value, panicking if the lock is poisoned.
55    ///
56    /// This is a convenience wrapper around `safe_lock` for cases where explicit error handling is
57    /// unnecessary or undesirable. Use with caution in production code.
58    pub fn super_safe_lock<F, Ret>(&self, thunk: F) -> Ret
59    where
60        F: FnOnce(&mut T) -> Ret,
61    {
62        //#[cfg(feature = "disable_nopanic")]
63        {
64            self.safe_lock(thunk).unwrap()
65        }
66        //#[cfg(not(feature = "disable_nopanic"))]
67        //{
68        //    // based on https://github.com/dtolnay/no-panic
69        //    struct __NoPanic;
70        //    extern "C" {
71        //        #[link_name = "super_safe_lock called on a function that may panic"]
72        //        fn trigger() -> !;
73        //    }
74        //    impl core::ops::Drop for __NoPanic {
75        //        fn drop(&mut self) {
76        //            unsafe {
77        //                trigger();
78        //            }
79        //        }
80        //    }
81        //    let mut lock = self.0.lock().expect("threads to never panic");
82        //    let __guard = __NoPanic;
83        //    let return_value = thunk(&mut *lock);
84        //    core::mem::forget(__guard);
85        //    drop(lock);
86        //    return_value
87        //}
88    }
89
90    /// Creates a new [`Mutex`] instance, storing the initial value inside.
91    pub fn new(v: T) -> Self {
92        Mutex(Mutex_::new(v))
93    }
94
95    /// Removes lock for direct access.
96    ///
97    /// Acquires a lock on the [`Mutex`] and returns a [`MutexGuard`] for direct access to the
98    /// inner value. Allows for manual lock handling and is useful in scenarios where closures are
99    /// not convenient.
100    pub fn to_remove(&self) -> Result<MutexGuard<'_, T>, PoisonError<MutexGuard<'_, T>>> {
101        self.0.lock()
102    }
103}
104
105#[cfg(test)]
106mod tests {
107
108    #[test]
109    fn test_super_safe_lock() {
110        let m = super::Mutex::new(1u32);
111        m.safe_lock(|i| *i += 1).unwrap();
112        // m.super_safe_lock(|i| *i = (*i).checked_add(1).unwrap()); // will not compile
113        m.super_safe_lock(|i| *i = (*i).checked_add(1).unwrap_or_default()); // compiles
114    }
115}