Skip to main content

clack_host/plugin/
handle.rs

1use clack_common::extensions::{Extension, PluginExtensionSide, RawExtension};
2use clack_common::plugin::PluginDescriptor;
3use clap_sys::plugin::clap_plugin;
4use std::fmt::{Debug, Formatter};
5use std::marker::PhantomData;
6use std::ops::{Deref, DerefMut};
7use std::ptr::NonNull;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::sync::{Arc, RwLock};
10
11/// A main-thread handle to the plugin instance.
12///
13/// This can be used to make requests to the plugin that can only be made in the main thread, which
14/// is required for e.g. some extensions.
15#[derive(Eq, PartialEq)]
16#[repr(transparent)]
17pub struct PluginMainThreadHandle<'a> {
18    raw: NonNull<clap_plugin>,
19    lifetime: PhantomData<&'a clap_plugin>,
20}
21
22impl<'a> PluginMainThreadHandle<'a> {
23    /// # Safety
24    /// The user must ensure the provided plugin pointer is valid.
25    /// This can only be called on the main thread.
26    pub(crate) const unsafe fn new(raw: NonNull<clap_plugin>) -> Self {
27        Self {
28            raw,
29            lifetime: PhantomData,
30        }
31    }
32
33    /// Returns a shared reference to the raw, C-FFI compatible plugin instance struct.
34    ///
35    /// This type enforces that the reference is valid for the lifetime of the instance (`'a`).
36    ///
37    /// If you need to access the raw pointer without dereferencing it first, use
38    /// [`as_raw_ptr`](Self::as_raw_ptr) instead.
39    #[inline]
40    pub const fn as_raw(&self) -> &'a clap_plugin {
41        // SAFETY: this type enforces that the clap_plugin instance is valid for 'a.
42        unsafe { self.raw.as_ref() }
43    }
44
45    /// Returns a raw pointer to the raw, C-FFI compatible plugin instance struct, without dereferencing it.
46    ///
47    /// If you need to safely access the plugin instance struct through a shared reference,
48    /// use [`as_raw`](Self::as_raw) instead.
49    #[inline]
50    pub const fn as_raw_ptr(&self) -> *const clap_plugin {
51        self.raw.as_ptr()
52    }
53
54    /// Produces a [`PluginSharedHandle`] from this main-thread handle.
55    #[inline]
56    pub const fn shared(&self) -> PluginSharedHandle<'a> {
57        // SAFETY: This type ensures the provided pointer is valid for 'a
58        unsafe { PluginSharedHandle::new(self.raw) }
59    }
60
61    /// Turns this handle reference to a [`PluginSharedHandle`] reference.
62    #[inline]
63    pub const fn as_shared(&self) -> &PluginSharedHandle<'a> {
64        // SAFETY: this cast is valid since both types are just a NonNull<clap_host> and repr(transparent)
65        unsafe { &*(self as *const Self as *const PluginSharedHandle<'a>) }
66    }
67}
68
69impl Debug for PluginMainThreadHandle<'_> {
70    #[inline]
71    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
72        write!(f, "PluginMainThreadHandle ({:p})", self.raw)
73    }
74}
75
76impl<'a> Deref for PluginMainThreadHandle<'a> {
77    type Target = PluginSharedHandle<'a>;
78
79    #[inline]
80    fn deref(&self) -> &Self::Target {
81        self.as_shared()
82    }
83}
84
85/// A main-thread handle to the plugin, that can only exist while the plugin is in the 'inactive' state.
86///
87/// This is used by some extensions that e.g. require a function to be called on the main thread only
88/// if the plugin is inactive, and on the audio thread if it is active.
89#[derive(Eq, PartialEq)]
90#[repr(transparent)]
91pub struct InactivePluginMainThreadHandle<'a> {
92    raw: NonNull<clap_plugin>,
93    lifetime: PhantomData<&'a clap_plugin>,
94}
95
96impl<'a> InactivePluginMainThreadHandle<'a> {
97    /// # Safety
98    /// The user must ensure the provided plugin pointer is valid.
99    /// This can only be called on the main thread, and the plugin instance must be inactive for `'a`.
100    pub(crate) const unsafe fn new(raw: NonNull<clap_plugin>) -> Self {
101        Self {
102            raw,
103            lifetime: PhantomData,
104        }
105    }
106
107    /// Returns a shared reference to the raw, C-FFI compatible plugin instance struct.
108    ///
109    /// This type enforces that the reference is valid for the lifetime of the instance (`'a`).
110    ///
111    /// If you need to access the raw pointer without dereferencing it first, use
112    /// [`as_raw_ptr`](Self::as_raw_ptr) instead.
113    #[inline]
114    pub const fn as_raw(&self) -> &'a clap_plugin {
115        // SAFETY: this type enforces that the clap_plugin instance is valid for 'a.
116        unsafe { self.raw.as_ref() }
117    }
118
119    /// Returns a raw pointer to the raw, C-FFI compatible plugin instance struct, without dereferencing it.
120    ///
121    /// If you need to safely access the plugin instance struct through a shared reference,
122    /// use [`as_raw`](Self::as_raw) instead.
123    #[inline]
124    pub const fn as_raw_ptr(&self) -> *const clap_plugin {
125        self.raw.as_ptr()
126    }
127
128    /// Produces a [`PluginSharedHandle`] from this main-thread handle.
129    #[inline]
130    pub const fn shared(&self) -> PluginSharedHandle<'a> {
131        // SAFETY: This type ensures the provided pointer is valid for 'a
132        unsafe { PluginSharedHandle::new(self.raw) }
133    }
134
135    /// Turns this handle reference to a [`PluginSharedHandle`] reference.
136    #[inline]
137    pub const fn as_shared(&self) -> &PluginSharedHandle<'a> {
138        // SAFETY: this cast is valid since both types are just a NonNull<clap_host> and repr(transparent)
139        unsafe { &*(self as *const Self as *const PluginSharedHandle<'a>) }
140    }
141
142    // Note: this function is pretty useless, but is needed for the Deref impl (required by DerefMut)
143    #[inline]
144    const fn as_main_thread_ref(&self) -> &PluginMainThreadHandle<'a> {
145        // SAFETY: this cast is valid since both types are just a NonNull<clap_host> and repr(transparent)
146        unsafe { &*(self as *const Self as *const PluginMainThreadHandle<'a>) }
147    }
148
149    /// Turns this handle reference to an exclusive [`PluginMainThreadHandle`] reference.
150    ///
151    /// This may be useful if you receive a [`InactivePluginMainThreadHandle`] but need to use
152    /// some methods or extensions that do not care whether the plugin instance is currently active or not.
153    ///
154    /// Only one of the [`InactivePluginMainThreadHandle`] and [`PluginMainThreadHandle`] may be
155    /// used at the same time.
156    #[inline]
157    pub const fn as_main_thread(&mut self) -> &mut PluginMainThreadHandle<'a> {
158        // SAFETY: this cast is valid since both types are just a NonNull<clap_host> and repr(transparent)
159        unsafe { &mut *(self as *mut Self as *mut PluginMainThreadHandle<'a>) }
160    }
161}
162
163impl Debug for InactivePluginMainThreadHandle<'_> {
164    #[inline]
165    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
166        write!(f, "InactivePluginMainThreadHandle ({:p})", self.raw)
167    }
168}
169
170impl<'a> Deref for InactivePluginMainThreadHandle<'a> {
171    type Target = PluginMainThreadHandle<'a>;
172
173    #[inline]
174    fn deref(&self) -> &Self::Target {
175        self.as_main_thread_ref()
176    }
177}
178
179impl DerefMut for InactivePluginMainThreadHandle<'_> {
180    #[inline]
181    fn deref_mut(&mut self) -> &mut Self::Target {
182        self.as_main_thread()
183    }
184}
185
186/// A thread-safe handle to the plugin instance.
187///
188/// This can be used to make requests to the plugin that support being made from any threads.
189#[derive(Copy, Clone, Eq, PartialEq)]
190#[repr(transparent)]
191pub struct PluginSharedHandle<'a> {
192    raw: NonNull<clap_plugin>,
193    lifetime: PhantomData<&'a clap_plugin>,
194}
195
196// SAFETY: The Shared handle only exposes thread-safe methods
197unsafe impl Send for PluginSharedHandle<'_> {}
198// SAFETY: The Shared handle only exposes thread-safe methods
199unsafe impl Sync for PluginSharedHandle<'_> {}
200
201impl<'a> PluginSharedHandle<'a> {
202    /// # Safety
203    ///
204    /// Users must ensure the provided instance pointer is valid.
205    pub(crate) const unsafe fn new(raw: NonNull<clap_plugin>) -> Self {
206        Self {
207            raw,
208            lifetime: PhantomData,
209        }
210    }
211
212    /// Returns the [`PluginDescriptor`] this instance corresponds to.
213    ///
214    /// This may return `None` if the underlying plugin implementation didn't properly populate
215    /// the descriptor pointer.
216    pub const fn descriptor(&self) -> Option<&'a PluginDescriptor> {
217        // SAFETY: the desc pointer is guaranteed to be valid (if present) by the CLAP spec.
218        let Some(descriptor) = (unsafe { self.as_raw().desc.as_ref() }) else {
219            return None;
220        };
221
222        // SAFETY: the desc pointer is guaranteed to be valid (if present) by the CLAP spec.
223        Some(unsafe { PluginDescriptor::from_raw(descriptor) })
224    }
225
226    /// Returns a shared reference to the raw, C-FFI compatible plugin instance struct.
227    ///
228    /// This type enforces that the reference is valid for the lifetime of the instance (`'a`).
229    ///
230    /// If you need to access the raw pointer without dereferencing it first, use
231    /// [`as_raw_ptr`](Self::as_raw_ptr) instead.
232    #[inline]
233    pub const fn as_raw(&self) -> &'a clap_plugin {
234        // SAFETY: this type enforces that the clap_plugin instance is valid for 'a.
235        unsafe { self.raw.as_ref() }
236    }
237
238    /// Returns a raw pointer to the raw, C-FFI compatible plugin instance struct, without dereferencing it.
239    ///
240    /// If you need to safely access the plugin instance struct through a shared reference,
241    /// use [`as_raw`](Self::as_raw) instead.
242    #[inline]
243    pub const fn as_raw_ptr(&self) -> *const clap_plugin {
244        self.raw.as_ptr()
245    }
246
247    /// Retrieves the plugin's pointer to the given [extension type](Extension) `E`.
248    ///
249    /// This returns `None` if the plugin does not support the given extension.
250    ///
251    /// # Example
252    ///
253    /// ```
254    /// use clack_extensions::params::{PluginParams};
255    /// use clack_host::plugin::PluginSharedHandle;
256    ///
257    /// # fn foo(info: PluginSharedHandle) {
258    /// let info: PluginSharedHandle = /* ... */
259    /// # info;
260    /// if let Some(params) = info.get_extension::<PluginParams>() {
261    ///     // The params extension is supported by this plugin
262    /// } else {
263    ///     // The params extension is not supported by this plugin
264    /// }
265    /// # }
266    /// ```
267    pub fn get_extension<E: Extension<ExtensionSide = PluginExtensionSide>>(&self) -> Option<E> {
268        let identifier = const { E::IDENTIFIERS.first().unwrap() };
269        // SAFETY: This type ensures the function pointers are valid
270        let ext = unsafe { self.as_raw().get_extension?(self.raw.as_ptr(), identifier.as_ptr()) };
271
272        let ext = NonNull::new(ext as *mut _)?;
273        // SAFETY: The CLAP spec guarantees that the extension lives as long as the instance.
274        let raw = unsafe { RawExtension::from_raw_plugin_extension(ext, self.raw) };
275
276        // SAFETY: pointer comes from the associated E::IDENTIFIER.
277        unsafe { Some(E::from_raw(raw)) }
278    }
279
280    /// Safely dereferences a [`RawExtension`] pointer produced by this plugin instance.
281    ///
282    /// See the documentation of the [`RawExtension`] type for more information about how this works
283    /// internally.
284    ///
285    /// # Panics
286    ///
287    /// This method will panic if the given extension pointer does not match the plugin instance of
288    /// this handle.
289    #[inline]
290    pub fn use_extension<E: Sized>(
291        &self,
292        extension: &RawExtension<PluginExtensionSide, E>,
293    ) -> &'a E {
294        if self.raw != extension.plugin_ptr() {
295            mismatched_instance();
296        }
297
298        // SAFETY: the RawExtension type enforces the pointee is valid for as long as the matching
299        // instance is still alive.
300        unsafe { extension.as_ptr().as_ref() }
301    }
302}
303
304impl Debug for PluginSharedHandle<'_> {
305    #[inline]
306    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
307        write!(f, "PluginSharedHandle ({:p})", self.raw)
308    }
309}
310
311/// An audio-thread handle to an activated plugin instance.
312///
313/// This can be used to make requests to the plugin that can only be made from the audio thread.
314#[derive(Eq, PartialEq)]
315#[repr(transparent)]
316pub struct PluginAudioProcessorHandle<'a> {
317    raw: NonNull<clap_plugin>,
318    lifetime: PhantomData<&'a clap_plugin>,
319}
320
321// SAFETY: This type only exposes audio-thread methods
322unsafe impl Send for PluginAudioProcessorHandle<'_> {}
323
324impl<'a> PluginAudioProcessorHandle<'a> {
325    pub(crate) const fn new(raw: NonNull<clap_plugin>) -> Self {
326        Self {
327            raw,
328            lifetime: PhantomData,
329        }
330    }
331
332    /// Returns a shared reference to the raw, C-FFI compatible plugin instance struct.
333    ///
334    /// This type enforces that the reference is valid for the lifetime of the instance (`'a`).
335    ///
336    /// If you need to access the raw pointer without dereferencing it first, use
337    /// [`as_raw_ptr`](Self::as_raw_ptr) instead.
338    #[inline]
339    pub const fn as_raw(&self) -> &'a clap_plugin {
340        // SAFETY: this type enforces that the clap_plugin instance is valid for 'a.
341        unsafe { self.raw.as_ref() }
342    }
343
344    /// Returns a raw pointer to the raw, C-FFI compatible plugin instance struct, without dereferencing it.
345    ///
346    /// If you need to safely access the plugin instance struct through a shared reference,
347    /// use [`as_raw`](Self::as_raw) instead.
348    #[inline]
349    pub const fn as_raw_ptr(&self) -> *const clap_plugin {
350        self.raw.as_ptr()
351    }
352
353    /// Turns this handle reference to a [`PluginSharedHandle`] reference.
354    #[inline]
355    pub const fn shared(&self) -> PluginSharedHandle<'a> {
356        PluginSharedHandle {
357            raw: self.raw,
358            lifetime: PhantomData,
359        }
360    }
361
362    /// Turns this handle reference to a [`PluginSharedHandle`] reference.
363    #[inline]
364    pub const fn as_shared(&self) -> &PluginSharedHandle<'a> {
365        // SAFETY: this cast is valid since both types are just a NonNull<clap_host> and repr(transparent)
366        unsafe { &*(self as *const Self as *const PluginSharedHandle<'a>) }
367    }
368}
369
370impl<'a> Deref for PluginAudioProcessorHandle<'a> {
371    type Target = PluginSharedHandle<'a>;
372
373    #[inline]
374    fn deref(&self) -> &Self::Target {
375        self.as_shared()
376    }
377}
378
379impl Debug for PluginAudioProcessorHandle<'_> {
380    #[inline]
381    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
382        write!(f, "PluginAudioProcessorHandle ({:p})", self.raw)
383    }
384}
385
386/// A handle to a plugin instance that may be in the process of initializing.
387///
388/// In this state, only [querying plugin extensions](Self::get_extension) is allowed.
389#[derive(Clone, Eq, PartialEq)]
390pub struct InitializingPluginHandle<'a> {
391    inner: RemoteHandleInner,
392    lifetime: PhantomData<&'a clap_plugin>,
393}
394
395impl<'a> InitializingPluginHandle<'a> {
396    /// # Safety
397    ///
398    /// Users must ensure the provided instance pointer is valid until the given lock is marked as destroying.
399    pub(crate) const unsafe fn new(lock: Arc<DestroyLock>, instance: NonNull<clap_plugin>) -> Self {
400        Self {
401            lifetime: PhantomData,
402            inner: RemoteHandleInner { instance, lock },
403        }
404    }
405
406    /// Returns a shared reference to the raw, C-FFI compatible plugin instance struct.
407    ///
408    /// This type enforces that the reference is valid for the lifetime of the instance (`'a`).
409    ///
410    /// If you need to access the raw pointer without dereferencing it first, use
411    /// [`as_raw_ptr`](Self::as_raw_ptr) instead.
412    #[inline]
413    pub const fn as_raw(&self) -> &'a clap_plugin {
414        // SAFETY: this type enforces that the clap_plugin instance is valid for 'a.
415        unsafe { self.inner.instance.as_ref() }
416    }
417
418    /// Returns a raw pointer to the raw, C-FFI compatible plugin instance struct, without dereferencing it.
419    ///
420    /// If you need to safely access the plugin instance struct through a shared reference,
421    /// use [`as_raw`](Self::as_raw) instead.
422    #[inline]
423    pub const fn as_raw_ptr(&self) -> *const clap_plugin {
424        self.inner.instance.as_ptr()
425    }
426
427    /// Retrieves the plugin's pointer to the given [extension type](Extension) `E`.
428    ///
429    /// This returns `None` if the plugin does not support the given extension.
430    pub fn get_extension<E: Extension<ExtensionSide = PluginExtensionSide>>(&self) -> Option<E> {
431        self.inner.get_extension()
432    }
433}
434
435impl Debug for InitializingPluginHandle<'_> {
436    #[inline]
437    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
438        write!(f, "InitializingPluginHandle ({:p})", self.inner.instance)
439    }
440}
441
442/// A handle to a plugin instance that has finished initializing.
443///
444/// This handle can be used to obtain a [`PluginSharedHandle`] to then call the plugin's thread-safe
445/// method.
446///
447/// However, this handle can outlive the plugin instance, as host callbacks may be called during
448/// the plugin's destruction.
449///
450/// Therefore, the [`PluginSharedHandle`] can only be accessed through the [`access`](Self::access)
451/// method, ensuring no access can be made during or after destruction.
452#[derive(Clone, Eq, PartialEq)]
453pub struct InitializedPluginHandle<'a> {
454    inner: RemoteHandleInner,
455    lifetime: PhantomData<&'a clap_plugin>,
456}
457
458impl<'a> InitializedPluginHandle<'a> {
459    /// # Safety
460    ///
461    /// Users must ensure the provided instance pointer is valid until the given lock is marked as destroying.
462    pub(crate) const unsafe fn new(lock: Arc<DestroyLock>, instance: NonNull<clap_plugin>) -> Self {
463        Self {
464            lifetime: PhantomData,
465            inner: RemoteHandleInner { instance, lock },
466        }
467    }
468
469    /// Returns a shared reference to the raw, C-FFI compatible plugin instance struct.
470    ///
471    /// This type enforces that the reference is valid for the lifetime of the instance (`'a`).
472    ///
473    /// If you need to access the raw pointer without dereferencing it first, use
474    /// [`as_raw_ptr`](Self::as_raw_ptr) instead.
475    #[inline]
476    pub const fn as_raw(&self) -> &'a clap_plugin {
477        // SAFETY: this type enforces that the clap_plugin instance is valid for 'a.
478        unsafe { self.inner.instance.as_ref() }
479    }
480
481    /// Returns a raw pointer to the raw, C-FFI compatible plugin instance struct, without dereferencing it.
482    ///
483    /// If you need to safely access the plugin instance struct through a shared reference,
484    /// use [`as_raw`](Self::as_raw) instead.
485    #[inline]
486    pub const fn as_raw_ptr(&self) -> *const clap_plugin {
487        self.inner.instance.as_ptr()
488    }
489
490    /// Attempts to access and perform the given operation on the plugin's handle, returning its
491    /// result.
492    ///
493    /// This method will return `None` if the plugin instance is being or has been destroyed, and
494    /// `f` will not be called.
495    ///
496    /// However, this method guarantees that the plugin instance will block starting its destruction
497    /// while the given operation `f` is still ongoing.
498    ///
499    /// This ensures the plugin instance is always valid if and when `f` runs.
500    ///
501    /// # Realtime safety
502    ///
503    /// This method *may* block the current thread if it is called concurrently while the plugin's
504    /// instance is being destroyed (i.e. [`PluginInstance::drop`] is running).
505    ///
506    /// [`PluginInstance::drop`]: crate::plugin::PluginInstance::drop
507    #[inline]
508    pub fn access<T>(&self, f: impl FnOnce(PluginSharedHandle) -> T) -> Option<T> {
509        self.inner.access(f)
510    }
511
512    /// Retrieves the plugin's pointer to the given [extension type](Extension) `E`.
513    ///
514    /// This returns `None` if the plugin does not support the given extension.
515    pub fn get_extension<E: Extension<ExtensionSide = PluginExtensionSide>>(&self) -> Option<E> {
516        self.inner.get_extension()
517    }
518}
519
520impl Debug for InitializedPluginHandle<'_> {
521    #[inline]
522    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
523        write!(f, "InitializingPluginHandle ({:p})", self.inner.instance)
524    }
525}
526
527#[derive(Clone)]
528struct RemoteHandleInner {
529    lock: Arc<DestroyLock>,
530    instance: NonNull<clap_plugin>,
531}
532
533impl RemoteHandleInner {
534    fn access<T>(&self, f: impl FnOnce(PluginSharedHandle) -> T) -> Option<T> {
535        self.lock.hold_off_destruction(|| {
536            // SAFETY: this type ensures the plugin is not being destroyed yet.
537            let handle = unsafe { PluginSharedHandle::new(self.instance) };
538            f(handle)
539        })
540    }
541
542    #[inline]
543    fn get_extension<E: Extension<ExtensionSide = PluginExtensionSide>>(&self) -> Option<E> {
544        self.access(|handle| handle.get_extension())?
545    }
546}
547
548impl PartialEq for RemoteHandleInner {
549    #[inline]
550    fn eq(&self, other: &Self) -> bool {
551        self.instance == other.instance
552    }
553}
554
555impl Eq for RemoteHandleInner {}
556
557// SAFETY: The Shared handles only exposes thread-safe methods
558unsafe impl Send for RemoteHandleInner {}
559// SAFETY: The Shared handles only exposes thread-safe methods
560unsafe impl Sync for RemoteHandleInner {}
561
562pub(crate) struct DestroyLock {
563    is_destroying: AtomicBool,
564    lock: RwLock<bool>,
565}
566
567impl DestroyLock {
568    pub(crate) fn new() -> Self {
569        Self {
570            is_destroying: AtomicBool::new(false),
571            lock: RwLock::new(false),
572        }
573    }
574
575    pub(crate) fn start_destroying(&self) {
576        // Notify threads that may use the lock in the future that we are about to start destroying.
577        self.is_destroying.store(true, Ordering::SeqCst);
578
579        let mut guard = self.lock.write().unwrap_or_else(|err| err.into_inner());
580        // This additional check may not be very useful, it's there just in case.
581        *guard = true;
582    }
583
584    fn hold_off_destruction<T>(&self, handler: impl FnOnce() -> T) -> Option<T> {
585        if self.is_destroying.load(Ordering::SeqCst) {
586            return None;
587        }
588
589        // Poisoning doesn't matter, we are only reading a bool
590        let guard = self.lock.read().unwrap_or_else(|err| err.into_inner());
591        if *guard {
592            return None;
593        }
594
595        let result = handler();
596
597        drop(guard);
598
599        Some(result)
600    }
601}
602
603#[cold]
604const fn mismatched_instance() -> ! {
605    panic!("Given plugin instance handle doesn't match the extension pointer it was used on.")
606}