Skip to main content

DirLock

Struct DirLock 

Source
pub struct DirLock { /* private fields */ }
Expand description

Advisory, single-writer lock on a database directory (ADR-hjxp/0006), claimed with O_CREAT|O_EXCL and kept alive by a heartbeat that is written to a fresh temporary and renamed over the lock file (ADR-6wjp/0016), so a stale timestamp is never mistaken for proof that the holder is gone and no write ever follows a link somebody planted in the way.

The lease — stale_after_ms since the last heartbeat — is the whole liveness story, and holds on every platform. Where processes can be asked about (unix), a holder whose pid is gone is reclaimed sooner; that is an accelerator and nothing rests on it.

The heartbeat runs on a background thread where threads exist. Where they do not, or where one could not be started, DirLock::tick and the renewal every write already performs are what keeps the lease alive — call tick from a host timer at least twice per heartbeat_ms.

Implementations§

Source§

impl DirLock

Source

pub fn acquire( path: impl Into<PathBuf>, options: LockOptions, ) -> Result<DirLock, DirLockError>

path is the absolute path of the lock file itself, not of the directory holding it.

Source

pub fn acquire_as( path: PathBuf, options: LockOptions, subject: PathBuf, ) -> Result<DirLock, DirLockError>

As Self::acquire, but names subject — the database root, not the lock file — in a resulting LockedError.

Source

pub fn challenge( path: impl Into<PathBuf>, options: LockOptions, ) -> Result<ChallengeOutcome, DirLockError>

One pass of the acquire protocol that waits for nothing, so that every wait belongs to the caller. Where the engine runs on the one thread a host has — wasm32-wasip1 — this and Self::finish_challenge are what let a reclaim happen without the module sleeping inside a call the host is waiting on.

timeout_ms and retry_interval_ms are not read: there is nothing here to time out. A lock file that vanishes underneath the pass is retried at once, up to the same small bound Self::acquire uses, because that costs no waiting either.

@param path the absolute path of the lock file itself, not of the directory holding it @param options the lease tuning — stale_after_ms and heartbeat_ms as acquire reads them @returns the lock, a refusal to reclaim, or the grace a hand-over is owed first

Source

pub fn finish_challenge( path: impl Into<PathBuf>, options: LockOptions, holder: &LockHolder, ) -> Result<Option<DirLock>, DirLockError>

The far side of a ChallengeOutcome::GraceOwed, for a caller that has waited grace_ms out on its own clock. Reclaims the lock only if the lease is still the one that was challenged, and does no waiting of its own.

Two callers finishing the same challenge at the same moment are safe the way two Self::acquire calls are: the lock file ends up naming exactly one of them, and any other that believed it won is fenced the first time it calls Self::tick.

@param path the absolute path of the lock file itself, not of the directory holding it @param options the lease tuning — only heartbeat_ms is read @param holder the holder the challenge named, unchanged @returns the lock, or None when somebody proved alive there or reclaimed it first — in which case the caller is back to Self::challenge

Source

pub fn inspect( path: impl AsRef<Path>, options: LockOptions, ) -> Result<LockState, DirLockError>

Reads what the lock file says without claiming, reclaiming or removing anything — the only entry point here that leaves the directory exactly as it found it, and so the only one an observer can use against a database somebody else is writing.

The answer is a snapshot of one moment: a holder reported as LockState::Held can release the lock the instant after, and a lease reported as expired can be renewed. It is a diagnosis, never a reservation — nothing here entitles the caller to the lock.

@param path the absolute path of the lock file itself, not of the directory holding it @param options the lease tuning — only stale_after_ms is read, to date the lease @returns what the lock file holds, or DirLockError::SuspiciousLockFile when something other than a regular file sits at path

Source

pub fn tick(&self) -> Result<(), DirLockError>

Renews the heartbeat if heartbeat_ms has elapsed since the last renewal, and does nothing otherwise — cheap enough to call on every write, and safe to call from a host timer. This is the only renewal a platform without background threads gets, so a host that drives one must call it at least twice per heartbeat_ms.

Returns DirLockError::Lost once the lock has been reclaimed by somebody else; a handle that has heard that must stop writing, and every later call repeats it.

Source

pub fn is_lost(&self) -> bool

Whether this handle still holds the lock as far as it knows — false once a tick or a heartbeat has caught somebody else’s token in the lock file.

Source

pub fn release(&mut self)

Trait Implementations§

Source§

impl Debug for DirLock

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for DirLock

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.