use std::sync::Arc;
use std::time::Duration;
use tracing::{Instrument, info_span, warn};
use crate::backend::LockBackend;
use crate::clock::Stamp;
use crate::error::{Error, Result};
use crate::watchdog::{LeaseState, WatchdogHandle};
#[must_use]
pub fn validate_write(token: u64, current_token: u64) -> bool {
token >= current_token
}
#[must_use = "If dropped, the lock will be released immediately"]
pub struct LockGuard {
backend: Arc<dyn LockBackend>,
state: Arc<LeaseState>,
watchdog: WatchdogHandle,
acquired_at: Stamp,
active: bool,
}
impl LockGuard {
pub(crate) fn new(
backend: Arc<dyn LockBackend>,
state: Arc<LeaseState>,
watchdog: WatchdogHandle,
acquired_at: Stamp,
) -> Self {
Self {
backend,
state,
watchdog,
acquired_at,
active: true,
}
}
pub fn fencing_token(&self) -> Result<u64> {
if self.state.is_poisoned() {
return Err(Error::LeaseExpired {
resource: self.state.resource.clone(),
});
}
Ok(self.state.token)
}
pub fn resource(&self) -> &str {
&self.state.resource
}
pub fn acquired_at(&self) -> Stamp {
self.acquired_at
}
pub async fn extend(&self, new_ttl: Duration) -> Result<()> {
if self.state.is_poisoned() {
return Err(Error::LeaseExpired {
resource: self.state.resource.clone(),
});
}
let span = info_span!(
"ash_lock.extend",
resource_name = %self.state.resource,
fencing_token = self.state.token,
node_id = self.state.node_id,
);
async {
self.backend
.try_renew(&self.state.resource, self.state.token, new_ttl)
.await?;
self.state.set_ttl(new_ttl);
Ok(())
}
.instrument(span)
.await
}
#[cfg(test)]
pub(crate) fn state_renewals(&self) -> u64 {
self.state
.renewals
.load(std::sync::atomic::Ordering::Relaxed)
}
pub async fn unlock(mut self) -> Result<()> {
self.active = false;
self.state.mark_released();
self.watchdog.abort();
let span = info_span!(
"ash_lock.release",
resource_name = %self.state.resource,
fencing_token = self.state.token,
node_id = self.state.node_id,
);
async {
self.backend
.release(&self.state.resource, self.state.token)
.await
}
.instrument(span)
.await
}
}
impl Drop for LockGuard {
fn drop(&mut self) {
if !self.active {
return; }
self.state.mark_released();
self.watchdog.abort();
let backend = Arc::clone(&self.backend);
let resource = self.state.resource.clone();
let token = self.state.token;
let node_id = self.state.node_id;
let span = info_span!(
"ash_lock.release.drop",
resource_name = %resource,
fencing_token = token,
node_id = node_id,
);
match tokio::runtime::Handle::try_current() {
Ok(handle) => {
handle.spawn(
async move {
let _ = backend.release(&resource, token).await;
}
.instrument(span),
);
}
Err(_) => {
warn!(
parent: &span,
"no tokio runtime on drop; relying on TTL {:?} to reclaim lock",
self.state.ttl()
);
}
}
}
}