Skip to main content

lmflow/graph/
api.rs

1//! Host-facing and FFI-facing `GraphInner` facade.
2
3use super::*;
4
5impl GraphInner {
6    pub fn add_observer(
7        &self,
8        port: &str,
9        cb: unsafe extern "C" fn(*mut c_void, crate::ffi::LMFlowPacket),
10        user: *mut c_void,
11        observe_timestamp_bounds: bool,
12    ) -> Result<()> {
13        let edge = *self
14            .output_by_name
15            .get(port)
16            .ok_or_else(|| Error::NotFound(format!("graph output port `{port}` does not exist")))?;
17        if observe_timestamp_bounds {
18            self.edges[edge]
19                .has_timestamp_bound_subscriber
20                .store(true, Ordering::Relaxed);
21        }
22        self.edges[edge]
23            .observers
24            .lock()
25            .expect("observer list lock poisoned")
26            .push(Observer::C {
27                cb,
28                user,
29                observe_timestamp_bounds,
30            });
31        Ok(())
32    }
33
34    /// Rust 宿主的推模式订阅。回调在**派发该包的线程**上执行(可能是池线程),
35    /// 因此必须 `Send + Sync`;回调内不得再调 graph 的生命周期接口。
36    pub fn add_observer_fn(
37        &self,
38        port: &str,
39        callback: Arc<dyn Fn(&Packet) + Send + Sync>,
40        observe_timestamp_bounds: bool,
41    ) -> Result<()> {
42        let edge = *self
43            .output_by_name
44            .get(port)
45            .ok_or_else(|| Error::NotFound(format!("graph output port `{port}` does not exist")))?;
46        if observe_timestamp_bounds {
47            self.edges[edge]
48                .has_timestamp_bound_subscriber
49                .store(true, Ordering::Relaxed);
50        }
51        self.edges[edge]
52            .observers
53            .lock()
54            .expect("observer list lock poisoned")
55            .push(Observer::Rust {
56                callback,
57                observe_timestamp_bounds,
58            });
59        Ok(())
60    }
61
62    pub fn nodes_len(&self) -> usize {
63        self.nodes.len()
64    }
65
66    pub fn node_name_at(&self, i: usize) -> Option<&str> {
67        self.nodes.get(i).map(|node| node.name.as_str())
68    }
69
70    pub fn node_input_ports_len(&self, node: usize) -> usize {
71        self.nodes.get(node).map_or(0, |value| value.in_ports.len())
72    }
73
74    pub fn node_input_port_name_at(&self, node: usize, port: usize) -> Option<&str> {
75        self.nodes.get(node)?.in_ports.name(port)
76    }
77
78    pub fn input_port_name_at(&self, i: usize) -> Option<&str> {
79        self.graph_inputs
80            .get(i)
81            .map(|&edge| self.edges[edge].name.as_str())
82    }
83
84    pub fn output_port_name_at(&self, i: usize) -> Option<&str> {
85        self.graph_outputs
86            .get(i)
87            .map(|&edge| self.edges[edge].name.as_str())
88    }
89
90    pub fn num_input_ports(&self) -> usize {
91        self.graph_inputs.len()
92    }
93
94    pub fn num_output_ports(&self) -> usize {
95        self.graph_outputs.len()
96    }
97
98    pub fn edge_id_by_name(&self, name: &str) -> Option<EdgeId> {
99        self.edge_by_name.get(name).copied()
100    }
101
102    pub fn input_edge_by_name(&self, name: &str) -> Option<EdgeId> {
103        self.input_by_name.get(name).copied()
104    }
105
106    pub fn queue_depth_by_name(&self, name: &str) -> Option<usize> {
107        Some(self.queue_depth(self.edge_id_by_name(name)?))
108    }
109
110    pub fn dropped_by_name(&self, name: &str) -> Option<u64> {
111        Some(self.edges[self.edge_id_by_name(name)?].dropped_count())
112    }
113
114    pub fn send_by_edge(&self, edge: EdgeId, packet: Packet, blocking: bool) -> Result<()> {
115        self.send(edge, packet, blocking)
116    }
117
118    pub(crate) fn watermark_backpressure_stats(
119        &self,
120        edge: EdgeId,
121    ) -> WatermarkBackpressureStatsSnapshot {
122        let edge = &self.edges[edge];
123        let stats = edge.watermark_backpressure.snapshot(self.epoch_us());
124        WatermarkBackpressureStatsSnapshot {
125            port_name: edge.name.clone(),
126            packet_limit: self.shared.config.max_queued_packets,
127            total_queued_packets: self.shared.total_queued(),
128            blocked: stats.blocked,
129            active_waiters: stats.active_waiters,
130            blocked_for_us: stats.blocked_for_us,
131            block_events: stats.block_events,
132            total_blocked_us: stats.total_blocked_us,
133        }
134    }
135
136    pub fn close_edge_pub(&self, edge: EdgeId) {
137        self.close_edge(edge);
138        self.set_state_draining_if_all_inputs_closed();
139    }
140
141    pub fn state_pub(&self) -> State {
142        self.state()
143    }
144
145    pub fn dump_pub(&self) -> String {
146        self.dump()
147    }
148
149    pub fn node_stats_pub(&self, i: usize) -> Option<NodeStatsSnapshot> {
150        self.node_stats(i)
151    }
152
153    pub fn side_packets_mut(&self) -> &Mutex<BTreeMap<String, Packet>> {
154        &self.side_packets
155    }
156
157    pub fn start_pub(self: &Arc<Self>) -> Result<()> {
158        self.start()
159    }
160
161    pub fn wait_done_pub(&self) -> Result<()> {
162        self.wait_done(None)
163    }
164
165    pub fn pump_step_pub(&self) -> bool {
166        let generation = self.wakeup_generation.load(Ordering::Acquire);
167        let progressed = self.pump_step();
168        if !progressed {
169            self.wakeup_pending.store(false, Ordering::Release);
170            if self.wakeup_generation.load(Ordering::Acquire) != generation
171                || self.delegated_tasks_pending()
172            {
173                self.request_wakeup();
174            }
175        }
176        progressed
177    }
178
179    pub fn all_nodes_closed_pub(&self) -> bool {
180        self.all_nodes_closed()
181    }
182
183    pub fn graph_inputs(&self) -> &[EdgeId] {
184        &self.graph_inputs
185    }
186
187    pub(crate) fn remaining_for_poller(
188        &self,
189        deadline: Option<std::time::Instant>,
190    ) -> Option<std::time::Duration> {
191        self.remaining(deadline)
192    }
193
194    pub(crate) fn wait_activity_since_pub(&self, before: u64, duration: std::time::Duration) {
195        self.wait_activity_since(before, duration);
196    }
197
198    pub(crate) fn activity_gen_pub(&self) -> u64 {
199        self.activity_gen()
200    }
201
202    pub(crate) fn is_idle_pub(&self) -> bool {
203        self.is_idle()
204    }
205
206    pub fn pause(&self) {
207        self.paused.store(true, Ordering::SeqCst);
208    }
209
210    /// 恢复调度,并重扫一遍就绪节点 —— 暂停期间到达的包否则会一直躺着。
211    pub fn resume(&self) {
212        self.paused.store(false, Ordering::SeqCst);
213        for node in 0..self.nodes.len() {
214            self.schedule_node(node);
215        }
216        self.notify_activity();
217    }
218
219    pub fn is_paused(&self) -> bool {
220        self.paused.load(Ordering::SeqCst)
221    }
222
223    pub fn executor_names(&self) -> Vec<&str> {
224        self.executors
225            .iter()
226            .map(|executor| executor.name())
227            .collect()
228    }
229
230    pub(super) fn executor_stats(&self, name: &str) -> Option<ExecutorStatsSnapshot> {
231        let executor = self
232            .executors
233            .iter()
234            .find(|executor| executor.name() == name)?;
235        let stats = executor.stats();
236        let capacity = if executor.is_delegating() {
237            1
238        } else {
239            executor.num_threads()
240        };
241        let mut queued_counts = std::collections::BTreeMap::<NodeId, usize>::new();
242        for node in executor.queued_nodes() {
243            *queued_counts.entry(node).or_default() += 1;
244        }
245        let mut queued_nodes = queued_counts.into_iter().collect::<Vec<_>>();
246        queued_nodes.sort_by(|(left_node, left_count), (right_node, right_count)| {
247            right_count.cmp(left_count).then_with(|| {
248                self.nodes[*left_node]
249                    .name
250                    .cmp(&self.nodes[*right_node].name)
251            })
252        });
253        Some(ExecutorStatsSnapshot {
254            queued: stats.queued,
255            running: stats.running,
256            peak_queued: stats.peak_queued,
257            completed: stats.completed,
258            total_wait_us: stats.total_wait_us,
259            total_execution_us: stats.total_execution_us,
260            queued_for_us: stats.queued_for_us,
261            saturated: stats.queued > 0 && stats.running >= capacity,
262            queued_nodes: queued_nodes
263                .into_iter()
264                .take(5)
265                .map(|(node, count)| format!("{} ({count})", self.nodes[node].name))
266                .collect(),
267        })
268    }
269
270    pub(crate) fn shutdown_executors_pub(&self) {
271        self.shutdown_executors();
272    }
273
274    /// 关停所有线程池并 join。委托执行器是 no-op。**必须在动节点之前做**。
275    pub(super) fn shutdown_executors(&self) {
276        for executor in &self.executors {
277            executor.shutdown();
278        }
279    }
280}