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