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