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::{
21    Callable,
22    CallableWith,
23    Runnable,
24    RunnableWith,
25    Tester,
26};
27
28use super::{
29    DoubleCheckedLockExecutor,
30    ExecutionContext,
31    executor_lock_builder::ExecutorLockBuilder,
32    executor_ready_builder::ExecutorReadyBuilder,
33};
34use crate::lock::Lock;
35
36/// Entry type for one-shot double-checked lock execution.
37///
38/// This API is useful when you do not need to keep a reusable executor
39/// instance. It delegates to [`DoubleCheckedLockExecutor`] internally.
40///
41/// # Examples
42///
43/// ```rust
44/// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
45///
46/// use qubit_dcl::{ArcMutex, DoubleCheckedLock, Lock};
47///
48/// let data = ArcMutex::new(10);
49/// let skip = Arc::new(AtomicBool::new(false));
50///
51/// let result = DoubleCheckedLock::on(data.clone())
52///     .when({
53///         let skip = skip.clone();
54///         move || !skip.load(Ordering::Acquire)
55///     })
56///     .call_with(|value: &mut i32| {
57///         *value += 5;
58///         Ok::<i32, std::io::Error>(*value)
59///     })
60///     .get_result();
61///
62/// assert!(result.is_success());
63/// assert_eq!(data.read(|value| *value), 15);
64/// ```
65#[derive(Debug, Clone, Copy, Default)]
66pub struct DoubleCheckedLock;
67
68impl DoubleCheckedLock {
69    /// Starts one-shot double-checked lock configuration by attaching a lock.
70    #[inline]
71    pub fn on<L, T>(lock: L) -> DoubleCheckedLockLockBuilder<L, T>
72    where
73        L: Lock<T>,
74    {
75        DoubleCheckedLockLockBuilder {
76            inner: DoubleCheckedLockExecutor::builder().on(lock),
77        }
78    }
79}
80
81/// Convenience builder state with lock attached.
82#[derive(Clone)]
83pub struct DoubleCheckedLockLockBuilder<L, T> {
84    inner: ExecutorLockBuilder<L, T>,
85}
86
87impl<L, T> DoubleCheckedLockLockBuilder<L, T>
88where
89    L: Lock<T>,
90{
91    /// Configures logging when the double-checked condition is not met.
92    #[inline]
93    pub fn log_unmet_condition(mut self, level: log::Level, message: impl Into<String>) -> Self {
94        self.inner = self.inner.log_unmet_condition(level, message);
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    /// Configures logging when the prepare commit action fails.
110    #[inline]
111    pub fn log_prepare_commit_failure(
112        mut self,
113        level: log::Level,
114        message_prefix: impl Into<String>,
115    ) -> Self {
116        self.inner = self.inner.log_prepare_commit_failure(level, message_prefix);
117        self
118    }
119
120    /// Configures logging when the prepare rollback action fails.
121    #[inline]
122    pub fn log_prepare_rollback_failure(
123        mut self,
124        level: log::Level,
125        message_prefix: impl Into<String>,
126    ) -> Self {
127        self.inner = self
128            .inner
129            .log_prepare_rollback_failure(level, message_prefix);
130        self
131    }
132
133    /// Sets the required double-checked condition.
134    #[inline]
135    pub fn when<Tst>(self, tester: Tst) -> DoubleCheckedLockReadyBuilder<L, T>
136    where
137        Tst: Tester + Send + Sync + 'static,
138    {
139        DoubleCheckedLockReadyBuilder {
140            inner: self.inner.when(tester),
141        }
142    }
143}
144
145/// Convenience builder state with tester attached.
146#[derive(Clone)]
147pub struct DoubleCheckedLockReadyBuilder<L, T> {
148    inner: ExecutorReadyBuilder<L, T>,
149}
150
151impl<L, T> DoubleCheckedLockReadyBuilder<L, T>
152where
153    L: Lock<T>,
154{
155    /// Configures logging when the double-checked condition is not met.
156    #[inline]
157    pub fn log_unmet_condition(mut self, level: log::Level, message: impl Into<String>) -> Self {
158        self.inner = self.inner.log_unmet_condition(level, message);
159        self
160    }
161
162    /// Configures logging when the prepare action fails.
163    #[inline]
164    pub fn log_prepare_failure(
165        mut self,
166        level: log::Level,
167        message_prefix: impl Into<String>,
168    ) -> Self {
169        self.inner = self.inner.log_prepare_failure(level, message_prefix);
170        self
171    }
172
173    /// Configures logging when the prepare commit action fails.
174    #[inline]
175    pub fn log_prepare_commit_failure(
176        mut self,
177        level: log::Level,
178        message_prefix: impl Into<String>,
179    ) -> Self {
180        self.inner = self.inner.log_prepare_commit_failure(level, message_prefix);
181        self
182    }
183
184    /// Configures logging when the prepare rollback action fails.
185    #[inline]
186    pub fn log_prepare_rollback_failure(
187        mut self,
188        level: log::Level,
189        message_prefix: impl Into<String>,
190    ) -> Self {
191        self.inner = self
192            .inner
193            .log_prepare_rollback_failure(level, message_prefix);
194        self
195    }
196
197    /// Sets the prepare action.
198    #[inline]
199    pub fn prepare<Rn, E>(mut self, prepare_action: Rn) -> Self
200    where
201        Rn: Runnable<E> + Send + 'static,
202        E: Display + Send + 'static,
203    {
204        self.inner = self.inner.prepare(prepare_action);
205        self
206    }
207
208    /// Sets the rollback action for prepare.
209    #[inline]
210    pub fn rollback_prepare<Rn, E>(mut self, rollback_prepare_action: Rn) -> Self
211    where
212        Rn: Runnable<E> + Send + 'static,
213        E: Display + Send + 'static,
214    {
215        self.inner = self.inner.rollback_prepare(rollback_prepare_action);
216        self
217    }
218
219    /// Sets the commit action for prepare.
220    #[inline]
221    pub fn commit_prepare<Rn, E>(mut self, commit_prepare_action: Rn) -> Self
222    where
223        Rn: Runnable<E> + Send + 'static,
224        E: Display + Send + 'static,
225    {
226        self.inner = self.inner.commit_prepare(commit_prepare_action);
227        self
228    }
229
230    /// Builds a reusable [`DoubleCheckedLockExecutor`].
231    #[inline]
232    pub fn build(self) -> DoubleCheckedLockExecutor<L, T> {
233        self.inner.build()
234    }
235
236    /// Runs a callable task with one-shot executor creation.
237    #[inline]
238    pub fn call<C, R, E>(self, task: C) -> ExecutionContext<R, E>
239    where
240        C: Callable<R, E> + Send + 'static,
241        R: Send + 'static,
242        E: Display + Send + 'static,
243    {
244        self.inner.build().call(task)
245    }
246
247    /// Runs a runnable task with one-shot executor creation.
248    #[inline]
249    pub fn execute<Rn, E>(self, task: Rn) -> ExecutionContext<(), E>
250    where
251        Rn: Runnable<E> + Send + 'static,
252        E: Display + Send + 'static,
253    {
254        self.inner.build().execute(task)
255    }
256
257    /// Runs a callable task with mutable protected data.
258    #[inline]
259    pub fn call_with<C, R, E>(self, task: C) -> ExecutionContext<R, E>
260    where
261        C: CallableWith<T, R, E> + Send + 'static,
262        R: Send + 'static,
263        E: Display + Send + 'static,
264    {
265        self.inner.build().call_with(task)
266    }
267
268    /// Runs a runnable task with mutable protected data.
269    #[inline]
270    pub fn execute_with<Rn, E>(self, task: Rn) -> ExecutionContext<(), E>
271    where
272        Rn: RunnableWith<T, E> + Send + 'static,
273        E: Display + Send + 'static,
274    {
275        self.inner.build().execute_with(task)
276    }
277}