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}