Rust async FFI

2023年 8月 21日 50.5k 0

前言

rust FFI(Foreign Function Interface),即允许rust同其他语言“交互”。近期在项目开发中,由于某些原因,同一个程序的部分模块是c++写的,部分模块是rust写的,rust需要调用c++接口,并且还是异步调用。看了一圈资料,都是同步调用,于是自行摸索了一下,总结了这篇文档给有需要的人。

同步调用

网上同步调用的例子很多,这里就简单提一下,不做过多的阐述,一些详细内容可以看文末参考资料。

简单的rust call c

代码位置:github.com/kkk-imm/Rus…

// 目录结构:
/*
├── README.md
└── example01
    ├── Cargo.lock
    ├── Cargo.toml
    ├── libcallee.so
    ├── callee.c
    ├── src
    │   └── main.rs
    ├── build.rs
    ├── target
    │   ├── CACHEDIR.TAG
    │   └── debug
    └── callee.o
*/ 
// example01/src/main.rs
use std::os::raw::c_int;

#[link(name="callee")]
extern "C" {
    fn sum(a: c_int, b: c_int) -> c_int;
}

fn main() {
    let k = unsafe { sum(2, 4) };
    println!("sum result is {}",k);
}
// example01/callee.c
// gcc -c -Wall -Werror -fpic callee.c
int sum(int a, int b) { return a + b; }
// build.rs
fn main() { 
    println!("cargo:rustc-link-search=native=."); 
    println!("cargo:rustc-link-lib=dylib=callee");
}

执行:(注意要带上 LD_LIBRARY_PATH)

LD_LIBRARY_PATH=. cargo run

异步FFI

这里所说的异步FFI是指,rust使用await语意,等待c接口返回结果。在我们项目开发过程中,rust模块位于上层,调用c接口,c接口内部会做一些耗时的IO操作。因此我们不能同步阻塞的去等待c接口返回结果。下面介绍两种实现方式。

自己封装future

假设我们的场景是,需要获取某个学生的信息,c代码对应的是存储引擎,rust侧则是查询模块。这里我们选用tokio作为rust async runtime。
代码位置:github.com/kkk-imm/Rus…

// src/main.rs
use std::future::Future;
use std::os::raw::{c_int, c_void};
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::task::{Context, Poll};
use std::thread::sleep;
use std::time;

// Record 学生信息,id、height
#[repr(C)]
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Record {
id: c_int,
height: c_int,
}

// 函数参数,第一个参数实际上对应的是Record 第二个参数是个closure。第一个参数实际上是closure执行时的参数。
pub type GetRecord = unsafe extern "C" fn(*mut T, *mut c_void);

extern "C" {
pub fn query(id: c_int, fnptr: GetRecord, closure: *mut c_void);
}

// hook 与GetRecord函数签名一致。我们期望c能够传递一个record 到我们的closure中,我们处理完之后,c再free record
unsafe extern "C" fn hook(record: *mut T, closure: *mut c_void)
where
F: FnOnce(*mut T),
{
let closure = Box::from_raw(closure as *mut F); // from_raw,使得closure 可以释放内存
closure(record);
}

// get_callback 返回一个函数指针
pub fn get_callback(_closure: &F) -> GetRecord
where
F: FnOnce(*mut T),
{
hook::
}

struct QueryFuture {
query_id: c_int, // 用于传给c接口,模拟作为查询参数
state: AtomicBool, // 用于通知Future是否结束
result: Option, // 用于返回查询结果
}

impl Future for QueryFuture {
type Output = Record;
fn poll(mut self: Pin, cx: &mut Context

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论