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}