use super::{TaskDetailScan, TaskDetailScope, TaskGroup, TaskGroupDiagnostics, TaskKind, TaskKindDiagnostics};
use std::time::Duration;
impl TaskGroup {
pub fn diagnostics_task_details(&self, scan_budget: usize, output_budget: usize) -> crate::RuntimeTaskDetails {
self.bounded_task_details(scan_budget, output_budget).into()
}
pub(crate) fn diagnostics(&self, long_running_threshold: Duration) -> TaskGroupDiagnostics {
let mut aggregate = TaskGroupDiagnosticsAccumulator::default();
let local_task_count = self.accumulate_local_diagnostics(long_running_threshold, &mut aggregate);
let mut pending = self.inner.registry.components_snapshot();
while let Some(group) = pending.pop() {
group.accumulate_local_diagnostics(long_running_threshold, &mut aggregate);
pending.extend(group.inner.registry.components_snapshot());
}
aggregate.finish(local_task_count)
}
pub(crate) fn bounded_task_details(&self, scan_budget: usize, output_budget: usize) -> TaskDetailScan {
let mut scan = TaskDetailScan::default();
if !self.collect_local_task_details(scan_budget, output_budget, TaskDetailScope::Local, &mut scan) {
return scan;
}
let mut remaining_groups = scan_budget;
let (mut pending, truncated) = self.inner.registry.bounded_components_snapshot(&mut remaining_groups);
scan.truncated |= truncated;
while let Some(group) = pending.pop() {
if scan.scanned >= scan_budget {
scan.truncated = true;
break;
}
if !group.collect_local_task_details(scan_budget, output_budget, TaskDetailScope::Subtree, &mut scan) {
break;
}
let (children, truncated) = group.inner.registry.bounded_components_snapshot(&mut remaining_groups);
scan.truncated |= truncated;
pending.extend(children);
}
scan.group_entries_scanned = scan_budget - remaining_groups;
scan
}
fn collect_local_task_details(
&self,
scan_budget: usize,
output_budget: usize,
scope: TaskDetailScope,
scan: &mut TaskDetailScan,
) -> bool {
self.inner
.registry
.collect_task_details(scan_budget, output_budget, scope, scan)
}
fn accumulate_local_diagnostics(
&self,
long_running_threshold: Duration,
aggregate: &mut TaskGroupDiagnosticsAccumulator,
) -> usize {
aggregate.group_count = aggregate.group_count.saturating_add(1);
let mut local_task_count = 0;
for task in self.inner.registry.tasks.iter() {
local_task_count += 1;
let elapsed = task.started_at.elapsed();
aggregate.record_task(task.kind, elapsed, elapsed >= long_running_threshold);
}
local_task_count
}
}
#[derive(Debug, Default)]
struct TaskGroupDiagnosticsAccumulator {
group_count: usize,
task_count: usize,
active_by_kind: [usize; TaskKind::COUNT],
long_running_by_kind: [usize; TaskKind::COUNT],
max_elapsed_by_kind: [Duration; TaskKind::COUNT],
}
impl TaskGroupDiagnosticsAccumulator {
fn record_task(&mut self, kind: TaskKind, elapsed: Duration, long_running: bool) {
let index = kind.index();
self.task_count = self.task_count.saturating_add(1);
self.active_by_kind[index] = self.active_by_kind[index].saturating_add(1);
if long_running {
self.long_running_by_kind[index] = self.long_running_by_kind[index].saturating_add(1);
}
self.max_elapsed_by_kind[index] = self.max_elapsed_by_kind[index].max(elapsed);
}
fn finish(self, local_task_count: usize) -> TaskGroupDiagnostics {
let task_kinds = TaskKind::ALL
.into_iter()
.filter_map(|kind| {
let index = kind.index();
(self.active_by_kind[index] > 0).then_some(TaskKindDiagnostics {
kind,
active: self.active_by_kind[index],
long_running: self.long_running_by_kind[index],
max_elapsed: self.max_elapsed_by_kind[index],
})
})
.collect();
TaskGroupDiagnostics {
local_task_count,
group_count: self.group_count,
task_count: self.task_count,
task_kinds,
}
}
}