C FFI 中 Arc 的惯用用法

问题描述 投票:0回答:1

我想为 C 代码实现一个 FFI,它调用用 Rust 编写的函数并将结果存储在 Rust 对象中以供以后使用。

  • Rust 代码返回对对象的引用,如
    Arc<T>
    ,其中
    T
    等于存储日期的 Rust 对象的类型。
  • C 代码必须创建新的 Rust 对象,读取其数据并在完成后释放内存。
  • 为此,我将
    Arc<T>
    转换为原始指针
    *const T
    并将该指针传递给 C 我了解到,由于 ABI 兼容性,C 可以将
    *const T
    解释为
    *void
    指针。 我关心的是 Arc 及其在 C 中引用的对象的惯用和内存安全处理。

到目前为止,我已经想出了这个

use std::sync::Arc;

pub struct my_struct {
    // some fields
}

pub fn init_my_struct(/*some parameters*/) -> Arc<my_struct> {
    // do stuff
    new_struct // new struct is of type Arc<my_struct> with field values initialized
}

pub extern "C" fn c_wrap_init_my_struct(/*some parameters*/) -> *const my_struct {
    // cast to raw pointer, hold by C
    Arc::into_raw(init_my_struct(/*some parameters*/))
}

pub extern "C" fn c_wrap_get_data_from_struct(my_struct_ptr: *const my_struct) -> /*some data type*/
{
    // reconstruct the rust object to extract a value
    // increment strong counter before re-constructing the object from pointer
    unsafe { Arc::increment_strong_count(my_struct_ptr) };
    // mys has ref count two due to increment above
    let mys = unsafe { Arc::from_raw(my_struct_ptr) };
    // do stuff to extract value from mys
    value // return value
} // mys goes out of scope and ref count of my_struct_ptr is decremented to one (2-1=1).

pub extern "C" fn c_wrap_drop_my_struct(my_struct_ptr: *const my_struct) {
    unsafe {
        assert!(!my_struct_ptr.is_null());
        drop(Arc::from_raw(my_struct_ptr)); // ref count drops to zero --> object is discarded.
    }
}

我不确定手动增加原始指针的引用计数是否安全且惯用?如果没有增量,getter

c_wrap_get_data_from_struct
中的引用计数将降至零,Rust 将销毁该对象(我不希望发生这种情况)。

c rust memory-leaks ffi raw-pointer
1个回答
0
投票

功能

c_wrap_get_data_from_struct
c_wrap_drop_my_struct
需要标记为
unsafe
。除此之外,您的代码是健全的(我认为这就是您所要求的,它绝对不安全)。不过,我建议使用
ManuallyDrop
而不是手动递增和递减参考计数器。对于一个在语义上不是正在发生的事情的情况,对于另一个不需要您思考并证明您需要维护的另一组安全不变量的情况:

use std::sync::Arc;
use std::mem::ManuallyDrop;

#[no_mangle]
pub unsafe extern "C" fn c_wrap_get_data_from_struct(my_struct_ptr: *const MyStruct) -> i32 {
    // reconstruct the Rust object to extract a value
    // wrap it in `ManuallyDrop` because we do not want to consume the `Arc` (decrement the reference count).
    let mys = ManuallyDrop::new(unsafe { Arc::from_raw(my_struct_ptr) });
    mys.value // extract and return value
}

另外一些注意事项:

  • init_my_struct
    应该是关联函数
    MyStruct::init
    (或
    new
  • 如果你想从 C 调用它们,你的包装器肯定需要阻止编译器用
    #[no_mangle]
  • 来破坏它们的名称
  • 在 Rust 中类型名称应该使用驼峰式大小写
    MyStruct
  • 不清楚为什么
    c_wrap_drop_my_struct
    断言指针不为空,但
    c_wrap_get_data_from_struct
© www.soinside.com 2019 - 2024. All rights reserved.