use std::sync::atomic::Ordering;
use crate::error::RegistryError;
use crate::event::LifecycleState;
use crate::fragment::{ComponentIncarnation, FragmentKey, FragmentRow};
use super::ComponentRegistry;
use super::support::{ComponentRecord, state};
impl ComponentRegistry {
pub(super) fn prepare_running_incarnation(
&self,
id: crate::component::ComponentId,
record: &ComponentRecord,
) -> Result<(), RegistryError> {
{
let mut content = record
.fragment_content
.lock()
.map_err(|_| RegistryError::SynchronizationPoisoned)?;
content.clear();
for declaration in &record.meta.fragments {
content.insert(declaration.id.clone(), declaration.content.clone());
}
}
let ordinal = {
let mut history = self
.incarnation_history
.lock()
.map_err(|_| RegistryError::SynchronizationPoisoned)?;
let entry = history.entry(id).or_insert(0);
*entry += 1;
*entry
};
record.incarnation.store(ordinal, Ordering::Release);
Ok(())
}
pub fn update_fragment_content(
&self,
key: &FragmentKey,
held: ComponentIncarnation,
content: Vec<u8>,
) -> Result<(), RegistryError> {
let record = self.record(key.component_id)?;
let fragment = key.fragment_id.as_str().to_owned();
let Some(limit) = self.config.max_fragment_bytes else {
return Err(RegistryError::FragmentLimitUnconfigured {
component: key.component_id,
});
};
if content.len() > limit.get() {
return Err(RegistryError::FragmentContentOversize {
component: key.component_id,
fragment,
size: content.len(),
limit: limit.get(),
});
}
{
let mut live = record
.fragment_content
.lock()
.map_err(|_| RegistryError::SynchronizationPoisoned)?;
if state(&record)? != LifecycleState::Running {
return Err(RegistryError::FragmentIncarnationStale {
component: key.component_id,
fragment,
held,
current: None,
});
}
let current = ComponentIncarnation::new(record.incarnation.load(Ordering::Acquire));
if held != current {
return Err(RegistryError::FragmentIncarnationStale {
component: key.component_id,
fragment,
held,
current: Some(current),
});
}
let Some(slot) = live.get_mut(&key.fragment_id) else {
return Err(RegistryError::UnknownFragment {
component: key.component_id,
fragment,
});
};
*slot = content;
}
self.events
.publish_fragment_update(key.clone())
.map_err(|_| RegistryError::SynchronizationPoisoned)
}
pub fn fragment_snapshot(&self) -> Result<Vec<FragmentRow>, RegistryError> {
let records = self
.records
.lock()
.map_err(|_| RegistryError::SynchronizationPoisoned)?;
let mut rows = Vec::new();
for record in records.values() {
if state(record)? != LifecycleState::Running {
continue;
}
let incarnation = ComponentIncarnation::new(record.incarnation.load(Ordering::Acquire));
let content = record
.fragment_content
.lock()
.map_err(|_| RegistryError::SynchronizationPoisoned)?;
for declaration in &record.meta.fragments {
let Some(bytes) = content.get(&declaration.id) else {
return Err(RegistryError::UnknownFragment {
component: record.meta.id,
fragment: declaration.id.as_str().to_owned(),
});
};
rows.push(FragmentRow {
key: FragmentKey {
component_id: record.meta.id,
fragment_id: declaration.id.clone(),
},
kind: declaration.kind.clone(),
order: declaration.order,
content: bytes.clone(),
incarnation,
});
}
}
rows.sort_by(|left, right| left.key.cmp(&right.key));
Ok(rows)
}
}