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