Impl Send для сгенерированного Bindgen типа указателя - PullRequest
0 голосов
/ 27 сентября 2018

Я пытаюсь отправить тип указателя FFI в другой поток.Структура, на которую он указывает, была сгенерирована bindgen из 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 голосов
/ 27 сентября 2018

Следующий код воспроизводит ту же проблему (предполагая, что 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.Действительно, независимо от того, содержит ли движок обработчик ввода-вывода аудио в качестве атрибута или эта информация существует только на уровне типа, даже PhantomData наследует эти реализации признака непосредственно от своего типа параметра.Цитирование из документов:

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

Вероятно, это тот случай, когда Engine можно иметь Sync (хотя PhantomData выбирает это безопасное поведение, избегая предположений о внутреннем типе).Чтобы решить эту проблему, сначала убедитесь, что absolute уверен, что Engine является потокобезопасным.Затем вручную добавьте Sync для этого.

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

Я попытался добавить unsafe impl Send for SLObjectItf_{} и другие варианты безрезультатно.

Ну,это вообще было бы Плохой Идеей ™ так или иначе.Реализация Send и / или Sync должна выполняться поверх безопасной высокоуровневой абстракции ваших привязок.

...