lmflow/executor/
delegating.rs1use std::collections::VecDeque;
2use std::sync::Mutex;
3use std::time::{Duration, Instant};
4
5use crate::config::StatsLevel;
6use crate::graph::NodeId;
7
8use super::stats::{ExecutorStats, ExecutorStatsSnapshot};
9
10struct QueuedTask {
11 enqueued_at: Option<Instant>,
12 node: NodeId,
13}
14
15pub struct DelegatingExecutor {
20 name: String,
21 queue: Mutex<VecDeque<QueuedTask>>,
22 stats: ExecutorStats,
23}
24
25impl DelegatingExecutor {
26 pub fn new(name: &str) -> Self {
27 Self::new_with_stats(name, StatsLevel::Full)
28 }
29
30 pub(crate) fn new_with_stats(name: &str, stats_level: StatsLevel) -> Self {
31 Self {
32 name: name.to_string(),
33 queue: Mutex::new(VecDeque::new()),
34 stats: ExecutorStats::new(stats_level),
35 }
36 }
37
38 pub fn name(&self) -> &str {
39 &self.name
40 }
41
42 pub fn submit(&self, node: NodeId) -> bool {
44 let mut queue = self.queue.lock().unwrap_or_else(|error| error.into_inner());
45 queue.push_back(QueuedTask {
46 enqueued_at: self.stats.full().then(Instant::now),
47 node,
48 });
49 self.stats.enqueued();
50 true
51 }
52
53 pub fn pending(&self) -> usize {
54 self.queue
55 .lock()
56 .unwrap_or_else(|error| error.into_inner())
57 .len()
58 }
59
60 pub fn take(&self) -> Option<NodeId> {
62 let mut queue = self.queue.lock().unwrap_or_else(|error| error.into_inner());
63 let task = queue.pop_front()?;
64 self.stats
65 .started(task.enqueued_at.map(|enqueued_at| enqueued_at.elapsed()));
66 Some(task.node)
67 }
68
69 pub fn complete(&self, execution: Option<Duration>) {
70 self.stats.completed(execution);
71 }
72
73 pub(crate) fn stats(&self) -> ExecutorStatsSnapshot {
74 self.stats.snapshot()
75 }
76
77 pub(crate) fn has_pending_work(&self) -> bool {
78 !self
79 .queue
80 .lock()
81 .unwrap_or_else(|error| error.into_inner())
82 .is_empty()
83 || self.stats.has_running()
84 }
85
86 pub(crate) fn queued_nodes(&self) -> Vec<NodeId> {
87 self.queue
88 .lock()
89 .unwrap_or_else(|error| error.into_inner())
90 .iter()
91 .map(|task| task.node)
92 .collect()
93 }
94
95 pub fn clear(&self) {
97 let mut queue = self.queue.lock().unwrap_or_else(|error| error.into_inner());
98 let count = queue.len();
99 queue.clear();
100 self.stats.dropped(count);
101 }
102
103 pub fn reset_stats(&self) {
104 self.stats.reset();
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn queues_fifo_and_never_rejects() {
114 let executor = DelegatingExecutor::new("host");
115 assert!(executor.submit(7));
116 assert!(executor.submit(8));
117 assert_eq!(executor.pending(), 2);
118 assert_eq!(executor.take(), Some(7));
119 assert_eq!(executor.take(), Some(8));
120 assert_eq!(executor.take(), None);
121 }
122
123 #[test]
124 fn clear_drops_pending() {
125 let executor = DelegatingExecutor::new("host");
126 executor.submit(1);
127 executor.submit(2);
128 executor.clear();
129 assert_eq!(executor.pending(), 0);
130 }
131}