我正在尝试将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
指向正确线程的指针。
答案 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;
}
这里的问题是,这个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
应该在对绑定进行安全,高级抽象的基础上完成。