1
0
Fork 0

Clean up EventLoop API

By taking an `Arc<T>` instead of a `Weak<T>`. The `Weak` is only needed
for the background threads.
This commit is contained in:
Robbert van der Helm 2022-10-23 14:53:00 +02:00
parent 21bfb57023
commit 967426453a
6 changed files with 16 additions and 43 deletions

View file

@ -1,6 +1,6 @@
//! An internal event loop for spooling tasks to the/a GUI thread.
use std::sync::Weak;
use std::sync::Arc;
#[cfg(all(target_family = "unix", not(target_os = "macos")))]
mod linux;
@ -38,10 +38,7 @@ where
{
/// Create and start a new event loop. The thread this is called on will be designated as the
/// main thread, so this should be called when constructing the wrapper.
//
// TODO: This use of `Weak` is a bit weird outside the context of the VST3 wrapper, but the
// alternatives didn't feel right either.
fn new_and_spawn(executor: Weak<E>) -> Self;
fn new_and_spawn(executor: Arc<E>) -> Self;
/// Either post the function to the task queue so it can be delegated to the main thread, or
/// execute the task directly if this is the main thread. This function needs to be callable at

View file

@ -3,7 +3,7 @@
//! delegate expensive processing to another thread.
use crossbeam::channel;
use std::sync::Weak;
use std::sync::{Arc, Weak};
use std::thread::{self, JoinHandle, ThreadId};
use super::{EventLoop, MainThreadExecutor};
@ -19,7 +19,7 @@ pub(crate) struct LinuxEventLoop<T, E> {
/// The thing that ends up executing these tasks. The tasks are usually executed from the worker
/// thread, but if the current thread is the main thread then the task cna also be executed
/// directly.
executor: Weak<E>,
executor: Arc<E>,
/// The ID of the main thread. In practice this is the ID of the thread that created this task
/// queue.
@ -47,7 +47,7 @@ where
T: Send + 'static,
E: MainThreadExecutor<T> + 'static,
{
fn new_and_spawn(executor: Weak<E>) -> Self {
fn new_and_spawn(executor: Arc<E>) -> Self {
let (sender, receiver) = channel::bounded(super::TASK_QUEUE_CAPACITY);
Self {
@ -57,7 +57,7 @@ where
worker_thread: Some(
thread::Builder::new()
.name(String::from("worker"))
.spawn(move || worker_thread(receiver, executor))
.spawn(move || worker_thread(receiver, Arc::downgrade(&executor)))
.expect("Could not spawn worker thread"),
),
worker_thread_channel: sender,
@ -66,19 +66,8 @@ where
fn do_maybe_async(&self, task: T) -> bool {
if self.is_main_thread() {
match self.executor.upgrade() {
Some(e) => {
unsafe { e.execute(task) };
true
}
None => {
nih_trace!(
"The executor doesn't exist but somehow it's still submitting tasks, this \
shouldn't be possible!"
);
false
}
}
unsafe { self.executor.execute(task) };
true
} else {
self.worker_thread_channel
.try_send(Message::Task(task))

View file

@ -6,7 +6,6 @@ use std::ffi::{c_void, CString};
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Weak;
use std::thread::{self, ThreadId};
use windows::Win32::Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, PSTR, WPARAM};
use windows::Win32::System::{
@ -38,7 +37,7 @@ pub(crate) struct WindowsEventLoop<T, E> {
/// The thing that ends up executing these tasks. The tasks are usually executed from the worker
/// thread, but if the current thread is the main thread then the task cna also be executed
/// directly.
executor: Weak<E>,
executor: Arc<E>,
/// The ID of the main thread. In practice this is the ID of the thread that created this task
/// queue.
@ -60,7 +59,7 @@ where
T: Send + 'static,
E: MainThreadExecutor<T> + 'static,
{
fn new_and_spawn(executor: Weak<E>) -> Self {
fn new_and_spawn(executor: Arc<E>) -> Self {
// We'll pass one copy of the this to the window, and we'll keep the other copy here
let tasks = Arc::new(ArrayQueue::new(super::TASK_QUEUE_CAPACITY));
@ -85,7 +84,7 @@ where
// can't pass the tasks queue and the executor to it directly, so this is a simple type
// erased version of the polling loop.
let callback: PollCallback = {
let executor = executor.clone();
let executor = Arc::downgrade(&executor);
let tasks = tasks.clone();
Box::new(move || {
@ -135,19 +134,8 @@ where
fn do_maybe_async(&self, task: T) -> bool {
if self.is_main_thread() {
match self.executor.upgrade() {
Some(e) => {
unsafe { e.execute(task) };
true
}
None => {
nih_trace!(
"The executor doesn't exist but somehow it's still submitting tasks, this \
shouldn't be possible!"
);
false
}
}
unsafe { e.execute(task) };
true
} else {
let success = self.tasks.push(task).is_ok();
if success {

View file

@ -316,7 +316,7 @@ pub enum OutputParamEvent {
/// Because CLAP has this [`clap_host::request_host_callback()`] function, we don't need to use
/// `OsEventLoop` and can instead just request a main thread callback directly.
impl<P: ClapPlugin> EventLoop<Task<P>, Wrapper<P>> for Wrapper<P> {
fn new_and_spawn(_executor: std::sync::Weak<Self>) -> Self {
fn new_and_spawn(_executor: std::sync::Arc<Self>) -> Self {
panic!("What are you doing");
}

View file

@ -214,7 +214,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
// Initialized later as it needs a reference to the wrapper for the async executor
editor: AtomicRefCell::new(None),
event_loop: OsEventLoop::new_and_spawn(Arc::downgrade(&task_executor_wrapper)),
event_loop: OsEventLoop::new_and_spawn(task_executor_wrapper),
bus_config: BusConfig {
num_input_channels: config.input_channels.unwrap_or(P::DEFAULT_INPUT_CHANNELS),

View file

@ -329,8 +329,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
// serving multiple plugin instances, Arc can't be used because its reference count
// is separate from the internal COM-style reference count.
let wrapper: Arc<WrapperInner<P>> = wrapper.into();
*wrapper.event_loop.borrow_mut() =
Some(OsEventLoop::new_and_spawn(Arc::downgrade(&wrapper)));
*wrapper.event_loop.borrow_mut() = Some(OsEventLoop::new_and_spawn(wrapper.clone()));
// The editor also needs to be initialized later so the Async executor can work.
*wrapper.editor.borrow_mut() = wrapper