1
0
Fork 0

Use channels for Windows event loop

Instead of an `ArrayQueue`. Since this doesn't need to both send and
receive on the same object.
This commit is contained in:
Robbert van der Helm 2022-10-23 15:52:14 +02:00
parent 31cda78201
commit dbb97f0534
2 changed files with 14 additions and 18 deletions

View file

@ -31,7 +31,7 @@ pub(crate) struct LinuxEventLoop<T, E> {
worker_thread: Option<JoinHandle<()>>, worker_thread: Option<JoinHandle<()>>,
/// A channel for waking up the worker thread and having it perform one of the tasks from /// A channel for waking up the worker thread and having it perform one of the tasks from
/// [`Message`]. /// [`Message`].
worker_thread_channel: channel::Sender<Message<T>>, tasks_sender: channel::Sender<Message<T>>,
} }
/// A message for communicating with the worker thread. /// A message for communicating with the worker thread.
@ -48,7 +48,7 @@ where
E: MainThreadExecutor<T> + 'static, E: MainThreadExecutor<T> + 'static,
{ {
fn new_and_spawn(executor: Arc<E>) -> Self { fn new_and_spawn(executor: Arc<E>) -> Self {
let (sender, receiver) = channel::bounded(super::TASK_QUEUE_CAPACITY); let (tasks_sender, tasks_receiver) = channel::bounded(super::TASK_QUEUE_CAPACITY);
Self { Self {
executor: executor.clone(), executor: executor.clone(),
@ -57,10 +57,10 @@ where
worker_thread: Some( worker_thread: Some(
thread::Builder::new() thread::Builder::new()
.name(String::from("worker")) .name(String::from("worker"))
.spawn(move || worker_thread(receiver, Arc::downgrade(&executor))) .spawn(move || worker_thread(tasks_receiver, Arc::downgrade(&executor)))
.expect("Could not spawn worker thread"), .expect("Could not spawn worker thread"),
), ),
worker_thread_channel: sender, tasks_sender,
} }
} }
@ -69,9 +69,7 @@ where
self.executor.execute(task, true); self.executor.execute(task, true);
true true
} else { } else {
self.worker_thread_channel self.tasks_sender.try_send(Message::Task(task)).is_ok()
.try_send(Message::Task(task))
.is_ok()
} }
} }
@ -84,7 +82,7 @@ where
impl<T, E> Drop for LinuxEventLoop<T, E> { impl<T, E> Drop for LinuxEventLoop<T, E> {
fn drop(&mut self) { fn drop(&mut self) {
self.worker_thread_channel self.tasks_sender
.send(Message::Shutdown) .send(Message::Shutdown)
.expect("Failed while sending worker thread shutdown request"); .expect("Failed while sending worker thread shutdown request");
if let Some(join_handle) = self.worker_thread.take() { if let Some(join_handle) = self.worker_thread.take() {
@ -95,13 +93,13 @@ impl<T, E> Drop for LinuxEventLoop<T, E> {
/// The worker thread used in [`EventLoop`] that executes incoming tasks on the event loop's /// The worker thread used in [`EventLoop`] that executes incoming tasks on the event loop's
/// executor. /// executor.
fn worker_thread<T, E>(receiver: channel::Receiver<Message<T>>, executor: Weak<E>) fn worker_thread<T, E>(tasks_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 tasks_receiver.recv() {
Ok(Message::Task(task)) => match executor.upgrade() { Ok(Message::Task(task)) => match executor.upgrade() {
Some(e) => e.execute(task, true), Some(e) => e.execute(task, true),
None => { None => {

View file

@ -1,7 +1,7 @@
//! An event loop for windows, using an invisible window to hook into the host's message loop. This //! An event loop for windows, using an invisible window to hook into the host's message loop. This
//! has only been tested under Wine with [yabridge](https://github.com/robbert-vdh/yabridge). //! has only been tested under Wine with [yabridge](https://github.com/robbert-vdh/yabridge).
use crossbeam::queue::ArrayQueue; use crossbeam::channel;
use std::ffi::{c_void, CString}; use std::ffi::{c_void, CString};
use std::mem; use std::mem;
use std::ptr; use std::ptr;
@ -51,7 +51,7 @@ pub(crate) struct WindowsEventLoop<T, E> {
/// A queue of tasks that still need to be performed. When something gets added to this queue /// A queue of tasks that still need to be performed. When something gets added to this queue
/// we'll wake up the window, which then continues to pop tasks off this queue until it is /// we'll wake up the window, which then continues to pop tasks off this queue until it is
/// empty. /// empty.
tasks: Arc<ArrayQueue<T>>, tasks_sender: channel::Sender<T>,
} }
impl<T, E> EventLoop<T, E> for WindowsEventLoop<T, E> impl<T, E> EventLoop<T, E> for WindowsEventLoop<T, E>
@ -60,8 +60,7 @@ where
E: MainThreadExecutor<T> + 'static, E: MainThreadExecutor<T> + 'static,
{ {
fn new_and_spawn(executor: Arc<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_sender, tasks_receiver) = channel::bounded(super::TASK_QUEUE_CAPACITY);
let tasks = Arc::new(ArrayQueue::new(super::TASK_QUEUE_CAPACITY));
// Window classes need to have unique names or else multiple plugins loaded into the same // Window classes need to have unique names or else multiple plugins loaded into the same
// process will end up calling the other plugin's callbacks // process will end up calling the other plugin's callbacks
@ -85,7 +84,6 @@ where
// erased version of the polling loop. // erased version of the polling loop.
let callback: PollCallback = { let callback: PollCallback = {
let executor = Arc::downgrade(&executor); let executor = Arc::downgrade(&executor);
let tasks = tasks.clone();
Box::new(move || { Box::new(move || {
let executor = match executor.upgrade() { let executor = match executor.upgrade() {
@ -96,7 +94,7 @@ where
} }
}; };
while let Some(task) = tasks.pop() { while let Ok(task) = tasks_receiver.try_recv() {
executor.execute(task, true); executor.execute(task, true);
} }
}) })
@ -128,7 +126,7 @@ where
main_thread_id: thread::current().id(), main_thread_id: thread::current().id(),
message_window: window, message_window: window,
message_window_class_name: class_name, message_window_class_name: class_name,
tasks, tasks_sender,
} }
} }
@ -137,7 +135,7 @@ where
self.executor.execute(task, true); self.executor.execute(task, true);
true true
} else { } else {
let success = self.tasks.push(task).is_ok(); let success = self.tasks_sender.try_send(task).is_ok();
if success { if success {
// Instead of polling on a timer, we can just wake up the window whenever there's a // Instead of polling on a timer, we can just wake up the window whenever there's a
// new message. // new message.