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