use std::sync::Arc;
use std::sync::Weak;
use std::time::Duration;
use std::time::Instant;
use dashmap::DashMap;
use parking_lot::Mutex;
use serde::Serialize;
use super::BlockingKind;
use super::BlockingLane;
use super::BlockingTaskId;
use super::BlockingTaskState;
pub(crate) type BlockingTaskTable = DashMap<BlockingTaskId, BlockingTaskMeta>;
#[derive(Debug, Default)]
pub(crate) struct IsolatedBlockingTables {
tables: Mutex<Vec<Weak<BlockingTaskTable>>>,
}
impl IsolatedBlockingTables {
pub(crate) fn register(&self, table: &Arc<BlockingTaskTable>) {
let mut tables = self.tables.lock();
tables.retain(|table| table.strong_count() > 0);
tables.push(Arc::downgrade(table));
}
pub(crate) fn report(&self) -> (usize, Vec<BlockingTaskSnapshot>) {
let tables = self.tables.lock().iter().filter_map(Weak::upgrade).collect::<Vec<_>>();
let mut still_running = 0;
let mut tasks = Vec::new();
for table in tables {
for entry in table.iter() {
let task = entry.value();
if matches!(
task.state,
BlockingTaskState::Running | BlockingTaskState::TimedOutStillRunning
) {
still_running += 1;
}
tasks.push(task.snapshot());
}
}
(still_running, tasks)
}
}
#[derive(Debug, Clone)]
pub(crate) struct BlockingTaskMeta {
pub id: BlockingTaskId,
pub name: Arc<str>,
pub kind: BlockingKind,
pub state: BlockingTaskState,
pub queued_at: Instant,
pub started_at: Option<Instant>,
}
impl BlockingTaskMeta {
pub(crate) fn snapshot(&self) -> BlockingTaskSnapshot {
let elapsed = self.started_at.unwrap_or(self.queued_at).elapsed();
BlockingTaskSnapshot {
id: self.id,
name: self.name.to_string(),
kind: self.kind,
state: self.state,
elapsed,
}
}
}
pub(crate) struct BlockingExecutorAggregate {
pub lane: BlockingLane,
pub max_concurrency: usize,
pub max_queue_depth: usize,
pub queued: usize,
pub running: usize,
pub timed_out_still_running: usize,
pub blocking_still_running: usize,
pub task_kinds: [(BlockingKind, usize, Duration); 3],
}
impl BlockingExecutorAggregate {
pub(crate) fn new(lane: BlockingLane, max_concurrency: usize, max_queue_depth: usize) -> Self {
Self {
lane,
max_concurrency,
max_queue_depth,
queued: 0,
running: 0,
timed_out_still_running: 0,
blocking_still_running: 0,
task_kinds: [
(BlockingKind::ShortIo, 0, Duration::ZERO),
(BlockingKind::CpuBound, 0, Duration::ZERO),
(BlockingKind::LongRunning, 0, Duration::ZERO),
],
}
}
pub(crate) fn record_kind(&mut self, kind: BlockingKind, elapsed: Duration) {
let index = match kind {
BlockingKind::ShortIo => 0,
BlockingKind::CpuBound => 1,
BlockingKind::LongRunning => 2,
};
self.task_kinds[index].1 = self.task_kinds[index].1.saturating_add(1);
self.task_kinds[index].2 = self.task_kinds[index].2.max(elapsed);
}
}
impl From<BlockingExecutorSnapshot> for BlockingExecutorAggregate {
fn from(snapshot: BlockingExecutorSnapshot) -> Self {
let mut aggregate = Self::new(snapshot.lane, snapshot.max_concurrency, snapshot.max_queue_depth);
aggregate.queued = snapshot.queued;
aggregate.running = snapshot.running;
aggregate.timed_out_still_running = snapshot.timed_out_still_running;
aggregate.blocking_still_running = snapshot.blocking_still_running;
for task in snapshot.tasks {
aggregate.record_kind(task.kind, task.elapsed);
}
aggregate
}
}
#[derive(Debug, Clone, Serialize)]
pub struct BlockingExecutorSnapshot {
pub name: String,
pub lane: BlockingLane,
pub max_concurrency: usize,
pub max_queue_depth: usize,
pub global_capacity: usize,
pub global_running: usize,
pub global_available: usize,
pub lane_reserved: usize,
pub lane_running: usize,
pub lane_borrowed: usize,
pub queued: usize,
pub running: usize,
pub timed_out_still_running: usize,
pub blocking_still_running: usize,
pub rejected: u64,
#[serde(with = "duration_millis")]
pub oldest_queue_wait: Duration,
pub tasks: Vec<BlockingTaskSnapshot>,
}
#[derive(Debug, Clone, Serialize)]
pub struct BlockingTaskSnapshot {
pub id: BlockingTaskId,
pub name: String,
pub kind: BlockingKind,
pub state: BlockingTaskState,
#[serde(with = "duration_millis")]
pub elapsed: Duration,
}
mod duration_millis {
use std::time::Duration;
use serde::Serializer;
pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u64(duration.as_millis() as u64)
}
}