Skip to main content

lmflow/ffi/
mod.rs

1//! The C ABI layer: the implementation behind `include/lmflow/flow.h`.
2//!
3//! Conventions:
4//!  * Every exported function is wrapped in `catch_unwind` — letting a Rust panic cross an FFI
5//!    boundary is undefined behaviour.
6//!  * On failure the thread-local `lmflow_last_error` is set, so the caller can obtain a readable
7//!    reason.
8//!  * Structs that cross the boundary are `#[repr(C)]`, with the layout pinned from both sides by
9//!    `tests/abi_layout.rs` and `cpp/abi_assert.cc`.
10//!  * Every exported function null-checks its pointers and returns an error code or a default
11//!    value rather than crashing.
12//!
13//! On `missing_safety_doc`: the functions here are consumed by **C callers**, whose safety
14//! contract — pointer validity, ownership transfer, lifetimes — is authoritatively defined by the
15//! comments in `include/lmflow/flow.h`, which is the documentation C and C++ users actually read.
16//! Restating it here in Rust `# Safety` form would only create two descriptions that drift apart,
17//! so the lint is waived for the whole module.
18#![allow(clippy::missing_safety_doc)]
19
20use std::ffi::{c_char, CStr};
21use std::panic::{catch_unwind, AssertUnwindSafe};
22use std::sync::Arc;
23
24use crate::context::Context;
25use crate::graph::GraphInner;
26use crate::kernel::Contract;
27use crate::kernel_runner::KernelRunner;
28use crate::runtime::last_error;
29use crate::status::{code, Error};
30
31pub const ABI_VERSION: u32 = 4;
32
33pub const INVALID_ID: usize = usize::MAX;
34pub const LMFLOW_POLLER_BLOCK: i32 = 0;
35pub const LMFLOW_POLLER_DROP_OLDEST: i32 = 1;
36pub const LMFLOW_POLLER_DROP_NEWEST: i32 = 2;
37pub const LMFLOW_POLLER_LATEST: i32 = 3;
38
39// ---------------------------------------------------------------- 工具
40
41fn guard<F: FnOnce() -> i32>(f: F) -> i32 {
42    match catch_unwind(AssertUnwindSafe(f)) {
43        Ok(v) => v,
44        Err(_) => {
45            last_error::set("internal engine panic (caught at the FFI boundary)");
46            code::PANIC
47        }
48    }
49}
50
51fn guard_val<T, F: FnOnce() -> T>(default: T, f: F) -> T {
52    match catch_unwind(AssertUnwindSafe(f)) {
53        Ok(v) => v,
54        Err(_) => {
55            last_error::set("internal engine panic (caught at the FFI boundary)");
56            default
57        }
58    }
59}
60
61fn fail(e: Error) -> i32 {
62    last_error::set(&e.to_string());
63    e.code()
64}
65
66fn to_status(r: crate::status::Result<()>) -> i32 {
67    match r {
68        Ok(()) => code::OK,
69        Err(e) => fail(e),
70    }
71}
72
73unsafe fn cstr<'a>(p: *const c_char) -> Option<&'a str> {
74    if p.is_null() {
75        return None;
76    }
77    CStr::from_ptr(p).to_str().ok()
78}
79
80unsafe fn ctx_ref<'a>(c: *const LMFlowContext) -> Option<&'a Context> {
81    if c.is_null() {
82        None
83    } else {
84        Some(&*(c as *const Context))
85    }
86}
87
88unsafe fn ctx_mut<'a>(c: *mut LMFlowContext) -> Option<&'a mut Context> {
89    if c.is_null() {
90        None
91    } else {
92        Some(&mut *(c as *mut Context))
93    }
94}
95
96unsafe fn contract_mut<'a>(c: *mut LMFlowContract) -> Option<&'a mut Contract> {
97    if c.is_null() {
98        None
99    } else {
100        Some(&mut *(c as *mut Contract))
101    }
102}
103
104// ---------------------------------------------------------------- 不透明句柄
105
106#[repr(C)]
107pub struct LMFlowGraph {
108    _private: [u8; 0],
109}
110#[repr(C)]
111pub struct LMFlowInput {
112    _private: [u8; 0],
113}
114#[repr(C)]
115pub struct LMFlowPoller {
116    _private: [u8; 0],
117}
118#[repr(C)]
119pub struct LMFlowKernelRunner {
120    _private: [u8; 0],
121}
122#[repr(C)]
123pub struct LMFlowContext {
124    _private: [u8; 0],
125}
126#[repr(C)]
127pub struct LMFlowContract {
128    _private: [u8; 0],
129}
130
131/// The object behind a graph input-port handle; its lifetime follows the graph.
132pub struct InputHandle {
133    graph: Arc<GraphInner>,
134    edge: usize,
135}
136
137pub struct KernelRunnerHandle {
138    pub runner: KernelRunner,
139}
140
141// 按 API 域拆成子模块;`pub use` 把条目原样再导出,故 `lmflow::ffi::X` 路径不变。
142mod context;
143mod graph;
144mod kernel;
145mod kernel_runner;
146mod packet;
147
148pub use context::*;
149pub use graph::*;
150pub use kernel::*;
151pub use kernel_runner::*;
152pub use packet::*;