Impl Send用于Bindgen生成的指针类型

时间:2018-09-27 06:52:42

标签: rust ffi

我正在尝试将FFI指针类型发送到另一个线程。它指向的结构是由bindgen from opensles-sys

生成的

这是我的包装器结构:

pub struct AndroidAudioIO {
    sl_output_buffer_queue: NonNull<SLObjectItf>,
}
unsafe impl Send for AndroidAudioIO{}

SLObjectItf类型是*const *const SLObjectItf_的别名,其定义是由bindgen生成的。这是FFI函数指针的集合。

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SLObjectItf_ {
    pub Realize: ::std::option::Option<
        unsafe extern "C" fn(self_: SLObjectItf, async: SLboolean) -> SLresult,
    >,
    // More of the same pattern, only extern "C" function pointers, no data
}

我尝试添加unsafe impl Send for SLObjectItf_{}和其他变体而无济于事。

error[E0277]: `std::ptr::NonNull<*const *const opensles::bindings::SLObjectItf_>` cannot be shared between threads safely
  --> src/lib.rs:12:1
   |
12 | / lazy_static! {
13 | | static ref engine:Option<mynoise::Engine<Box<audio::AndroidAudioIO>>> = None;
14 | | }
   | |_^ `std::ptr::NonNull<*const *const opensles::bindings::SLObjectItf_>` cannot be shared between threads safely
   |
   = help: within `audio::AndroidAudioIO`, the trait `std::marker::Sync` is not implemented for `std::ptr::NonNull<*const *const opensles::bindings::SLObjectItf_>`
   = note: required because it appears within the type `audio::AndroidAudioIO`

我只关心Send而不关心Sync的原因是,单个线程(RT音频线程)与此结构进行交互,但是它是在另一个线程上创建的,因此需要Send指向正确线程的指针。

1 个答案:

答案 0 :(得分:2)

以下代码重现了相同的问题(假设Engine仅在类型级别保留AndroidAudioIO,以便以后可以产生这样的处理程序;它也可以通过直接组合来工作)

#[macro_use]
extern crate lazy_static;

use std::marker::PhantomData;
use std::ptr::NonNull;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SLObjectItf;

pub struct AndroidAudioIO {
    sl_output_buffer_queue: NonNull<SLObjectItf>,
}
unsafe impl Send for AndroidAudioIO {}

#[derive(Debug)]
pub struct Engine<T>(PhantomData<T>);

lazy_static! {
    static ref engine: Option<Engine<AndroidAudioIO>> = None;
}

Playground

这里的问题是,这个Engine实体位于全局静态变量中,这立即使它在所有线程之间共享。这需要Sync,但是Engine没有实现Sync,因为AudioAndroidIO没有实现Sync。实际上,无论引擎是将音频I / O处理程序作为属性包含还是仅在类型级别存在信息,即使PhantomData都直接从其参数类型继承这些特征实现。引用文档:

impl<T: ?Sized> Send for PhantomData<T>
where
    T: Send,
impl<T: ?Sized> Sync for PhantomData<T>
where
    T: Sync

这很可能是Engine可以拥有Sync的情况(尽管PhantomData选择这种安全的行为是避免对内部类型的假设)。要解决此问题,请首先确保绝对确保Engine是线程安全的。然后,为此手动实施Sync

unsafe impl<T> Sync for Engine<T> {}

  

我尝试添加unsafe impl Send for SLObjectItf_{}和其他变体而无济于事。

好吧,无论如何,这通常都是Bad Idea™。实现Send和/或Sync应该在对绑定进行安全,高级抽象的基础上完成。