1mod error;
7mod fence;
8mod layout;
9mod protocol;
10mod state;
11
12use std::sync::{Arc, Mutex};
13use std::time::Duration;
14
15use bytes::Bytes;
16use orbit_core::fleet::FleetLaneCursor;
17use orbit_core::{Fleet, NetId64, RingLoss};
18
19pub use error::{Error, Result};
20pub use fence::{Fence, FenceToken};
21pub use layout::{
22 DefaultLockLayout, LOCK_EVENT_RING_CAPACITY, LOCK_EVENT_RING_KIND,
23 LOCK_EVENT_RING_PAYLOAD_CAPACITY, LOCK_EVENT_RING_SPEC, LOCK_STATE_KIND, LockLayout,
24};
25pub use state::{LOCK_STATE_CAPACITY, LOCK_STATE_PAYLOAD_MAX};
26
27#[cfg(any(target_os = "linux", target_os = "freebsd"))]
28pub use orbit_core::RingEventFd;
29
30use layout::LockEventRecord;
31use state::LockStateStore;
32
33pub trait LockType {
35 const NAMESPACE: &'static str;
36}
37
38#[derive(Clone, Debug, PartialEq, Eq, Hash)]
40pub struct LockKey {
41 namespace: Bytes,
42 label: Bytes,
43}
44
45impl LockKey {
46 pub fn new<T: LockType>(label: impl Into<Bytes>) -> Self {
47 Self {
48 namespace: Bytes::from_static(T::NAMESPACE.as_bytes()),
49 label: label.into(),
50 }
51 }
52
53 pub fn from_parts(namespace: impl Into<Bytes>, label: impl Into<Bytes>) -> Self {
55 Self {
56 namespace: namespace.into(),
57 label: label.into(),
58 }
59 }
60
61 pub fn namespace(&self) -> &[u8] {
62 &self.namespace
63 }
64
65 pub fn label(&self) -> &[u8] {
66 &self.label
67 }
68}
69
70#[derive(Clone, Debug, PartialEq, Eq, Hash)]
72pub struct LockOwner(Bytes);
73
74impl LockOwner {
75 pub fn new(value: impl Into<Bytes>) -> Self {
76 Self(value.into())
77 }
78
79 pub fn as_bytes(&self) -> &[u8] {
80 &self.0
81 }
82}
83
84impl From<Bytes> for LockOwner {
85 fn from(value: Bytes) -> Self {
86 Self(value)
87 }
88}
89
90impl From<String> for LockOwner {
91 fn from(value: String) -> Self {
92 Self(Bytes::from(value))
93 }
94}
95
96impl From<&str> for LockOwner {
97 fn from(value: &str) -> Self {
98 Self(Bytes::copy_from_slice(value.as_bytes()))
99 }
100}
101
102#[derive(Clone, Debug, PartialEq, Eq)]
104pub struct LockLease {
105 pub lock_id: NetId64,
106 pub key: LockKey,
107 pub owner: LockOwner,
108 pub acquired_at_ms: u64,
110 pub expires_at_ms: u64,
112 pub state_revision: u64,
113}
114
115impl LockLease {
116 pub fn fencing_token(&self) -> FenceToken {
118 FenceToken::new(self.lock_id.counter())
119 }
120
121 pub fn is_expired_at(&self, now_ms: u64) -> bool {
122 self.expires_at_ms <= now_ms
123 }
124}
125
126#[derive(Clone, Debug, PartialEq, Eq)]
128pub enum LockAcquire {
129 Acquired(LockLease),
130 Occupied(LockLease),
131}
132
133impl LockAcquire {
134 pub fn acquired(self) -> Option<LockLease> {
135 match self {
136 Self::Acquired(lease) => Some(lease),
137 Self::Occupied(_) => None,
138 }
139 }
140
141 pub fn holder(&self) -> &LockLease {
142 match self {
143 Self::Acquired(lease) | Self::Occupied(lease) => lease,
144 }
145 }
146}
147
148#[derive(Clone, Debug, PartialEq, Eq)]
150pub enum LockTransition {
151 Acquired(LockLease),
152 Renewed(LockLease),
153 Released {
154 lock_id: NetId64,
155 key: LockKey,
156 owner: LockOwner,
157 state_revision: u64,
158 },
159}
160
161impl LockTransition {
162 pub fn lock_id(&self) -> NetId64 {
163 match self {
164 Self::Acquired(lease) | Self::Renewed(lease) => lease.lock_id,
165 Self::Released { lock_id, .. } => *lock_id,
166 }
167 }
168
169 pub fn key(&self) -> &LockKey {
170 match self {
171 Self::Acquired(lease) | Self::Renewed(lease) => &lease.key,
172 Self::Released { key, .. } => key,
173 }
174 }
175
176 pub fn owner(&self) -> &LockOwner {
177 match self {
178 Self::Acquired(lease) | Self::Renewed(lease) => &lease.owner,
179 Self::Released { owner, .. } => owner,
180 }
181 }
182
183 pub fn state_revision(&self) -> u64 {
184 match self {
185 Self::Acquired(lease) | Self::Renewed(lease) => lease.state_revision,
186 Self::Released { state_revision, .. } => *state_revision,
187 }
188 }
189}
190
191#[derive(Clone, Debug, PartialEq, Eq)]
193pub struct LockEvent {
194 pub event_id: NetId64,
195 pub transition: LockTransition,
196}
197
198#[derive(Clone, Debug, PartialEq, Eq)]
199pub struct LockCursor {
200 inner: FleetLaneCursor,
201}
202
203#[derive(Clone, Debug, Default, PartialEq, Eq)]
205pub struct LockPoll {
206 pub events: Vec<LockEvent>,
207 pub loss: RingLoss,
208 pub malformed: u64,
209}
210
211impl LockPoll {
212 pub fn is_empty(&self) -> bool {
213 self.events.is_empty() && self.loss.is_empty() && self.malformed == 0
214 }
215}
216
217#[derive(Clone)]
219pub struct Lock<L: LockLayout = DefaultLockLayout> {
220 fleet: Arc<Fleet>,
221 state: LockStateStore<L>,
222 cursor: Arc<Mutex<LockCursor>>,
223}
224
225impl Lock<DefaultLockLayout> {
226 pub fn new(fleet: Arc<Fleet>) -> Result<Self> {
228 Self::open(fleet, false)
229 }
230
231 pub fn replay_retained(fleet: Arc<Fleet>) -> Result<Self> {
233 Self::open(fleet, true)
234 }
235}
236
237impl<L: LockLayout> Lock<L> {
238 pub fn with_layout(fleet: Arc<Fleet>) -> Result<Self> {
240 Self::open(fleet, false)
241 }
242
243 pub fn replay_layout(fleet: Arc<Fleet>) -> Result<Self> {
246 Self::open(fleet, true)
247 }
248
249 fn open(fleet: Arc<Fleet>, replay_retained: bool) -> Result<Self> {
250 layout::validate::<L>()?;
251 let state = LockStateStore::new(&fleet);
252 let cursor = LockCursor {
253 inner: if replay_retained {
254 fleet.lane_cursor_from_start::<LockEventRecord<L>>()
255 } else {
256 fleet.lane_cursor_at_head::<LockEventRecord<L>>()
257 },
258 };
259 Ok(Self {
260 fleet,
261 state,
262 cursor: Arc::new(Mutex::new(cursor)),
263 })
264 }
265
266 pub fn try_acquire(
267 &self,
268 key: &LockKey,
269 owner: &LockOwner,
270 ttl: Duration,
271 ) -> Result<LockAcquire> {
272 let now_ms = monotonic_ms()?;
273 let expires_at_ms = deadline(now_ms, ttl)?;
274 self.state
275 .acquire(key, owner, now_ms, expires_at_ms, |transition| {
276 self.publish(transition).map(|_| ())
277 })
278 }
279
280 pub fn renew(&self, lease: &LockLease, ttl: Duration) -> Result<Option<LockLease>> {
282 let now_ms = monotonic_ms()?;
283 let expires_at_ms = deadline(now_ms, ttl)?;
284 self.state
285 .renew_id(lease, now_ms, expires_at_ms, |transition| {
286 self.publish(transition).map(|_| ())
287 })
288 }
289
290 pub fn renew_owned(
292 &self,
293 key: &LockKey,
294 owner: &LockOwner,
295 ttl: Duration,
296 ) -> Result<Option<LockLease>> {
297 let now_ms = monotonic_ms()?;
298 let expires_at_ms = deadline(now_ms, ttl)?;
299 self.state
300 .renew_owner(key, owner, now_ms, expires_at_ms, |transition| {
301 self.publish(transition).map(|_| ())
302 })
303 }
304
305 pub fn release(&self, lease: &LockLease) -> Result<bool> {
307 let now_ms = monotonic_ms()?;
308 self.state.release_id(lease, now_ms, |transition| {
309 self.publish(transition).map(|_| ())
310 })
311 }
312
313 pub fn release_owned(&self, key: &LockKey, owner: &LockOwner) -> Result<bool> {
315 let now_ms = monotonic_ms()?;
316 self.state.release_owner(key, owner, now_ms, |transition| {
317 self.publish(transition).map(|_| ())
318 })
319 }
320
321 pub fn force_release(&self, key: &LockKey) -> Result<bool> {
327 let now_ms = monotonic_ms()?;
328 self.state.force_release(key, now_ms, |transition| {
329 self.publish(transition).map(|_| ())
330 })
331 }
332
333 pub fn current(&self, key: &LockKey) -> Result<Option<LockLease>> {
335 self.state.current(key, monotonic_ms()?)
336 }
337
338 pub fn restore(&self, key: &LockKey, owner: &LockOwner) -> Result<Option<LockLease>> {
340 Ok(self
341 .current(key)?
342 .filter(|lease| lease.owner.as_bytes() == owner.as_bytes()))
343 }
344
345 pub fn poll(&self) -> LockPoll {
347 let raw = {
348 let mut cursor = self
349 .cursor
350 .lock()
351 .unwrap_or_else(|error| error.into_inner());
352 self.fleet
353 .poll_lanes::<LockEventRecord<L>>(&mut cursor.inner)
354 };
355 let mut events = Vec::with_capacity(raw.frames.len());
356 let mut malformed = 0;
357 for frame in raw.frames {
358 match protocol::decode(&frame) {
359 Some(event) => events.push(event),
360 None => malformed += 1,
361 }
362 }
363 events.sort_by_key(|event| event.transition.state_revision());
364 LockPoll {
365 events,
366 loss: raw.loss,
367 malformed,
368 }
369 }
370
371 pub fn reset_transport(&self) -> Result<()> {
373 let mut cursor = self
374 .cursor
375 .lock()
376 .unwrap_or_else(|error| error.into_inner());
377 self.state.reset()?;
378 self.fleet.reset_ring::<LockEventRecord<L>>()?;
379 cursor.inner = self.fleet.lane_cursor_at_head::<LockEventRecord<L>>();
380 Ok(())
381 }
382
383 #[cfg(unix)]
385 pub fn unlink(&self) -> Result<()> {
386 let ring = self.fleet.shm_ring::<LockEventRecord<L>>()?;
387 let ring_result = ring.unlink();
388 let state_result = self.state.unlink();
389 ring_result?;
390 state_result
391 }
392
393 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
395 pub fn event_fd(&self) -> Result<RingEventFd> {
396 self.fleet
397 .ring_event_fd::<LockEventRecord<L>>()
398 .map_err(Error::Io)
399 }
400
401 fn publish(&self, transition: &LockTransition) -> Result<LockEvent> {
402 let (kind, payload) = protocol::encode::<L>(transition)?;
403 let event_id = {
404 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
405 {
406 self.fleet.publish_notified::<LockEventRecord<L>>(
407 kind,
408 transition.state_revision(),
409 payload,
410 )?
411 }
412 #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
413 {
414 self.fleet
415 .publish::<LockEventRecord<L>>(kind, transition.state_revision(), payload)
416 }
417 };
418 Ok(LockEvent {
419 event_id,
420 transition: transition.clone(),
421 })
422 }
423}
424
425fn deadline(now_ms: u64, ttl: Duration) -> Result<u64> {
426 if ttl.is_zero() {
427 return Err(Error::TtlZero);
428 }
429 let ttl_ms = u64::try_from(ttl.as_millis().max(1)).map_err(|_| Error::TtlOverflow)?;
430 now_ms.checked_add(ttl_ms).ok_or(Error::TtlOverflow)
431}
432
433#[cfg(unix)]
434fn monotonic_ms() -> Result<u64> {
435 let mut value = std::mem::MaybeUninit::<libc::timespec>::uninit();
436 let result = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, value.as_mut_ptr()) };
437 if result != 0 {
438 return Err(Error::Io(std::io::Error::last_os_error()));
439 }
440 let value = unsafe { value.assume_init() };
441 let seconds = u64::try_from(value.tv_sec).map_err(|_| Error::TtlOverflow)?;
442 let nanos = u64::try_from(value.tv_nsec).map_err(|_| Error::TtlOverflow)?;
443 Ok(seconds
444 .saturating_mul(1_000)
445 .saturating_add(nanos / 1_000_000))
446}
447
448#[cfg(not(unix))]
449fn monotonic_ms() -> Result<u64> {
450 use std::sync::LazyLock;
451 use std::time::Instant;
452
453 static START: LazyLock<Instant> = LazyLock::new(Instant::now);
454 Ok(START.elapsed().as_millis().min(u128::from(u64::MAX)) as u64)
455}