rustthread-safety

Why does this trait object not implement Send, despite requiring `+ Sync`?


I'm writing an audio synthesiser and want to use the cpal crate to output audio. That happens by spawning an output thread, whose "data generation" function needs to implement FnMut(&mut [T], &OutputCallbackInfo) + Send + 'static. The OutputCallbackInfo is irrelevant to the question at hand.

To generate the audio, I have an AudioPipeline struct:

pub struct AudioPipeline {
    producer: Box<dyn AudioProducer + Sync>,
    modifiers: Vec<Box<dyn AudioModifier + Sync>>,
}

where AudioProducer and AudioModifier are some other traits. In main, I create such a pipeline, and then send it over to spawned thread using a moving closure:

let pipeline = AudioPipeline::new(Box::new(sine), vec![Box::new(asdr)]);
let stream = device
    .build_output_stream(
        ...
        move |data: &mut [u8], callback_info: &cpal::OutputCallbackInfo| {
            ...
            pipeline.recv();
            ...
        },
        ...
    );

Here, pipeline.recv() is a method on AudioPipeline that takes &mut self.

The code doesn't compile, giving me this error instead:

(dyn AudioProducer + Sync + 'static) cannot be sent between threads safely
the trait bound Unique<(dyn AudioProducer + Sync + 'static)>: Send is not satisfied
required for Unique<(dyn AudioProducer + Sync + 'static)> to implement Send

I gather that the issue has something to do with using Box? But I find this error a bit hard to grasp: after all, the docs explicitly say "[Unique<T>] implements Send/Sync if T is Send/Sync." Surely by requiring that the dyn AudioProducer is also Sync, it should definitely be Send, right? I'm probably missing some thread-safe subtelty here.


Solution

  • Sync does not imply Send.

    They are related but its not like Sync is a sub type of Send or anything like that. There are types that are Send + !Sync and others that are Sync + !Send; they each have their own meanings.

    So your trait object needs to be specified Send + Sync - very common in multi-threaded code.