Skip to main content

qubit_dcl/double_checked/
double_checked_lock.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Double-Checked Lock Convenience API
10//!
11//! Provides a one-shot convenience wrapper around
12//! [`super::DoubleCheckedLockExecutor`].
13//!
14//! # Author
15//!
16//! Haixing Hu
17
18use std::fmt::Display;
19
20use qubit_function::{Callable, CallableWith, Runnable, RunnableWith, Tester};
21
22use super::{
23    DoubleCheckedLockExecutor, ExecutionContext, executor_lock_builder::ExecutorLockBuilder,
24    executor_ready_builder::ExecutorReadyBuilder,
25};
26use crate::lock::Lock;
27
28/// Entry type for one-shot double-checked lock execution.
29///
30/// This API is useful when you do not need to keep a reusable executor
31/// instance. It delegates to [`DoubleCheckedLockExecutor`] internally.
32///
33/// # Examples
34///
35/// ```rust
36/// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
37///
38/// use qubit_dcl::{ArcMutex, DoubleCheckedLock, Lock};
39///
40/// let data = ArcMutex::new(10);
41/// let skip = Arc::new(AtomicBool::new(false));
42///
43/// let result = DoubleCheckedLock::on(data.clone())
44///     .when({
45///         let skip = skip.clone();
46///         move || !skip.load(Ordering::Acquire)
47///     })
48///     .call_with(|value: &mut i32| {
49///         *value += 5;
50///         Ok::<i32, std::io::Error>(*value)
51///     })
52///     .get_result();
53///
54/// assert!(result.is_success());
55/// assert_eq!(data.read(|value| *value), 15);
56/// ```
57#[derive(Debug, Clone, Copy, Default)]
58pub struct DoubleCheckedLock;
59
60impl DoubleCheckedLock {
61    /// Starts one-shot double-checked lock configuration by attaching a lock.
62    #[inline]
63    pub fn on<L, T>(lock: L) -> DoubleCheckedLockBuilder<L, T>
64    where
65        L: Lock<T>,
66    {
67        DoubleCheckedLockBuilder {
68            inner: DoubleCheckedLockExecutor::builder().on(lock),
69        }
70    }
71}
72
73/// Convenience builder state with lock attached.
74#[derive(Clone)]
75pub struct DoubleCheckedLockBuilder<L, T> {
76    inner: ExecutorLockBuilder<L, T>,
77}
78
79impl<L, T> DoubleCheckedLockBuilder<L, T>
80where
81    L: Lock<T>,
82{
83    /// Configures logging when the double-checked condition is not met.
84    #[inline]
85    pub fn log_unmet_condition(mut self, level: log::Level, message: impl Into<String>) -> Self {
86        self.inner = self.inner.log_unmet_condition(level, message);
87        self
88    }
89
90    /// Disables logging when the double-checked condition is not met.
91    #[inline]
92    pub fn disable_unmet_condition_logging(mut self) -> Self {
93        self.inner = self.inner.disable_unmet_condition_logging();
94        self
95    }
96
97    /// Configures logging when the prepare action fails.
98    #[inline]
99    pub fn log_prepare_failure(
100        mut self,
101        level: log::Level,
102        message_prefix: impl Into<String>,
103    ) -> Self {
104        self.inner = self.inner.log_prepare_failure(level, message_prefix);
105        self
106    }
107
108    /// Disables logging when the prepare action fails.
109    #[inline]
110    pub fn disable_prepare_failure_logging(mut self) -> Self {
111        self.inner = self.inner.disable_prepare_failure_logging();
112        self
113    }
114
115    /// Configures logging when the prepare commit action fails.
116    #[inline]
117    pub fn log_prepare_commit_failure(
118        mut self,
119        level: log::Level,
120        message_prefix: impl Into<String>,
121    ) -> Self {
122        self.inner = self.inner.log_prepare_commit_failure(level, message_prefix);
123        self
124    }
125
126    /// Disables logging when the prepare commit action fails.
127    #[inline]
128    pub fn disable_prepare_commit_failure_logging(mut self) -> Self {
129        self.inner = self.inner.disable_prepare_commit_failure_logging();
130        self
131    }
132
133    /// Configures logging when the prepare rollback action fails.
134    #[inline]
135    pub fn log_prepare_rollback_failure(
136        mut self,
137        level: log::Level,
138        message_prefix: impl Into<String>,
139    ) -> Self {
140        self.inner = self
141            .inner
142            .log_prepare_rollback_failure(level, message_prefix);
143        self
144    }
145
146    /// Disables logging when the prepare rollback action fails.
147    #[inline]
148    pub fn disable_prepare_rollback_failure_logging(mut self) -> Self {
149        self.inner = self.inner.disable_prepare_rollback_failure_logging();
150        self
151    }
152
153    /// Enables panic capture for tester, prepare callbacks, and task execution.
154    #[inline]
155    pub fn catch_panics(mut self) -> Self {
156        self.inner = self.inner.catch_panics();
157        self
158    }
159
160    /// Sets whether panic capture for tester, prepare callbacks, and task
161    /// execution is enabled.
162    #[inline]
163    pub fn set_catch_panics(mut self, catch_panics: bool) -> Self {
164        self.inner = self.inner.set_catch_panics(catch_panics);
165        self
166    }
167
168    /// Disables panic capture for tester, prepare callbacks, and task execution.
169    #[inline]
170    pub fn disable_catch_panics(mut self) -> Self {
171        self.inner = self.inner.disable_catch_panics();
172        self
173    }
174
175    /// Sets the required double-checked condition.
176    #[inline]
177    pub fn when<Tst>(self, tester: Tst) -> DoubleCheckedLockReadyBuilder<L, T>
178    where
179        Tst: Tester + Send + Sync + 'static,
180    {
181        DoubleCheckedLockReadyBuilder {
182            inner: self.inner.when(tester),
183        }
184    }
185}
186
187/// Convenience builder state with tester attached.
188#[derive(Clone)]
189pub struct DoubleCheckedLockReadyBuilder<L, T> {
190    inner: ExecutorReadyBuilder<L, T>,
191}
192
193impl<L, T> DoubleCheckedLockReadyBuilder<L, T>
194where
195    L: Lock<T>,
196{
197    /// Configures logging when the double-checked condition is not met.
198    #[inline]
199    pub fn log_unmet_condition(mut self, level: log::Level, message: impl Into<String>) -> Self {
200        self.inner = self.inner.log_unmet_condition(level, message);
201        self
202    }
203
204    /// Disables logging when the double-checked condition is not met.
205    #[inline]
206    pub fn disable_unmet_condition_logging(mut self) -> Self {
207        self.inner = self.inner.disable_unmet_condition_logging();
208        self
209    }
210
211    /// Configures logging when the prepare action fails.
212    #[inline]
213    pub fn log_prepare_failure(
214        mut self,
215        level: log::Level,
216        message_prefix: impl Into<String>,
217    ) -> Self {
218        self.inner = self.inner.log_prepare_failure(level, message_prefix);
219        self
220    }
221
222    /// Disables logging when the prepare action fails.
223    #[inline]
224    pub fn disable_prepare_failure_logging(mut self) -> Self {
225        self.inner = self.inner.disable_prepare_failure_logging();
226        self
227    }
228
229    /// Configures logging when the prepare commit action fails.
230    #[inline]
231    pub fn log_prepare_commit_failure(
232        mut self,
233        level: log::Level,
234        message_prefix: impl Into<String>,
235    ) -> Self {
236        self.inner = self.inner.log_prepare_commit_failure(level, message_prefix);
237        self
238    }
239
240    /// Disables logging when the prepare commit action fails.
241    #[inline]
242    pub fn disable_prepare_commit_failure_logging(mut self) -> Self {
243        self.inner = self.inner.disable_prepare_commit_failure_logging();
244        self
245    }
246
247    /// Configures logging when the prepare rollback action fails.
248    #[inline]
249    pub fn log_prepare_rollback_failure(
250        mut self,
251        level: log::Level,
252        message_prefix: impl Into<String>,
253    ) -> Self {
254        self.inner = self
255            .inner
256            .log_prepare_rollback_failure(level, message_prefix);
257        self
258    }
259
260    /// Disables logging when the prepare rollback action fails.
261    #[inline]
262    pub fn disable_prepare_rollback_failure_logging(mut self) -> Self {
263        self.inner = self.inner.disable_prepare_rollback_failure_logging();
264        self
265    }
266
267    /// Enables panic capture for tester, prepare callbacks, and task execution.
268    #[inline]
269    pub fn catch_panics(mut self) -> Self {
270        self.inner = self.inner.catch_panics();
271        self
272    }
273
274    /// Sets whether panic capture for tester, prepare callbacks, and task
275    /// execution is enabled.
276    #[inline]
277    pub fn set_catch_panics(mut self, catch_panics: bool) -> Self {
278        self.inner = self.inner.set_catch_panics(catch_panics);
279        self
280    }
281
282    /// Disables panic capture for tester, prepare callbacks, and task execution.
283    #[inline]
284    pub fn disable_catch_panics(mut self) -> Self {
285        self.inner = self.inner.disable_catch_panics();
286        self
287    }
288
289    /// Sets the prepare action.
290    #[inline]
291    pub fn prepare<Rn, E>(mut self, prepare_action: Rn) -> Self
292    where
293        Rn: Runnable<E> + Send + 'static,
294        E: Display,
295    {
296        self.inner = self.inner.prepare(prepare_action);
297        self
298    }
299
300    /// Sets the rollback action for prepare.
301    #[inline]
302    pub fn rollback_prepare<Rn, E>(mut self, rollback_prepare_action: Rn) -> Self
303    where
304        Rn: Runnable<E> + Send + 'static,
305        E: Display,
306    {
307        self.inner = self.inner.rollback_prepare(rollback_prepare_action);
308        self
309    }
310
311    /// Sets the commit action for prepare.
312    #[inline]
313    pub fn commit_prepare<Rn, E>(mut self, commit_prepare_action: Rn) -> Self
314    where
315        Rn: Runnable<E> + Send + 'static,
316        E: Display,
317    {
318        self.inner = self.inner.commit_prepare(commit_prepare_action);
319        self
320    }
321
322    /// Builds a reusable [`DoubleCheckedLockExecutor`].
323    #[inline]
324    pub fn build(self) -> DoubleCheckedLockExecutor<L, T> {
325        self.inner.build()
326    }
327
328    /// Runs a callable task with one-shot executor creation.
329    #[inline]
330    pub fn call<C, R, E>(self, task: C) -> ExecutionContext<R, E>
331    where
332        C: Callable<R, E>,
333        E: Display,
334    {
335        self.inner.build().call(task)
336    }
337
338    /// Runs a runnable task with one-shot executor creation.
339    #[inline]
340    pub fn execute<Rn, E>(self, task: Rn) -> ExecutionContext<(), E>
341    where
342        Rn: Runnable<E>,
343        E: Display,
344    {
345        self.inner.build().execute(task)
346    }
347
348    /// Runs a callable task with mutable protected data.
349    #[inline]
350    pub fn call_with<C, R, E>(self, task: C) -> ExecutionContext<R, E>
351    where
352        C: CallableWith<T, R, E>,
353        E: Display,
354    {
355        self.inner.build().call_with(task)
356    }
357
358    /// Runs a runnable task with mutable protected data.
359    #[inline]
360    pub fn execute_with<Rn, E>(self, task: Rn) -> ExecutionContext<(), E>
361    where
362        Rn: RunnableWith<T, E>,
363        E: Display,
364    {
365        self.inner.build().execute_with(task)
366    }
367}