From 26e0d17833745d1d0ac70afdd7d8805d9232ab90 Mon Sep 17 00:00:00 2001
From: Robbert van der Helm <mail@robbertvanderhelm.nl>
Date: Mon, 22 Aug 2022 12:31:09 +0200
Subject: [PATCH] Fix VST3 event loop leaking

This should have never used MaybeUninit in the first place.
---
 src/wrapper/vst3/inner.rs | 24 ++++++++++++++----------
 src/wrapper/vst3/view.rs  | 14 +++++++-------
 2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/src/wrapper/vst3/inner.rs b/src/wrapper/vst3/inner.rs
index 9f6d4984..0476285a 100644
--- a/src/wrapper/vst3/inner.rs
+++ b/src/wrapper/vst3/inner.rs
@@ -3,7 +3,6 @@ use crossbeam::atomic::AtomicCell;
 use crossbeam::channel::{self, SendTimeoutError};
 use parking_lot::RwLock;
 use std::collections::{HashMap, HashSet, VecDeque};
-use std::mem::MaybeUninit;
 use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
 use std::sync::Arc;
 use std::time::Duration;
@@ -53,11 +52,11 @@ pub(crate) struct WrapperInner<P: Vst3Plugin> {
     /// through [`Self::do_maybe_async()`] instead. That method posts the task to the host's
     /// `IRunLoop` instead of it's available.
     ///
-    /// This RwLock is only needed because it has to be initialized late. There is no reason to
-    /// mutably borrow the event loop, so reads will never be contested.
+    /// This AtomicRefCell+Option is only needed because it has to be initialized late. There is no
+    /// reason to mutably borrow the event loop, so reads will never be contested.
     ///
     /// TODO: Is there a better type for Send+Sync late initializaiton?
-    pub event_loop: AtomicRefCell<MaybeUninit<OsEventLoop<Task, Self>>>,
+    pub event_loop: AtomicRefCell<Option<OsEventLoop<Task, Self>>>,
 
     /// Whether the plugin is currently processing audio. In other words, the last state
     /// `IAudioProcessor::setActive()` has been called with.
@@ -281,7 +280,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
 
             plug_view: RwLock::new(None),
 
-            event_loop: AtomicRefCell::new(MaybeUninit::uninit()),
+            event_loop: AtomicRefCell::new(None),
 
             is_processing: AtomicBool::new(false),
             // Some hosts, like the current version of Bitwig and Ardour at the time of writing,
@@ -321,7 +320,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
         //        is separate from the internal COM-style reference count.
         let wrapper: Arc<WrapperInner<P>> = wrapper.into();
         *wrapper.event_loop.borrow_mut() =
-            MaybeUninit::new(OsEventLoop::new_and_spawn(Arc::downgrade(&wrapper)));
+            Some(OsEventLoop::new_and_spawn(Arc::downgrade(&wrapper)));
 
         wrapper
     }
@@ -351,7 +350,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
     #[must_use]
     pub fn do_maybe_async(&self, task: Task) -> bool {
         let event_loop = self.event_loop.borrow();
-        let event_loop = unsafe { event_loop.assume_init_ref() };
+        let event_loop = event_loop.as_ref().unwrap();
         if event_loop.is_main_thread() {
             unsafe { self.execute(task) };
             true
@@ -480,9 +479,14 @@ impl<P: Vst3Plugin> WrapperInner<P> {
         }
 
         // After the state has been updated, notify the host about the new parameter values
-        let task_posted = unsafe { self.event_loop.borrow().assume_init_ref() }.do_maybe_async(
-            Task::TriggerRestart(RestartFlags::kParamValuesChanged as i32),
-        );
+        let task_posted =
+            self.event_loop
+                .borrow()
+                .as_ref()
+                .unwrap()
+                .do_maybe_async(Task::TriggerRestart(
+                    RestartFlags::kParamValuesChanged as i32,
+                ));
         nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
     }
 
diff --git a/src/wrapper/vst3/view.rs b/src/wrapper/vst3/view.rs
index 02a75360..fbe5102a 100644
--- a/src/wrapper/vst3/view.rs
+++ b/src/wrapper/vst3/view.rs
@@ -508,13 +508,13 @@ impl<P: Vst3Plugin> Drop for RunLoopEventHandler<P> {
         // handle those in the regular event loop so no work gets lost
         let mut posting_failed = false;
         while let Some(task) = self.tasks.pop() {
-            posting_failed |= !unsafe {
-                self.inner
-                    .event_loop
-                    .borrow()
-                    .assume_init_ref()
-                    .do_maybe_async(task)
-            };
+            posting_failed |= !self
+                .inner
+                .event_loop
+                .borrow()
+                .as_ref()
+                .unwrap()
+                .do_maybe_async(task);
         }
 
         if posting_failed {