Skip to content

Commit

Permalink
Replace direct handler access with callbacks in order to hide the act…
Browse files Browse the repository at this point in the history
…ual self-referential lifetime
  • Loading branch information
prokopyl committed Apr 30, 2024
1 parent 4252854 commit cd6e3aa
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 77 deletions.
3 changes: 1 addition & 2 deletions extensions/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@
//! let mut plugin_instance: PluginInstance<MyHost> = /* ... */
//! # utils::get_working_instance(|_| MyHostShared { state_ext: OnceLock::new() }, |shared| MyHostMainThread { is_state_dirty: false, shared })?;
//!
//! let state_ext = plugin_instance.shared_handler().state_ext
//! .get()
//! let state_ext = plugin_instance.use_shared_handler(|h| h.state_ext.get())
//! .expect("Plugin is not yet instantiated")
//! .expect("Plugin does not implement State extension");
//!
Expand Down
9 changes: 2 additions & 7 deletions host/examples/cpal/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ pub fn run(plugin: FoundBundlePlugin) -> Result<(), Box<dyn Error>> {
let _stream = activate_to_stream(&mut instance)?;

let gui = instance
.handler()
.gui
.use_handler(|h| h.gui)
.map(|gui| Gui::new(gui, &mut instance.plugin_handle()));

let gui = gui.and_then(|gui| Some((gui.needs_floating()?, gui)));
Expand Down Expand Up @@ -235,11 +234,7 @@ fn run_gui_embedded(

let uses_logical_pixels = gui.configuration.unwrap().api_type.uses_logical_size();

let main_thread = instance.handler();

let timers = main_thread
.timer_support
.map(|ext| (main_thread.timers.clone(), ext));
let timers = instance.use_handler(|h| h.timer_support.map(|ext| (h.timers.clone(), ext)));

event_loop.run(move |event, target| {
while let Ok(message) = receiver.try_recv() {
Expand Down
4 changes: 2 additions & 2 deletions host/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,10 @@
//! /* ... */
//!
//! // Let's check if the plugin requested a callback, by accessing our shared host data.
//! let shared: &MyHostShared = plugin_instance.shared_handler();
//! let callback_requested: &AtomicBool = plugin_instance.use_shared_handler(|h| &h.callback_requested);
//!
//! // This fetches the previous value and sets it to false in a single atomic operation.
//! if shared.callback_requested.fetch_and(false, Ordering::SeqCst) {
//! if callback_requested.fetch_and(false, Ordering::SeqCst) {
//! plugin_instance.call_on_main_thread_callback();
//! }
//!
Expand Down
21 changes: 15 additions & 6 deletions host/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,22 +132,31 @@ impl<H: HostHandlers> PluginInstance<H> {
}

#[inline]
pub fn shared_handler(&self) -> &<H as HostHandlers>::Shared<'_> {
self.inner.get().wrapper().shared()
pub fn use_shared_handler<'s, R>(
&'s self,
access: impl for<'a> FnOnce(&'s <H as HostHandlers>::Shared<'a>) -> R,
) -> R {
access(self.inner.get().wrapper().shared())
}

#[inline]
pub fn handler(&self) -> &<H as HostHandlers>::MainThread<'_> {
pub fn use_handler<'s, R>(
&'s self,
access: impl for<'a> FnOnce(&'s <H as HostHandlers>::MainThread<'a>) -> R,
) -> R {
// SAFETY: we take &self, the only reference to the wrapper on the main thread, therefore
// we can guarantee there are no mutable reference anywhere
unsafe { self.inner.get().wrapper().main_thread().as_ref() }
unsafe { access(self.inner.get().wrapper().main_thread().as_ref()) }
}

#[inline]
pub fn handler_mut(&mut self) -> &mut <H as HostHandlers>::MainThread<'_> {
pub fn use_handler_mut<'s, R>(
&'s mut self,
access: impl for<'a> FnOnce(&'s <H as HostHandlers>::MainThread<'a>) -> R,
) -> R {
// SAFETY: we take &mut self, the only reference to the wrapper on the main thread, therefore
// we can guarantee there are no mutable reference anywhere
unsafe { self.inner.get().wrapper().main_thread().as_mut() }
unsafe { access(self.inner.get().wrapper().main_thread().as_mut()) }
}

#[inline]
Expand Down
115 changes: 65 additions & 50 deletions host/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::fmt::{Debug, Display, Formatter};
use std::marker::PhantomData;
use std::sync::Arc;

use crate::extensions::wrapper::HostWrapper;
use crate::plugin::instance::PluginInstanceInner;
pub use clack_common::process::*;

Expand All @@ -24,7 +25,7 @@ pub enum PluginAudioProcessor<H: HostHandlers> {
Poisoned,
}

impl<'a, H: 'a + HostHandlers> PluginAudioProcessor<H> {
impl<H: HostHandlers> PluginAudioProcessor<H> {
#[inline]
pub fn as_started(&self) -> Result<&StartedPluginAudioProcessor<H>, HostError> {
match self {
Expand Down Expand Up @@ -62,29 +63,36 @@ impl<'a, H: 'a + HostHandlers> PluginAudioProcessor<H> {
}

#[inline]
pub fn shared_handler(&self) -> &<H as HostHandlers>::Shared<'_> {
pub fn use_shared_handler<'s, R>(
&'s self,
access: impl for<'a> FnOnce(&'s <H as HostHandlers>::Shared<'a>) -> R,
) -> R {
match self {
Poisoned => panic!("Plugin audio processor was poisoned"),
Started(s) => s.shared(),
Stopped(s) => s.shared(),
Started(s) => s.use_shared_handler(access),
Stopped(s) => s.use_shared_handler(access),
}
}

#[inline]
pub fn handler(&self) -> &<H as HostHandlers>::AudioProcessor<'_> {
pub fn use_handler<'s, R>(
&'s self,
access: impl for<'a> FnOnce(&'s <H as HostHandlers>::AudioProcessor<'a>) -> R,
) -> R {
match self {
Poisoned => panic!("Plugin audio processor was poisoned"),
Started(s) => s.handler(),
Stopped(s) => s.handler(),
Started(s) => s.use_handler(access),
Stopped(s) => s.use_handler(access),
}
}

#[inline]
pub fn handler_mut(&mut self) -> &mut <H as HostHandlers>::AudioProcessor<'_> {
pub fn use_handler_mut<'s, R>(
&'s mut self,
access: impl for<'a> FnOnce(&'s mut <H as HostHandlers>::AudioProcessor<'a>) -> R,
) -> R {
match self {
Poisoned => panic!("Plugin audio processor was poisoned"),
Started(s) => s.handler_mut(),
Stopped(s) => s.handler_mut(),
Started(s) => s.use_handler_mut(access),
Stopped(s) => s.use_handler_mut(access),
}
}

Expand Down Expand Up @@ -220,14 +228,14 @@ impl<'a, H: 'a + HostHandlers> PluginAudioProcessor<H> {
}
}

impl<'a, H: 'a + HostHandlers> From<StartedPluginAudioProcessor<H>> for PluginAudioProcessor<H> {
impl<H: HostHandlers> From<StartedPluginAudioProcessor<H>> for PluginAudioProcessor<H> {
#[inline]
fn from(p: StartedPluginAudioProcessor<H>) -> Self {
Started(p)
}
}

impl<'a, H: 'a + HostHandlers> From<StoppedPluginAudioProcessor<H>> for PluginAudioProcessor<H> {
impl<H: HostHandlers> From<StoppedPluginAudioProcessor<H>> for PluginAudioProcessor<H> {
#[inline]
fn from(p: StoppedPluginAudioProcessor<H>) -> Self {
Stopped(p)
Expand Down Expand Up @@ -305,40 +313,38 @@ impl<H: HostHandlers> StartedPluginAudioProcessor<H> {
}

#[inline]
pub fn shared(&self) -> &<H as HostHandlers>::Shared<'_> {
self.inner.as_ref().unwrap().wrapper().shared()
fn wrapper(&self) -> &HostWrapper<H> {
self.inner.as_ref().unwrap().wrapper()
}

#[inline]
pub fn handler(&self) -> &<H as HostHandlers>::AudioProcessor<'_> {
// SAFETY: we take &self, the only reference to the wrapper on the audio thread, therefore
// we can guarantee there are no mutable references anywhere
// PANIC: This struct exists, therefore we are guaranteed the plugin is active
unsafe {
self.inner
.as_ref()
.unwrap()
.wrapper()
.audio_processor()
.unwrap()
.as_ref()
}
pub fn use_shared_handler<'s, R>(
&'s self,
access: impl for<'a> FnOnce(&'s <H as HostHandlers>::Shared<'a>) -> R,
) -> R {
access(self.wrapper().shared())
}

#[inline]
pub fn handler_mut(&mut self) -> &mut <H as HostHandlers>::AudioProcessor<'_> {
pub fn use_handler<'s, R>(
&'s self,
access: impl for<'a> FnOnce(&'s <H as HostHandlers>::AudioProcessor<'a>) -> R,
) -> R {
// SAFETY: we take &mut self, the only reference to the wrapper on the audio thread,
// therefore we can guarantee there are other references anywhere
// PANIC: This struct exists, therefore we are guaranteed the plugin is active
unsafe {
self.inner
.as_ref()
.unwrap()
.wrapper()
.audio_processor()
.unwrap()
.as_mut()
}
unsafe { access(self.wrapper().audio_processor().unwrap().as_ref()) }
}

#[inline]
pub fn use_handler_mut<'s, R>(
&'s mut self,
access: impl for<'a> FnOnce(&'s mut <H as HostHandlers>::AudioProcessor<'a>) -> R,
) -> R {
// SAFETY: we take &self, the only reference to the wrapper on the audio thread, therefore
// we can guarantee there are no mutable references anywhere
// PANIC: This struct exists, therefore we are guaranteed the plugin is active
unsafe { access(self.wrapper().audio_processor().unwrap().as_mut()) }
}

#[inline]
Expand Down Expand Up @@ -371,7 +377,7 @@ pub struct StoppedPluginAudioProcessor<H: HostHandlers> {
_no_sync: PhantomData<UnsafeCell<()>>,
}

impl<'a, H: 'a + HostHandlers> StoppedPluginAudioProcessor<H> {
impl<H: HostHandlers> StoppedPluginAudioProcessor<H> {
#[inline]
pub(crate) fn new(inner: Arc<PluginInstanceInner<H>>) -> Self {
Self {
Expand Down Expand Up @@ -406,24 +412,33 @@ impl<'a, H: 'a + HostHandlers> StoppedPluginAudioProcessor<H> {
}

#[inline]
pub fn shared(&self) -> &<H as HostHandlers>::Shared<'_> {
self.inner.wrapper().shared()
pub fn use_shared_handler<'s, R>(
&'s self,
access: impl for<'a> FnOnce(&'s <H as HostHandlers>::Shared<'a>) -> R,
) -> R {
access(self.inner.wrapper().shared())
}

#[inline]
pub fn handler(&self) -> &<H as HostHandlers>::AudioProcessor<'_> {
// SAFETY: we take &self, the only reference to the wrapper on the audio thread, therefore
// we can guarantee there are no mutable references anywhere
pub fn use_handler<'s, R>(
&'s self,
access: impl for<'a> FnOnce(&'s <H as HostHandlers>::AudioProcessor<'a>) -> R,
) -> R {
// SAFETY: we take &mut self, the only reference to the wrapper on the audio thread,
// therefore we can guarantee there are other references anywhere
// PANIC: This struct exists, therefore we are guaranteed the plugin is active
unsafe { self.inner.wrapper().audio_processor().unwrap().as_ref() }
unsafe { access(self.inner.wrapper().audio_processor().unwrap().as_ref()) }
}

#[inline]
pub fn handler_mut(&mut self) -> &mut <H as HostHandlers>::AudioProcessor<'_> {
// SAFETY: we take &mut self, the only reference to the wrapper on the audio thread,
// therefore we can guarantee there are other references anywhere
pub fn use_handler_mut<'s, R>(
&'s mut self,
access: impl for<'a> FnOnce(&'s mut <H as HostHandlers>::AudioProcessor<'a>) -> R,
) -> R {
// SAFETY: we take &self, the only reference to the wrapper on the audio thread, therefore
// we can guarantee there are no mutable references anywhere
// PANIC: This struct exists, therefore we are guaranteed the plugin is active
unsafe { self.inner.wrapper().audio_processor().unwrap().as_mut() }
unsafe { access(self.inner.wrapper().audio_processor().unwrap().as_mut()) }
}

#[inline]
Expand Down
2 changes: 1 addition & 1 deletion host/tests/reentrant-init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,5 @@ fn can_call_host_methods_during_init() {
.unwrap();

// Timer should have already been registered by the plugin during init().
assert!(instance.handler().timer_registered);
assert!(instance.use_handler(|h| h.timer_registered));
}
12 changes: 3 additions & 9 deletions host/tests/shared-state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,11 @@ impl<'a> SharedHandler<'a> for MyHostShared {
}
}

struct MyHostMainThread<'a> {
shared: &'a MyHostShared,
}

impl<'a> MainThreadHandler<'a> for MyHostMainThread<'a> {}

struct MyHost;
impl HostHandlers for MyHost {
type Shared<'a> = MyHostShared;

type MainThread<'a> = MyHostMainThread<'a>;
type MainThread<'a> = ();
type AudioProcessor<'a> = ();
}

Expand All @@ -106,12 +100,12 @@ pub fn handles_drop_order() {
|_| MyHostShared {
state_ext: OnceLock::new(),
},
|shared| MyHostMainThread { shared },
|_| (),
&bundle,
CStr::from_bytes_with_nul(b"com.u-he.diva\0").unwrap(),
&host_info,
)
.unwrap();

let _ext = plugin_instance.handler().shared.state_ext.get();
let _ext = plugin_instance.use_shared_handler(|s| s.state_ext.get());
}

0 comments on commit cd6e3aa

Please sign in to comment.