use super::RuntimeConstructionError;
use ic_platform::stable_structures::{Memory, VectorMemory};
use std::{collections::BTreeSet, rc::Rc, sync::Arc};
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct BackingIdentity(usize);
impl BackingIdentity {
#[must_use]
pub fn of_rc<T: ?Sized>(backing: &Rc<T>) -> Self {
Self(Rc::as_ptr(backing).cast::<()>() as usize)
}
#[must_use]
pub fn of_arc<T: ?Sized>(backing: &Arc<T>) -> Self {
Self(Arc::as_ptr(backing).cast::<()>() as usize)
}
}
pub trait MemoryBacking: Memory {
fn backing_identity(&self) -> BackingIdentity;
}
impl MemoryBacking for VectorMemory {
fn backing_identity(&self) -> BackingIdentity {
BackingIdentity::of_rc(self)
}
}
impl<M: MemoryBacking> MemoryBacking for Rc<M> {
fn backing_identity(&self) -> BackingIdentity {
self.as_ref().backing_identity()
}
}
#[cfg(target_arch = "wasm32")]
impl MemoryBacking for ic_platform::stable_structures::DefaultMemoryImpl {
fn backing_identity(&self) -> BackingIdentity {
BackingIdentity(0)
}
}
#[cfg(not(target_arch = "wasm32"))]
static CLAIMS: std::sync::Mutex<BTreeSet<BackingIdentity>> = std::sync::Mutex::new(BTreeSet::new());
#[cfg(target_arch = "wasm32")]
thread_local! {
static CLAIMS: std::cell::RefCell<BTreeSet<BackingIdentity>> = const {
std::cell::RefCell::new(BTreeSet::new())
};
}
fn with_claims<T>(operation: impl FnOnce(&mut BTreeSet<BackingIdentity>) -> T) -> T {
#[cfg(not(target_arch = "wasm32"))]
{
let mut claims = CLAIMS
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
operation(&mut claims)
}
#[cfg(target_arch = "wasm32")]
CLAIMS.with(|claims| operation(&mut claims.borrow_mut()))
}
pub(super) struct BackingClaim(BackingIdentity);
impl BackingClaim {
pub fn acquire(memory: &impl MemoryBacking) -> Result<Self, RuntimeConstructionError> {
let identity = memory.backing_identity();
if with_claims(|claims| claims.insert(identity)) {
Ok(Self(identity))
} else {
Err(RuntimeConstructionError::BackingAlreadyClaimed)
}
}
}
impl Drop for BackingClaim {
fn drop(&mut self) {
with_claims(|claims| claims.remove(&self.0));
}
}
#[cfg(all(test, not(target_arch = "wasm32")))]
impl<M: MemoryBacking> MemoryBacking for ic_testkit_native::memory::MeteredMemory<M> {
fn backing_identity(&self) -> BackingIdentity {
self.backing().backing_identity()
}
}