use super::attachments::{AttachmentError, AttachmentStore, TypedAttachments};
use super::{BlockRegistry, PositionalRadixTree};
use crate::blocks::{BlockMetadata, SequenceHash};
use std::any::{Any, TypeId};
use std::marker::PhantomData;
use std::sync::{Arc, Weak};
#[cfg(not(test))]
use parking_lot::Mutex;
#[cfg(test)]
use tracing_mutex::parkinglot::Mutex;
#[derive(Clone, Debug)]
pub struct BlockRegistrationHandle {
pub(crate) inner: Arc<BlockRegistrationHandleInner>,
}
type TouchCallback = Arc<dyn Fn(SequenceHash) + Send + Sync>;
pub(crate) struct BlockRegistrationHandleInner {
seq_hash: SequenceHash,
pub(crate) attachments: Mutex<AttachmentStore>,
touch_callbacks: Mutex<Vec<TouchCallback>>,
registry: Weak<PositionalRadixTree<Weak<BlockRegistrationHandleInner>>>,
}
impl std::fmt::Debug for BlockRegistrationHandleInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BlockRegistrationHandleInner")
.field("seq_hash", &self.seq_hash)
.field("attachments", &self.attachments)
.field(
"touch_callbacks",
&format!("[{} callbacks]", self.touch_callbacks.lock().len()),
)
.finish()
}
}
impl BlockRegistrationHandleInner {
pub(super) fn new(
seq_hash: SequenceHash,
registry: Weak<PositionalRadixTree<Weak<BlockRegistrationHandleInner>>>,
) -> Self {
Self {
seq_hash,
attachments: Mutex::new(AttachmentStore::new()),
touch_callbacks: Mutex::new(Vec::new()),
registry,
}
}
}
impl Drop for BlockRegistrationHandleInner {
#[inline]
fn drop(&mut self) {
let Some(registry) = self.registry.upgrade() else {
return;
};
let map = registry.prefix(&self.seq_hash);
let should_remove = match map.get(&self.seq_hash) {
Some(weak_ref) => std::ptr::eq(weak_ref.as_ptr(), self as *const Self),
None => {
debug_assert!(
false,
"registry entry vanished while a strong ref was alive: {:?}",
self.seq_hash
);
false
}
};
if should_remove {
map.remove(&self.seq_hash);
}
}
}
impl BlockRegistrationHandle {
pub(crate) fn from_inner(inner: Arc<BlockRegistrationHandleInner>) -> Self {
Self { inner }
}
pub fn seq_hash(&self) -> SequenceHash {
self.inner.seq_hash
}
pub fn is_from_registry(&self, registry: &BlockRegistry) -> bool {
self.inner
.registry
.upgrade()
.map(|reg| Arc::ptr_eq(®, ®istry.prt))
.unwrap_or(false)
}
pub(crate) fn mark_present<T: BlockMetadata>(&self) {
let type_id = TypeId::of::<T>();
let mut attachments = self.inner.attachments.lock();
*attachments.presence_markers.entry(type_id).or_insert(0) += 1;
}
pub(crate) fn mark_absent<T: BlockMetadata>(&self) {
let type_id = TypeId::of::<T>();
let mut attachments = self.inner.attachments.lock();
match attachments.presence_markers.get_mut(&type_id) {
Some(count) => {
debug_assert!(*count > 0, "mark_absent on zero-count presence marker");
*count -= 1;
if *count == 0 {
attachments.presence_markers.remove(&type_id);
}
}
None => debug_assert!(false, "mark_absent with no presence marker present"),
}
}
pub fn has_block<T: BlockMetadata>(&self) -> bool {
let type_id = TypeId::of::<T>();
let attachments = self.inner.attachments.lock();
attachments
.presence_markers
.get(&type_id)
.copied()
.unwrap_or(0)
> 0
}
pub fn has_any_block(&self, type_ids: &[TypeId]) -> bool {
let attachments = self.inner.attachments.lock();
type_ids.iter().any(|type_id| {
attachments
.presence_markers
.get(type_id)
.copied()
.unwrap_or(0)
> 0
})
}
pub fn on_touch(&self, callback: Arc<dyn Fn(SequenceHash) + Send + Sync>) {
self.inner.touch_callbacks.lock().push(callback);
}
pub fn touch(&self) {
let callbacks: Vec<_> = self.inner.touch_callbacks.lock().clone();
let seq_hash = self.inner.seq_hash;
for cb in &callbacks {
cb(seq_hash);
}
}
pub fn get<T: Any + Send + Sync>(&self) -> TypedAttachments<'_, T> {
TypedAttachments {
handle: self,
_phantom: PhantomData,
}
}
pub fn attach_unique<T: Any + Send + Sync>(&self, value: T) -> Result<(), AttachmentError> {
let type_id = TypeId::of::<T>();
let mut attachments = self.inner.attachments.lock();
if let Some(super::attachments::AttachmentMode::Multiple) =
attachments.type_registry.get(&type_id)
{
return Err(AttachmentError::TypeAlreadyRegisteredAsMultiple(type_id));
}
attachments
.unique_attachments
.insert(type_id, Box::new(value));
attachments
.type_registry
.insert(type_id, super::attachments::AttachmentMode::Unique);
Ok(())
}
pub fn attach<T: Any + Send + Sync>(&self, value: T) -> Result<(), AttachmentError> {
let type_id = TypeId::of::<T>();
let mut attachments = self.inner.attachments.lock();
if let Some(super::attachments::AttachmentMode::Unique) =
attachments.type_registry.get(&type_id)
{
return Err(AttachmentError::TypeAlreadyRegisteredAsUnique(type_id));
}
attachments
.multiple_attachments
.entry(type_id)
.or_default()
.push(Box::new(value));
attachments
.type_registry
.insert(type_id, super::attachments::AttachmentMode::Multiple);
Ok(())
}
}