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