Add an is_gui_thread flag to MainThreadExecutor
We'll also use the EventLoop for running background tasks completely decoupled from the GUI.
This commit is contained in:
parent
967426453a
commit
4524719128
7 changed files with 28 additions and 31 deletions
|
@ -55,11 +55,7 @@ where
|
|||
|
||||
/// Something that can execute tasks of type `T`.
|
||||
pub(crate) trait MainThreadExecutor<T>: Send + Sync {
|
||||
/// Execute a task on the current thread. This should only be called from the main thread.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This is not actually unsafe in the typical Rust sense. But the implemnting function will
|
||||
/// assume (and can only assume) that this is called from the main thread.
|
||||
unsafe fn execute(&self, task: T);
|
||||
/// Execute a task on the current thread. This is either called from the GUI thread or from
|
||||
/// another background thread, depending on how the task was scheduled in the [`EventContext`].
|
||||
fn execute(&self, task: T, is_gui_thread: bool);
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ where
|
|||
|
||||
fn do_maybe_async(&self, task: T) -> bool {
|
||||
if self.is_main_thread() {
|
||||
unsafe { self.executor.execute(task) };
|
||||
self.executor.execute(task, true);
|
||||
true
|
||||
} else {
|
||||
self.worker_thread_channel
|
||||
|
@ -103,7 +103,7 @@ where
|
|||
loop {
|
||||
match receiver.recv() {
|
||||
Ok(Message::Task(task)) => match executor.upgrade() {
|
||||
Some(e) => unsafe { e.execute(task) },
|
||||
Some(e) => e.execute(task, true),
|
||||
None => {
|
||||
nih_trace!(
|
||||
"Received a new task but the executor is no longer alive, shutting down \
|
||||
|
|
|
@ -97,7 +97,7 @@ where
|
|||
};
|
||||
|
||||
while let Some(task) = tasks.pop() {
|
||||
unsafe { executor.execute(task) };
|
||||
executor.execute(task, true);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
@ -134,7 +134,7 @@ where
|
|||
|
||||
fn do_maybe_async(&self, task: T) -> bool {
|
||||
if self.is_main_thread() {
|
||||
unsafe { e.execute(task) };
|
||||
self.executor.execute(task, true);
|
||||
true
|
||||
} else {
|
||||
let success = self.tasks.push(task).is_ok();
|
||||
|
|
|
@ -322,7 +322,7 @@ impl<P: ClapPlugin> EventLoop<Task<P>, Wrapper<P>> for Wrapper<P> {
|
|||
|
||||
fn do_maybe_async(&self, task: Task<P>) -> bool {
|
||||
if self.is_main_thread() {
|
||||
unsafe { self.execute(task) };
|
||||
self.execute(task, true);
|
||||
true
|
||||
} else {
|
||||
let success = self.tasks.push(task).is_ok();
|
||||
|
@ -351,33 +351,37 @@ impl<P: ClapPlugin> EventLoop<Task<P>, Wrapper<P>> for Wrapper<P> {
|
|||
}
|
||||
|
||||
impl<P: ClapPlugin> MainThreadExecutor<Task<P>> for Wrapper<P> {
|
||||
unsafe fn execute(&self, task: Task<P>) {
|
||||
fn execute(&self, task: Task<P>, is_gui_thread: bool) {
|
||||
// This function is always called from the main thread, from [Self::on_main_thread].
|
||||
match task {
|
||||
Task::PluginTask(task) => (self.task_executor.lock())(task),
|
||||
Task::LatencyChanged => match &*self.host_latency.borrow() {
|
||||
Some(host_latency) => {
|
||||
nih_debug_assert!(is_gui_thread);
|
||||
|
||||
// XXX: The CLAP docs mention that you should request a restart if this happens
|
||||
// while the plugin is activated (which is not entirely the same thing as
|
||||
// is processing, but we'll treat it as the same thing). In practice just
|
||||
// calling the latency changed function also seems to work just fine.
|
||||
if self.is_processing.load(Ordering::SeqCst) {
|
||||
clap_call! { &*self.host_callback=>request_restart(&*self.host_callback) };
|
||||
unsafe_clap_call! { &*self.host_callback=>request_restart(&*self.host_callback) };
|
||||
} else {
|
||||
clap_call! { host_latency=>changed(&*self.host_callback) };
|
||||
unsafe_clap_call! { host_latency=>changed(&*self.host_callback) };
|
||||
}
|
||||
}
|
||||
None => nih_debug_assert_failure!("Host does not support the latency extension"),
|
||||
},
|
||||
Task::VoiceInfoChanged => match &*self.host_voice_info.borrow() {
|
||||
Some(host_voice_info) => {
|
||||
clap_call! { host_voice_info=>changed(&*self.host_callback) };
|
||||
nih_debug_assert!(is_gui_thread);
|
||||
unsafe_clap_call! { host_voice_info=>changed(&*self.host_callback) };
|
||||
}
|
||||
None => nih_debug_assert_failure!("Host does not support the voice-info extension"),
|
||||
},
|
||||
Task::RescanParamValues => match &*self.host_params.borrow() {
|
||||
Some(host_params) => {
|
||||
clap_call! { host_params=>rescan(&*self.host_callback, CLAP_PARAM_RESCAN_VALUES) };
|
||||
nih_debug_assert!(is_gui_thread);
|
||||
unsafe_clap_call! { host_params=>rescan(&*self.host_callback, CLAP_PARAM_RESCAN_VALUES) };
|
||||
}
|
||||
None => nih_debug_assert_failure!("The host does not support parameters? What?"),
|
||||
},
|
||||
|
@ -2352,7 +2356,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
// [Self::do_maybe_async] posts a task to the queue and asks the host to call this function
|
||||
// on the main thread, so once that's done we can just handle all requests here
|
||||
while let Some(task) = wrapper.tasks.pop() {
|
||||
wrapper.execute(task);
|
||||
wrapper.execute(task, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ pub struct TaskExecutorWrapper<P: Plugin> {
|
|||
}
|
||||
|
||||
impl<P: Plugin> MainThreadExecutor<P::BackgroundTask> for TaskExecutorWrapper<P> {
|
||||
unsafe fn execute(&self, task: P::BackgroundTask) {
|
||||
fn execute(&self, task: P::BackgroundTask, _is_gui_thread: bool) {
|
||||
(self.task_executor.lock())(task)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -377,7 +377,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
|||
let event_loop = self.event_loop.borrow();
|
||||
let event_loop = event_loop.as_ref().unwrap();
|
||||
if event_loop.is_main_thread() {
|
||||
unsafe { self.execute(task) };
|
||||
self.execute(task, true);
|
||||
true
|
||||
} else {
|
||||
// If the editor is open, and the host exposes the `IRunLoop` interface, then we'll run
|
||||
|
@ -535,25 +535,22 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
|||
}
|
||||
|
||||
impl<P: Vst3Plugin> MainThreadExecutor<Task<P>> for WrapperInner<P> {
|
||||
unsafe fn execute(&self, task: Task<P>) {
|
||||
fn execute(&self, task: Task<P>, is_gui_thread: bool) {
|
||||
// This function is always called from the main thread
|
||||
// TODO: When we add GUI resizing and context menus, this should propagate those events to
|
||||
// `IRunLoop` on Linux to keep REAPER happy. That does mean a double spool, but we can
|
||||
// come up with a nicer solution to handle that later (can always add a separate
|
||||
// function for checking if a to be scheduled task can be handled right there and
|
||||
// then).
|
||||
match task {
|
||||
Task::PluginTask(task) => (self.task_executor.lock())(task),
|
||||
Task::TriggerRestart(flags) => match &*self.component_handler.borrow() {
|
||||
Some(handler) => {
|
||||
Some(handler) => unsafe {
|
||||
nih_debug_assert!(is_gui_thread);
|
||||
handler.restart_component(flags);
|
||||
}
|
||||
},
|
||||
None => nih_debug_assert_failure!("Component handler not yet set"),
|
||||
},
|
||||
Task::RequestResize => match &*self.plug_view.read() {
|
||||
Some(plug_view) => {
|
||||
Some(plug_view) => unsafe {
|
||||
nih_debug_assert!(is_gui_thread);
|
||||
plug_view.request_resize();
|
||||
}
|
||||
},
|
||||
None => nih_debug_assert_failure!("Can't resize a closed editor"),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -486,7 +486,7 @@ impl<P: Vst3Plugin> IEventHandler for RunLoopEventHandler<P> {
|
|||
// This gets called from the host's UI thread because we wrote some bytes to the Unix domain
|
||||
// socket. We'll read that data from the socket again just to make REAPER happy.
|
||||
while let Some(task) = self.tasks.pop() {
|
||||
self.inner.execute(task);
|
||||
self.inner.execute(task, true);
|
||||
|
||||
let mut notify_value = 1i8;
|
||||
const NOTIFY_VALUE_SIZE: usize = std::mem::size_of::<i8>();
|
||||
|
|
Loading…
Add table
Reference in a new issue