use crate::component::concurrent::table::{TableDebug, TableId};
use crate::component::concurrent::{ConcurrentState, CurrentThread};
use crate::error::Result;
use crate::store::StoreOpaque;
use crate::{AsContextMut as _, Store, StoreContextMut};
use alloc::boxed::Box;
use alloc::vec::Vec;
struct TaskGroup {
ref_count: usize,
}
impl TableDebug for TaskGroup {
fn type_name() -> &'static str {
"TaskGroup"
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct TaskGroupId(TableId<TaskGroup>);
pub trait TaskGroupHook: Send + Sync + 'static {
fn handle_start(&mut self, id: TaskGroupId) -> Result<()>;
fn handle_enter(&mut self, id: TaskGroupId) -> Result<()>;
fn handle_exit(&mut self, id: TaskGroupId) -> Result<()>;
fn handle_finish(&mut self, id: TaskGroupId) -> Result<()>;
}
impl<T> Store<T> {
pub fn task_group_hook(&mut self, hook: impl TaskGroupHook) {
self.as_context_mut().task_group_hook(hook);
}
}
impl<T> StoreContextMut<'_, T> {
pub fn task_group_hook(self, hook: impl TaskGroupHook) {
self.0
.concurrent_state_mut_without_forcing_current_thread()
.task_group_hook = Some(Box::new(hook));
}
}
impl StoreOpaque {
pub(crate) fn clean_up_task_groups(&mut self) {
if !self.concurrency_support() {
return;
}
let state = self.concurrent_state_mut_without_forcing_current_thread();
if let Some(mut hook) = state.task_group_hook.take() {
let thread = state.unforced_current_thread;
if let Ok(Some(group)) = thread.group(state) {
_ = hook.handle_exit(group);
}
let groups = state
.table
.get_mut()
.iter_mut()
.filter_map(|(rep, entry)| {
if entry.downcast_mut::<TaskGroup>().is_some() {
Some(TableId::new(rep))
} else {
None
}
})
.collect::<Vec<_>>();
for group in groups {
state.delete(group).unwrap();
_ = hook.handle_finish(TaskGroupId(group));
}
state.task_group_hook = Some(hook);
}
}
}
impl CurrentThread {
fn group(&self, state: &mut ConcurrentState) -> Result<Option<TaskGroupId>> {
Ok(match self {
Self::Guest(thread) | Self::DeferredHost(thread) => {
Some(state.get_mut(thread.task)?.group)
}
Self::Host(task) => Some(state.get_mut(*task)?.group),
Self::None => None,
})
}
}
impl ConcurrentState {
pub(super) fn handle_thread_switch(
&mut self,
old: CurrentThread,
new: CurrentThread,
) -> Result<()> {
let old_group = old.group(self)?;
let new_group = new.group(self)?;
if let (true, Some(hook)) = ((old_group != new_group), &mut self.task_group_hook) {
if let Some(group) = old_group {
hook.handle_exit(group)?;
}
if let Some(group) = new_group {
hook.handle_enter(group)?;
}
}
Ok(())
}
pub(super) fn make_task_group(&mut self) -> Result<TaskGroupId> {
let group = TaskGroupId(self.push(TaskGroup { ref_count: 1 })?);
if let Some(hook) = &mut self.task_group_hook {
hook.handle_start(group)?;
}
log::trace!("new {group:?}");
Ok(group)
}
pub(super) fn increment_group_ref_count(&mut self, group: TaskGroupId) -> Result<()> {
let count = &mut self.get_mut(group.0)?.ref_count;
*count += 1;
log::trace!("increment {group:?} to {count}");
Ok(())
}
pub(super) fn decrement_group_ref_count(&mut self, group: TaskGroupId) -> Result<()> {
let count = &mut self.get_mut(group.0)?.ref_count;
assert!(*count >= 1);
*count -= 1;
log::trace!("decrement {group:?} to {count}");
if *count == 0 {
self.delete(group.0)?;
if let Some(hook) = &mut self.task_group_hook {
hook.handle_finish(group)?;
}
}
Ok(())
}
}