use std::sync::{Arc, Weak};
use uqa_core::memory::Budgeted;
use super::{
DatabaseId, SerializableLeases, SerializableTransactionId, StorageTransactionId, VersionError,
VersionResult,
};
use crate::read_control::StorageReadControl;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ResourceLeaseId(StorageTransactionId);
impl ResourceLeaseId {
pub fn new(database: DatabaseId, allocation: u64) -> VersionResult<Self> {
if allocation > u64::MAX / 2 {
return Err(VersionError::InvalidTransactionId);
}
StorageTransactionId::new(database, allocation).map(Self)
}
pub const fn database(self) -> DatabaseId {
self.0.database()
}
pub const fn allocation(self) -> u64 {
self.0.allocation()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ResourceLeaseRequest {
Claim,
Share,
Recover,
}
impl ResourceLeaseId {
pub fn tag(self, request: ResourceLeaseRequest) -> u64 {
self.allocation() * 2 + u64::from(request == ResourceLeaseRequest::Recover)
}
}
#[derive(Clone)]
pub struct ResourceLease {
id: ResourceLeaseId,
retained: Arc<dyn Send + Sync>,
}
impl ResourceLease {
pub fn retain<T: Send + Sync + 'static>(
id: ResourceLeaseId,
retained: T,
control: &StorageReadControl,
) -> VersionResult<Self> {
control.check()?;
Ok(Self {
id,
retained: Budgeted::new(retained, control.memory().empty_reservation())
.into_shared()?,
})
}
pub fn id(&self) -> ResourceLeaseId {
self.id
}
pub(crate) fn downgrade(&self) -> WeakResourceLease {
WeakResourceLease {
id: self.id,
retained: Arc::downgrade(&self.retained),
}
}
}
pub(crate) struct WeakResourceLease {
id: ResourceLeaseId,
retained: Weak<dyn Send + Sync>,
}
impl WeakResourceLease {
pub(crate) fn id(&self) -> ResourceLeaseId {
self.id
}
pub(crate) fn upgrade(&self) -> Option<ResourceLease> {
Some(ResourceLease {
id: self.id,
retained: self.retained.upgrade()?,
})
}
}
pub trait ResourceLeaseProvider: Send + Sync {
fn try_acquire_resource(
&self,
id: ResourceLeaseId,
request: ResourceLeaseRequest,
control: &StorageReadControl,
) -> VersionResult<Option<ResourceLease>>;
}
pub fn retain_local_resource(
leases: &dyn SerializableLeases,
id: ResourceLeaseId,
request: ResourceLeaseRequest,
control: &StorageReadControl,
) -> VersionResult<Option<ResourceLease>> {
let participant = |request| {
SerializableTransactionId::new(id.database(), *b"UQAResourceLease", id.tag(request))
};
if leases.is_alive(participant(ResourceLeaseRequest::Recover)?, control)?
|| (request != ResourceLeaseRequest::Share
&& leases.is_alive(participant(ResourceLeaseRequest::Share)?, control)?)
{
return Ok(None);
}
let retained = if request == ResourceLeaseRequest::Share {
leases.retain_shared(participant(request)?, control)?
} else {
leases.retain(participant(request)?, control)?
};
if retained.id() != participant(request)? {
return Err(VersionError::InvalidEncoding(
"resource transport returned another owner",
));
}
ResourceLease::retain(id, retained, control).map(Some)
}