Skip to main content

qubit_lock/monitor/
monitor_guard.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Monitor Guard
10//!
11//! Provides the guard returned by [`Monitor::lock`](super::Monitor::lock).
12//! The guard wraps a standard-library mutex guard and keeps a reference to the
13//! monitor that created it, so waiting operations can use the matching
14//! condition variable.
15//!
16//! # Author
17//!
18//! Haixing Hu
19
20use std::{
21    ops::{
22        Deref,
23        DerefMut,
24    },
25    sync::MutexGuard,
26    time::Duration,
27};
28
29use super::{
30    monitor::Monitor,
31    wait_timeout_status::WaitTimeoutStatus,
32};
33
34/// Guard returned by [`Monitor::lock`](super::Monitor::lock).
35///
36/// `MonitorGuard` is the monitor-specific counterpart of
37/// [`std::sync::MutexGuard`]. While it exists, the protected state is locked.
38/// Dropping the guard releases the lock. It implements [`Deref`] and
39/// [`DerefMut`], so callers can read and mutate the protected state as if they
40/// held `&T` or `&mut T`.
41///
42/// Unlike a raw `MutexGuard`, this guard also remembers the monitor that
43/// created it. That lets [`Self::wait`] and [`Self::wait_timeout`] release and
44/// reacquire the correct mutex with the correct condition variable.
45///
46/// # Type Parameters
47///
48/// * `T` - The state protected by the monitor.
49///
50/// # Example
51///
52/// ```rust
53/// use qubit_lock::lock::Monitor;
54///
55/// let monitor = Monitor::new(Vec::new());
56/// {
57///     let mut items = monitor.lock();
58///     items.push("first");
59/// }
60///
61/// assert_eq!(monitor.read(|items| items.len()), 1);
62/// ```
63pub struct MonitorGuard<'a, T> {
64    /// Monitor that owns the mutex and condition variable.
65    monitor: &'a Monitor<T>,
66    /// Standard mutex guard protecting the monitor state.
67    inner: MutexGuard<'a, T>,
68}
69
70impl<'a, T> MonitorGuard<'a, T> {
71    /// Creates a guard from its owning monitor and standard mutex guard.
72    ///
73    /// # Parameters
74    ///
75    /// * `monitor` - Monitor whose mutex produced `inner`.
76    /// * `inner` - Standard mutex guard protecting the monitor state.
77    ///
78    /// # Returns
79    ///
80    /// A monitor guard that can access state and wait on the monitor's
81    /// condition variable.
82    #[inline]
83    pub(super) fn new(monitor: &'a Monitor<T>, inner: MutexGuard<'a, T>) -> Self {
84        Self { monitor, inner }
85    }
86
87    /// Waits for a notification while temporarily releasing the monitor lock.
88    ///
89    /// This method consumes the current guard, calls the underlying
90    /// [`std::sync::Condvar::wait`], and returns a new guard after the lock has
91    /// been reacquired. It is intended for explicit guarded-suspension loops
92    /// where the caller needs to inspect or update state before and after
93    /// waiting.
94    ///
95    /// The method may block indefinitely if no notification is sent. The wait
96    /// may also wake spuriously, so callers should use it inside a loop that
97    /// re-checks the protected state.
98    ///
99    /// If the mutex is poisoned while waiting, the poisoned state is recovered
100    /// and returned in the new guard.
101    ///
102    /// # Returns
103    ///
104    /// A new guard holding the monitor lock after the wait returns.
105    ///
106    /// # Example
107    ///
108    /// ```rust
109    /// use std::{
110    ///     sync::Arc,
111    ///     thread,
112    /// };
113    ///
114    /// use qubit_lock::lock::Monitor;
115    ///
116    /// let monitor = Arc::new(Monitor::new(false));
117    /// let waiter_monitor = Arc::clone(&monitor);
118    ///
119    /// let waiter = thread::spawn(move || {
120    ///     let mut ready = waiter_monitor.lock();
121    ///     while !*ready {
122    ///         ready = ready.wait();
123    ///     }
124    ///     *ready = false;
125    /// });
126    ///
127    /// {
128    ///     let mut ready = monitor.lock();
129    ///     *ready = true;
130    /// }
131    /// monitor.notify_one();
132    ///
133    /// waiter.join().expect("waiter should finish");
134    /// assert!(!monitor.read(|ready| *ready));
135    /// ```
136    #[inline]
137    pub fn wait(self) -> Self {
138        let MonitorGuard { monitor, inner } = self;
139        let inner = monitor
140            .changed
141            .wait(inner)
142            .unwrap_or_else(std::sync::PoisonError::into_inner);
143        Self { monitor, inner }
144    }
145
146    /// Waits for a notification or timeout while temporarily releasing the lock.
147    ///
148    /// This method consumes the current guard, calls the underlying
149    /// [`std::sync::Condvar::wait_timeout`], and returns a new guard after the
150    /// lock has been reacquired. The status reports whether the wait reached
151    /// the timeout boundary or returned earlier.
152    ///
153    /// A [`WaitTimeoutStatus::Woken`] result does not prove that another thread
154    /// changed the state; condition variables may wake spuriously. A
155    /// [`WaitTimeoutStatus::TimedOut`] result also does not remove the need to
156    /// inspect the state, because another thread may have changed it while this
157    /// thread was reacquiring the lock.
158    ///
159    /// If the mutex is poisoned while waiting, the poisoned state is recovered
160    /// and returned in the new guard.
161    ///
162    /// # Arguments
163    ///
164    /// * `timeout` - Maximum duration to wait before returning
165    ///   [`WaitTimeoutStatus::TimedOut`].
166    ///
167    /// # Returns
168    ///
169    /// A tuple containing the reacquired guard and the timed-wait status.
170    ///
171    /// # Example
172    ///
173    /// ```rust
174    /// use std::time::Duration;
175    ///
176    /// use qubit_lock::lock::{Monitor, WaitTimeoutStatus};
177    ///
178    /// let monitor = Monitor::new(0);
179    /// let guard = monitor.lock();
180    /// let (guard, status) = guard.wait_timeout(Duration::from_millis(1));
181    ///
182    /// assert_eq!(*guard, 0);
183    /// assert_eq!(status, WaitTimeoutStatus::TimedOut);
184    /// ```
185    #[inline]
186    pub fn wait_timeout(self, timeout: Duration) -> (Self, WaitTimeoutStatus) {
187        let MonitorGuard { monitor, inner } = self;
188        let (inner, timeout_result) = monitor
189            .changed
190            .wait_timeout(inner, timeout)
191            .unwrap_or_else(std::sync::PoisonError::into_inner);
192        let status = if timeout_result.timed_out() {
193            WaitTimeoutStatus::TimedOut
194        } else {
195            WaitTimeoutStatus::Woken
196        };
197        (Self { monitor, inner }, status)
198    }
199}
200
201impl<T> Deref for MonitorGuard<'_, T> {
202    type Target = T;
203
204    /// Returns an immutable reference to the protected state.
205    #[inline]
206    fn deref(&self) -> &Self::Target {
207        &self.inner
208    }
209}
210
211impl<T> DerefMut for MonitorGuard<'_, T> {
212    /// Returns a mutable reference to the protected state.
213    #[inline]
214    fn deref_mut(&mut self) -> &mut Self::Target {
215        &mut self.inner
216    }
217}