qubit_lock/lock/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::monitor::{
30 Monitor,
31 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}