use std::sync::Arc;
use std::time::Instant;
use a2a_protocol_types::task::TaskId;
use tokio_util::sync::CancellationToken;
use super::super::{CancellationEntry, ExecutorTurn, RequestHandler};
use super::decisions::{evict_aged_token, token_aged, token_still_evictable};
impl RequestHandler {
pub(super) async fn register_cancellation_token(
&self,
task_id: &TaskId,
token: CancellationToken,
) -> Arc<ExecutorTurn> {
let (cancelled_ids, aged_candidates) = self.collect_stale_candidates().await;
let mut stale_ids = cancelled_ids;
stale_ids.extend(self.confirm_aged_candidates(aged_candidates).await);
if !stale_ids.is_empty() {
self.evict_stale_tokens(&stale_ids).await;
}
let turn = Arc::new(ExecutorTurn::default());
let mut tokens = self.cancellation_tokens.write().await;
tokens.insert(
task_id.clone(),
CancellationEntry {
token,
created_at: Instant::now(),
turn: Arc::clone(&turn),
},
);
turn
}
async fn collect_stale_candidates(&self) -> (Vec<TaskId>, Vec<TaskId>) {
let tokens = self.cancellation_tokens.read().await;
if tokens.len() >= self.limits.max_cancellation_tokens {
let now = Instant::now();
let mut cancelled = Vec::new();
let mut aged = Vec::new();
for (id, entry) in tokens.iter() {
if entry.token.is_cancelled() {
cancelled.push(id.clone());
} else if token_aged(
now.duration_since(entry.created_at),
self.limits.max_token_age,
) {
aged.push(id.clone());
}
}
drop(tokens);
(cancelled, aged)
} else {
(Vec::new(), Vec::new())
}
}
async fn confirm_aged_candidates(&self, aged: Vec<TaskId>) -> Vec<TaskId> {
let mut evictable = Vec::new();
for id in aged {
let queue_live = self.event_queue_manager.has_queue(&id).await;
if evict_aged_token(queue_live) {
evictable.push(id);
}
}
evictable
}
async fn evict_stale_tokens(&self, stale_ids: &[TaskId]) {
let now = Instant::now();
let mut tokens = self.cancellation_tokens.write().await;
for id in stale_ids {
let evict = tokens
.get(id)
.is_some_and(|e| token_still_evictable(e, now, self.limits.max_token_age));
if evict {
tokens.remove(id);
}
}
}
}