robbert-vdh / nih-plug

Rust VST3 and CLAP plugin framework and plugins - because everything is better when you do it yourself
ISC License
1.72k stars 150 forks source link

How to use state variables which don't implement Sync trait? #32

Closed teodly closed 1 year ago

teodly commented 2 years ago

Hi, I'm trying to implement a variable clock delay plugin in the NIH-plug framework. Here's the WIP: https://github.com/teowoz/nih-plug/tree/varispeed-delay

The problem is, it looks like all members of plugin state struct must implement Sync trait, but the library I want to use (rubato) doesn't implement it:

$ cargo xtask bundle varispeed_delay --release
    Finished release [optimized] target(s) in 0.10s
     Running `target/release/xtask bundle varispeed_delay --release`
   Compiling varispeed_delay v0.1.0 (/home/teo/Projects/nih-plug/plugins/varispeed_delay)
error[E0277]: `(dyn rubato::asynchro::SincInterpolator<f64> + 'static)` cannot be shared between threads safely
  --> plugins/varispeed_delay/src/lib.rs:84:6
   |
84 | impl Plugin for VariSpeedDelay {
   |      ^^^^^^ `(dyn rubato::asynchro::SincInterpolator<f64> + 'static)` cannot be shared between threads safely
   |
   = help: the trait `Sync` is not implemented for `(dyn rubato::asynchro::SincInterpolator<f64> + 'static)`
   = note: required because of the requirements on the impl of `Sync` for `Unique<(dyn rubato::asynchro::SincInterpolator<f64> + 'static)>`
   = note: required because it appears within the type `Box<(dyn rubato::asynchro::SincInterpolator<f64> + 'static)>`
   = note: required because it appears within the type `SincFixedIn<f64>`
note: required because it appears within the type `VariSpeedDelay`
  --> plugins/varispeed_delay/src/lib.rs:25:8
   |
25 | struct VariSpeedDelay {
   |        ^^^^^^^^^^^^^^
note: required by a bound in `nih_plug::prelude::Plugin`
  --> /home/teo/Projects/nih-plug/src/plugin.rs:26:36
   |
26 | pub trait Plugin: Default + Send + Sync + 'static {
   |                                    ^^^^ required by this bound in `nih_plug::prelude::Plugin`

error[E0277]: `(dyn rubato::asynchro::SincInterpolator<f64> + 'static)` cannot be shared between threads safely
   --> plugins/varispeed_delay/src/lib.rs:189:6
    |
189 | impl Vst3Plugin for VariSpeedDelay {
    |      ^^^^^^^^^^ `(dyn rubato::asynchro::SincInterpolator<f64> + 'static)` cannot be shared between threads safely
    |
    = help: the trait `Sync` is not implemented for `(dyn rubato::asynchro::SincInterpolator<f64> + 'static)`
    = note: required because of the requirements on the impl of `Sync` for `Unique<(dyn rubato::asynchro::SincInterpolator<f64> + 'static)>`
    = note: required because it appears within the type `Box<(dyn rubato::asynchro::SincInterpolator<f64> + 'static)>`
    = note: required because it appears within the type `SincFixedIn<f64>`
note: required because it appears within the type `VariSpeedDelay`
   --> plugins/varispeed_delay/src/lib.rs:25:8
    |
25  | struct VariSpeedDelay {
    |        ^^^^^^^^^^^^^^
note: required by a bound in `nih_plug::prelude::Vst3Plugin`
   --> /home/teo/Projects/nih-plug/src/plugin.rs:201:23
    |
201 | pub trait Vst3Plugin: Plugin {
    |                       ^^^^^^ required by this bound in `nih_plug::prelude::Vst3Plugin`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `varispeed_delay` due to 2 previous errors
Error: Could not build varispeed_delay

The compiler says that resampler struct cannot be shared between threads safely, but it won't be used in multiple threads at once anyway, so why is the Sync trait required? Is there way around it? Sorry if it's a stupid question, I'm just starting to learn Rust.

robbert-vdh commented 2 years ago

Everything you store in your plugin struct needs to be Sync. The issue you're running into is that the type you're using contains a boxed trait object which does not have the Send + Sync trait bounds, thus making it non-Sync. Since you'll have exclusive mutable access in the initialize, reset, and process functions you should be able to do whatever you want with your struct's fields, but you still need to be able to store them in a way that's Send+Sync. There's a proposed and currently nightly-only API for this through the Exclusive` struct, but outside of reimplementing that yourself or using a crate I don't know if there's another alternative here.

robbert-vdh commented 1 year ago

Plugin and most other traits now long require Send as of a couple weeks ago (https://github.com/robbert-vdh/nih-plug/commit/f9bdaffc625cbb5cbf56c14b995c4c9d356dff09 for Plugin). Sync is no longer needed.