1
0
Fork 0

Use weak references to the executor

So this cyclic reference can't keep it alive.
This commit is contained in:
Robbert van der Helm 2022-02-01 15:31:16 +01:00
parent 98076ff792
commit 565d9259c3
3 changed files with 24 additions and 10 deletions

View file

@ -16,7 +16,7 @@
//! Different contexts the plugin can use to make callbacks to the host in different...contexts. //! Different contexts the plugin can use to make callbacks to the host in different...contexts.
use std::sync::Arc; use std::sync::Weak;
use crate::params::Param; use crate::params::Param;
@ -66,7 +66,7 @@ where
{ {
/// Create and start a new event loop. The thread this is called on will be designated as the /// 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. /// main thread, so this should be called when constructing the wrapper.
fn new_and_spawn(executor: Arc<E>) -> Self; fn new_and_spawn(executor: Weak<E>) -> Self;
/// Either post the function to the task queue so it can be delegated to the main thread, or /// 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 /// execute the task directly if this is the main thread. This function needs to be callable at

View file

@ -19,7 +19,7 @@
//! delegate expensive processing to another thread. //! delegate expensive processing to another thread.
use crossbeam::channel; use crossbeam::channel;
use std::sync::Arc; use std::sync::Weak;
use std::thread::{self, JoinHandle, ThreadId}; use std::thread::{self, JoinHandle, ThreadId};
use super::{EventLoop, MainThreadExecutor}; use super::{EventLoop, MainThreadExecutor};
@ -31,7 +31,7 @@ pub(crate) struct LinuxEventLoop<T, E> {
/// The thing that ends up executing these tasks. The tasks are usually executed from the worker /// 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 /// thread, but if the current thread is the main thread then the task cna also be executed
/// directly. /// directly.
executor: Arc<E>, executor: Weak<E>,
/// The ID of the main thread. In practice this is the ID of the thread that created this task /// The ID of the main thread. In practice this is the ID of the thread that created this task
/// queue. /// queue.
@ -59,7 +59,7 @@ where
T: Send, T: Send,
E: MainThreadExecutor<T>, E: MainThreadExecutor<T>,
{ {
fn new_and_spawn(executor: Arc<E>) -> Self { fn new_and_spawn(executor: Weak<E>) -> Self {
let (sender, receiver) = channel::bounded(super::TASK_QUEUE_CAPACITY); let (sender, receiver) = channel::bounded(super::TASK_QUEUE_CAPACITY);
Self { Self {
@ -83,8 +83,16 @@ where
fn do_maybe_async(&self, task: T) -> bool { fn do_maybe_async(&self, task: T) -> bool {
if self.is_main_thread() { if self.is_main_thread() {
self.executor.execute(task); match self.executor.upgrade() {
true Some(e) => {
e.execute(task);
true
}
None => {
nih_log!("The executor doesn't exist but somehow it's still submitting tasks, this shouldn't be possible!");
false
}
}
} else { } else {
self.worker_thread_channel self.worker_thread_channel
.try_send(Message::Task(task)) .try_send(Message::Task(task))
@ -109,14 +117,20 @@ impl<T, E> Drop for LinuxEventLoop<T, E> {
} }
/// The worker thread used in [EventLoop] that executes incmoing tasks on the event loop's executor. /// The worker thread used in [EventLoop] that executes incmoing tasks on the event loop's executor.
fn worker_thread<T, E>(receiver: channel::Receiver<Message<T>>, executor: Arc<E>) fn worker_thread<T, E>(receiver: channel::Receiver<Message<T>>, executor: Weak<E>)
where where
T: Send, T: Send,
E: MainThreadExecutor<T>, E: MainThreadExecutor<T>,
{ {
loop { loop {
match receiver.recv() { match receiver.recv() {
Ok(Message::Task(task)) => executor.execute(task), Ok(Message::Task(task)) => match executor.upgrade() {
Some(e) => e.execute(task),
None => {
nih_log!("Received a new task but the executor is no longer alive, shutting down worker");
return;
}
},
Ok(Message::Shutdown) => return, Ok(Message::Shutdown) => return,
Err(err) => { Err(err) => {
nih_log!( nih_log!(

View file

@ -220,7 +220,7 @@ impl<P: Plugin> WrapperInner<'_, P> {
// XXX: This unsafe block is unnecessary. rust-analyzer gets a bit confused and this this // XXX: This unsafe block is unnecessary. rust-analyzer gets a bit confused and this this
// `write()` function is from `IBStream` which it definitely is not. // `write()` function is from `IBStream` which it definitely is not.
*unsafe { wrapper.event_loop.write() } = *unsafe { wrapper.event_loop.write() } =
MaybeUninit::new(OsEventLoop::new_and_spawn(wrapper.clone())); MaybeUninit::new(OsEventLoop::new_and_spawn(Arc::downgrade(&wrapper)));
wrapper wrapper
} }