laminar_core/cluster/control/lease_deadline/
mod.rs1use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
2use std::time::{Duration, Instant};
3
4pub struct LeaseDeadline {
6 origin: Instant,
7 valid_until_ns: AtomicU64,
8 terminal: AtomicBool,
9 transition: parking_lot::Mutex<()>,
10 changed: tokio::sync::Notify,
11}
12
13impl LeaseDeadline {
14 #[must_use]
16 pub(crate) fn uninitialized() -> Self {
17 Self {
18 origin: Instant::now(),
19 valid_until_ns: AtomicU64::new(0),
20 terminal: AtomicBool::new(false),
21 transition: parking_lot::Mutex::new(()),
22 changed: tokio::sync::Notify::new(),
23 }
24 }
25
26 #[must_use]
28 pub fn fenced() -> Self {
29 let deadline = Self::uninitialized();
30 deadline.fence();
31 deadline
32 }
33
34 #[must_use]
36 pub fn live_for(remaining: Duration) -> Self {
37 let deadline = Self::uninitialized();
38 deadline.extend(remaining);
39 deadline
40 }
41
42 pub(crate) fn extend(&self, remaining: Duration) {
43 let until = self.origin.elapsed().saturating_add(remaining).as_nanos();
44 let until = u64::try_from(until).unwrap_or(u64::MAX).max(1);
45 self.update_valid_until(until);
46 }
47
48 pub(crate) fn extend_until(&self, valid_until: Instant) {
49 let Some(remaining_from_origin) = valid_until.checked_duration_since(self.origin) else {
50 self.fence();
51 return;
52 };
53 if remaining_from_origin.is_zero() {
54 self.fence();
55 return;
56 }
57 let until = u64::try_from(remaining_from_origin.as_nanos()).unwrap_or(u64::MAX);
58 self.update_valid_until(until);
59 }
60
61 fn update_valid_until(&self, valid_until_ns: u64) {
62 let _transition = self.transition.lock();
63 if self.terminal.load(Ordering::Acquire) {
64 return;
65 }
66 let current = self.valid_until_ns.load(Ordering::Acquire);
67 if current != 0 && self.origin.elapsed().as_nanos() >= u128::from(current) {
68 self.terminal.store(true, Ordering::Release);
69 self.valid_until_ns.store(0, Ordering::Release);
70 self.changed.notify_waiters();
71 return;
72 }
73 self.valid_until_ns.store(valid_until_ns, Ordering::Release);
74 self.changed.notify_waiters();
75 }
76
77 pub fn fence(&self) {
79 let _transition = self.transition.lock();
80 self.terminal.store(true, Ordering::Release);
81 self.valid_until_ns.store(0, Ordering::Release);
82 self.changed.notify_waiters();
83 }
84
85 #[must_use]
87 pub fn is_live(&self) -> bool {
88 if self.terminal.load(Ordering::Acquire) {
89 return false;
90 }
91 let deadline = self.valid_until_ns.load(Ordering::Acquire);
92 deadline != 0 && self.origin.elapsed().as_nanos() < u128::from(deadline)
93 }
94
95 pub async fn wait_until_expired(&self) {
97 loop {
98 let changed = self.changed.notified();
99 tokio::pin!(changed);
100 changed.as_mut().enable();
101
102 if self.terminal.load(Ordering::Acquire) {
103 return;
104 }
105 let valid_until_ns = self.valid_until_ns.load(Ordering::Acquire);
106 let elapsed_ns = self.origin.elapsed().as_nanos();
107 if valid_until_ns == 0 {
108 return;
109 }
110 if elapsed_ns >= u128::from(valid_until_ns) {
111 let _transition = self.transition.lock();
112 if self.terminal.load(Ordering::Acquire) {
113 return;
114 }
115 let current = self.valid_until_ns.load(Ordering::Acquire);
116 if current != 0 && self.origin.elapsed().as_nanos() < u128::from(current) {
117 continue;
118 }
119 self.terminal.store(true, Ordering::Release);
120 self.valid_until_ns.store(0, Ordering::Release);
121 self.changed.notify_waiters();
122 return;
123 }
124 let remaining_ns = u128::from(valid_until_ns).saturating_sub(elapsed_ns);
125 let remaining = Duration::from_nanos(u64::try_from(remaining_ns).unwrap_or(u64::MAX));
126 tokio::select! {
127 () = tokio::time::sleep(remaining) => {}
128 () = &mut changed => {}
129 }
130 }
131 }
132}
133
134impl std::fmt::Debug for LeaseDeadline {
135 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 formatter
137 .debug_struct("LeaseDeadline")
138 .field("terminal", &self.terminal.load(Ordering::Acquire))
139 .field("live", &self.is_live())
140 .finish_non_exhaustive()
141 }
142}
143
144#[cfg(test)]
145mod tests;