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<()>>,
/// A channel for waking up the worker thread and having it perform one of the tasks from
/// [`Message`].
worker_thread_channel: channel::Sender<Message<T>>,
tasks_sender: channel::Sender<Message<T>>,
}
/// A message for communicating with the worker thread.
@ -48,7 +48,7 @@ where
E: MainThreadExecutor<T> + 'static,
{
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 {
executor: executor.clone(),
@ -57,10 +57,10 @@ where
worker_thread: Some(
thread::Builder::new()
.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"),
),
worker_thread_channel: sender,
tasks_sender,
}
}
@ -69,9 +69,7 @@ where
self.executor.execute(task, true);
true
} else {
self.worker_thread_channel
.try_send(Message::Task(task))
.is_ok()
self.tasks_sender.try_send(Message::Task(task)).is_ok()
}
}
@ -84,7 +82,7 @@ where
impl<T, E> Drop for LinuxEventLoop<T, E> {
fn drop(&mut self) {
self.worker_thread_channel
self.tasks_sender
.send(Message::Shutdown)
.expect("Failed while sending worker thread shutdown request");
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
/// 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
T: Send,
E: MainThreadExecutor<T>,
{
loop {
match receiver.recv() {
match tasks_receiver.recv() {
Ok(Message::Task(task)) => match executor.upgrade() {
Some(e) => e.execute(task, true),
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
//! 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::mem;
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
/// we'll wake up the window, which then continues to pop tasks off this queue until it is
/// empty.
tasks: Arc<ArrayQueue<T>>,
tasks_sender: channel::Sender<T>,
}
impl<T, E> EventLoop<T, E> for WindowsEventLoop<T, E>
@ -60,8 +60,7 @@ where
E: MainThreadExecutor<T> + 'static,
{
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));
let (tasks_sender, tasks_receiver) = channel::bounded(super::TASK_QUEUE_CAPACITY);
// 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
@ -85,7 +84,6 @@ where
// erased version of the polling loop.
let callback: PollCallback = {
let executor = Arc::downgrade(&executor);
let tasks = tasks.clone();
Box::new(move || {
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);
}
})
@ -128,7 +126,7 @@ where
main_thread_id: thread::current().id(),
message_window: window,
message_window_class_name: class_name,
tasks,
tasks_sender,
}
}
@ -137,7 +135,7 @@ where
self.executor.execute(task, true);
true
} else {
let success = self.tasks.push(task).is_ok();
let success = self.tasks_sender.try_send(task).is_ok();
if success {
// Instead of polling on a timer, we can just wake up the window whenever there's a
// new message.