首页 > 解决方案 > 如何制作静态容器以从传递给 C 代码的回调中引用可变实例?

问题描述

对于我需要编写的库,我有一个表示用 C 编写的服务的结构:

struct RustService {
    id: u8, /* it's me setting the id in the first place */
    service: *mut c_void,
    status: *mut c_void,
    value: Option<u32>,
    /* various fields here */
}

impl RustService {
    fn new() -> /* can be wrapped*/RustService {
       /* ... */
    }
}

我有几个回调传递给 C 库,如下所示:

#[no_mangle]
extern "C" fn callback_handle(service: *mut c_void, size: *mut u32, buffer: *mut u8)
{
    let id: u8 = c_library_get_id(service);
    let instance: &mut RustService = /* get that from static container based on id */

    if size = mem::size_of::<u32>() as u32 {
        let value = buffer.cast::<u32>().read();
        instance.value = Some(value);
    } else {
        panic!("Invalid length passed.");
    }
}

我正在寻找的是一种经过验证的/良好的实践,可以在这里应用,不会导致死锁,我的回调可以使用 C 中的库引用正确的实例。C 中的那个库可以运行多个实例,但事实并非如此重要的。或者,我可以接受在这种情况下可以工作的单个实例模式。

其他方法RustService需要调用例如具有以下签名的函数(来自 bindgen):

extern "C" pub fn clibCoreWork(status: *mut clibStatus, service: *mut clibService) -> u32;

并且该函数的核心可能会决定调用上面提到的这些回调。

即使在单线程中,内部互斥锁死锁,我采取的方法也让我大吃一惊:

lazy_static! {
    static ref MY_INSTANCES: Mutex<HashMap<u8, Weak<Mutex<RustService>>>> = Mutex::new(HashMap::new());
}

我挖了互联网,但其他 Rustaceans 似乎没有发布任何关于此类问题的信息。

注意:我无法更改我正在使用的 C 库中的基础类型。在理想的世界中,它可以让我存储指向用户数据的指针,并且我会粗暴地将其转换为不安全代码中的可变引用。

标签: rustthread-safetyffi

解决方案


推荐阅读