Skip to main content

qubit_lock/monitor/
monitor_guard.rs

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