use super::id::ChunkId;
use super::job::SerializationJob;
use crate::format::ChildRef;
use crate::{ParcodeError, Result};
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Debug)]
pub struct Node<'a> {
pub id: ChunkId,
pub parent: Option<ChunkId>,
pub atomic_deps: AtomicUsize,
pub job: Box<dyn SerializationJob<'a> + 'a>,
pub parent_slot: Option<usize>,
pub completed_children: Mutex<Vec<Option<ChildRef>>>,
}
impl<'a> Node<'a> {
pub fn new(id: ChunkId, job: Box<dyn SerializationJob<'a> + 'a>) -> Self {
Self {
id,
parent: None,
parent_slot: None,
atomic_deps: AtomicUsize::new(0),
job,
completed_children: Mutex::new(Vec::new()),
}
}
pub fn register_child_result(&self, slot: usize, child_ref: ChildRef) -> Result<()> {
let mut lock = self
.completed_children
.lock()
.map_err(|_| ParcodeError::Internal(format!("Mutex poisoned on node {:?}", self.id)))?;
let entry = lock.get_mut(slot).ok_or_else(|| {
ParcodeError::Internal(format!(
"Slot {} out of bounds for node {:?}",
slot, self.id
))
})?;
*entry = Some(child_ref);
Ok(())
}
}
#[derive(Debug)]
pub struct TaskGraph<'a> {
nodes: Vec<Node<'a>>,
}
impl<'a> TaskGraph<'a> {
pub fn new() -> Self {
Self { nodes: Vec::new() }
}
pub fn add_node(&mut self, job: Box<dyn SerializationJob<'a> + 'a>) -> ChunkId {
let id = ChunkId::new(u32::try_from(self.nodes.len()).unwrap_or(u32::MAX));
let node = Node::new(id, job);
self.nodes.push(node);
id
}
pub fn link_parent_child(&mut self, parent_id: ChunkId, child_id: ChunkId) {
let parent_node_idx = parent_id.as_u32() as usize;
let child_node_idx = child_id.as_u32() as usize;
let parent_node = self
.nodes
.get(parent_node_idx)
.expect("Parent node not found");
parent_node.atomic_deps.fetch_add(1, Ordering::SeqCst);
let slot = {
let mut guard = parent_node
.completed_children
.lock()
.expect("Mutex poisoned");
let idx = guard.len();
guard.push(None);
idx
};
let child_node = self
.nodes
.get_mut(child_node_idx)
.expect("Child node not found");
child_node.parent = Some(parent_id);
child_node.parent_slot = Some(slot);
}
pub fn get_node(&self, id: ChunkId) -> &Node<'a> {
self.nodes
.get(id.as_u32() as usize)
.expect("TaskGraph invariant violated: Node ID out of bounds")
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn nodes(&self) -> &[Node<'a>] {
&self.nodes
}
pub fn replace_job(&mut self, id: ChunkId, new_job: Box<dyn SerializationJob<'a> + 'a>) {
let node_idx = id.as_u32() as usize;
let node = self
.nodes
.get_mut(node_idx)
.expect("TaskGraph invariant violated: Node ID not found during job replacement");
node.job = new_job;
}
}
impl<'a> Default for TaskGraph<'a> {
fn default() -> Self {
Self::new()
}
}