desktop: Made WinitAsyncExecutor not depend on winit

This commit is contained in:
Nathan Adams 2024-04-10 22:43:06 +02:00
parent a188962306
commit 3529a2d12d
2 changed files with 42 additions and 34 deletions

View File

@ -1,6 +1,5 @@
//! Async executor //! Async executor
use crate::custom_event::RuffleEvent;
use crate::task::Task; use crate::task::Task;
use async_channel::{unbounded, Receiver, Sender}; use async_channel::{unbounded, Receiver, Sender};
use ruffle_core::backend::navigator::OwnedFuture; use ruffle_core::backend::navigator::OwnedFuture;
@ -9,7 +8,10 @@ use slotmap::{new_key_type, SlotMap};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak}; use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use winit::event_loop::EventLoopProxy;
pub trait PollRequester: Clone {
fn request_poll(&self);
}
new_key_type! { new_key_type! {
// This is what we would call `TaskHandle` everywhere else, but that name is already taken. // This is what we would call `TaskHandle` everywhere else, but that name is already taken.
@ -21,7 +23,7 @@ new_key_type! {
/// All task handles are identical and interchangeable. Cloning a `TaskHandle` /// All task handles are identical and interchangeable. Cloning a `TaskHandle`
/// does not clone the underlying task. /// does not clone the underlying task.
#[derive(Clone)] #[derive(Clone)]
struct TaskHandle { struct TaskHandle<R: PollRequester> {
/// The arena handle for a given task. /// The arena handle for a given task.
handle: TaskKey, handle: TaskKey,
@ -29,12 +31,12 @@ struct TaskHandle {
/// ///
/// Weak reference ensures that the executor along /// Weak reference ensures that the executor along
/// with its tasks is dropped properly. /// with its tasks is dropped properly.
executor: Weak<WinitAsyncExecutor>, executor: Weak<WinitAsyncExecutor<R>>,
} }
impl TaskHandle { impl<R: PollRequester> TaskHandle<R> {
/// Construct a handle to a given task. /// Construct a handle to a given task.
fn for_task(task: TaskKey, executor: Weak<WinitAsyncExecutor>) -> Self { fn for_task(task: TaskKey, executor: Weak<WinitAsyncExecutor<R>>) -> Self {
Self { Self {
handle: task, handle: task,
executor, executor,
@ -97,28 +99,29 @@ impl TaskHandle {
/// ///
/// This is part of the vtable methods of our `RawWaker` impl. /// This is part of the vtable methods of our `RawWaker` impl.
unsafe fn clone_as_ptr(almost_self: *const ()) -> RawWaker { unsafe fn clone_as_ptr(almost_self: *const ()) -> RawWaker {
let selfish = TaskHandle::from_const_ptr(almost_self).expect("non-null context ptr"); let selfish = TaskHandle::<R>::from_const_ptr(almost_self).expect("non-null context ptr");
selfish.raw_waker() selfish.raw_waker()
} }
/// Wake the given task, then drop it. /// Wake the given task, then drop it.
unsafe fn wake_as_ptr(almost_self: *const ()) { unsafe fn wake_as_ptr(almost_self: *const ()) {
let selfish = TaskHandle::box_from_const_ptr(almost_self).expect("non-null context ptr"); let selfish =
TaskHandle::<R>::box_from_const_ptr(almost_self).expect("non-null context ptr");
selfish.wake(); selfish.wake();
} }
/// Wake the given task. /// Wake the given task.
unsafe fn wake_by_ref_as_ptr(almost_self: *const ()) { unsafe fn wake_by_ref_as_ptr(almost_self: *const ()) {
let selfish = TaskHandle::from_const_ptr(almost_self).expect("non-null context ptr"); let selfish = TaskHandle::<R>::from_const_ptr(almost_self).expect("non-null context ptr");
selfish.wake(); selfish.wake();
} }
/// Drop the async executor. /// Drop the async executor.
unsafe fn drop_as_ptr(almost_self: *const ()) { unsafe fn drop_as_ptr(almost_self: *const ()) {
let _ = TaskHandle::box_from_const_ptr(almost_self).expect("non-null context ptr"); let _ = TaskHandle::<R>::box_from_const_ptr(almost_self).expect("non-null context ptr");
} }
const VTABLE: RawWakerVTable = RawWakerVTable::new( const VTABLE: RawWakerVTable = RawWakerVTable::new(
@ -129,7 +132,7 @@ impl TaskHandle {
); );
} }
pub struct WinitAsyncExecutor { pub struct WinitAsyncExecutor<R: PollRequester> {
/// List of all spawned tasks. /// List of all spawned tasks.
task_queue: RwLock<SlotMap<TaskKey, Mutex<Task>>>, task_queue: RwLock<SlotMap<TaskKey, Mutex<Task>>>,
@ -140,29 +143,29 @@ pub struct WinitAsyncExecutor {
self_ref: Weak<Self>, self_ref: Weak<Self>,
/// Event injector for the main thread event loop. /// Event injector for the main thread event loop.
event_loop: EventLoopProxy<RuffleEvent>, poll_requester: R,
/// Whether we have already queued a `TaskPoll` event. /// Whether we have already queued a `TaskPoll` event.
waiting_for_poll: AtomicBool, waiting_for_poll: AtomicBool,
} }
impl WinitAsyncExecutor { impl<R: PollRequester> WinitAsyncExecutor<R> {
/// Construct a new executor for the winit event loop. /// Construct a new executor for the winit event loop.
/// ///
/// This function returns the executor itself, plus the `Sender` necessary /// This function returns the executor itself, plus the `Sender` necessary
/// to spawn new tasks. /// to spawn new tasks.
pub fn new(event_loop: EventLoopProxy<RuffleEvent>) -> (Arc<Self>, WinitFutureSpawner) { pub fn new(poll_requester: R) -> (Arc<Self>, WinitFutureSpawner<R>) {
let (send, recv) = unbounded(); let (send, recv) = unbounded();
let new_self = Arc::new_cyclic(|self_ref| Self { let new_self = Arc::new_cyclic(|self_ref| Self {
task_queue: RwLock::new(SlotMap::with_key()), task_queue: RwLock::new(SlotMap::with_key()),
channel: recv, channel: recv,
self_ref: self_ref.clone(), self_ref: self_ref.clone(),
event_loop: event_loop.clone(), poll_requester: poll_requester.clone(),
waiting_for_poll: AtomicBool::new(false), waiting_for_poll: AtomicBool::new(false),
}); });
( (
new_self, new_self,
WinitFutureSpawner::send_and_poll(send, event_loop), WinitFutureSpawner::send_and_poll(send, poll_requester),
) )
} }
@ -204,9 +207,7 @@ impl WinitAsyncExecutor {
if !task.is_completed() { if !task.is_completed() {
task.set_ready(); task.set_ready();
if !self.waiting_for_poll.swap(true, Ordering::SeqCst) { if !self.waiting_for_poll.swap(true, Ordering::SeqCst) {
if self.event_loop.send_event(RuffleEvent::TaskPoll).is_err() { self.poll_requester.request_poll();
tracing::warn!("A task was queued on an event loop that has already ended. It will not be polled.");
}
} else { } else {
tracing::info!("Double polling"); tracing::info!("Double polling");
} }
@ -247,32 +248,28 @@ pub trait FutureSpawner {
fn spawn(&self, future: OwnedFuture<(), Error>); fn spawn(&self, future: OwnedFuture<(), Error>);
} }
pub struct WinitFutureSpawner { pub struct WinitFutureSpawner<R: PollRequester> {
channel: Sender<OwnedFuture<(), Error>>, channel: Sender<OwnedFuture<(), Error>>,
event_loop: EventLoopProxy<RuffleEvent>, poll_requester: R,
} }
impl WinitFutureSpawner { impl<R: PollRequester> WinitFutureSpawner<R> {
pub fn send_and_poll( pub fn send_and_poll(
channel: Sender<OwnedFuture<(), Error>>, channel: Sender<OwnedFuture<(), Error>>,
event_loop: EventLoopProxy<RuffleEvent>, request_poll: R,
) -> WinitFutureSpawner { ) -> WinitFutureSpawner<R> {
WinitFutureSpawner { WinitFutureSpawner {
channel, channel,
event_loop, poll_requester: request_poll,
} }
} }
} }
impl FutureSpawner for WinitFutureSpawner { impl<R: PollRequester> FutureSpawner for WinitFutureSpawner<R> {
fn spawn(&self, future: OwnedFuture<(), Error>) { fn spawn(&self, future: OwnedFuture<(), Error>) {
self.channel self.channel
.send_blocking(future) .send_blocking(future)
.expect("working channel send"); .expect("working channel send");
if self.event_loop.send_event(RuffleEvent::TaskPoll).is_err() { self.poll_requester.request_poll()
tracing::warn!(
"A task was queued on an event loop that has already ended. It will not be polled."
);
}
} }
} }

View File

@ -3,7 +3,7 @@ use crate::backends::{
ExternalNavigatorBackend, ExternalNavigatorBackend,
}; };
use crate::custom_event::RuffleEvent; use crate::custom_event::RuffleEvent;
use crate::executor::WinitAsyncExecutor; use crate::executor::{PollRequester, WinitAsyncExecutor};
use crate::gui::MovieView; use crate::gui::MovieView;
use crate::preferences::GlobalPreferences; use crate::preferences::GlobalPreferences;
use crate::{CALLSTACK, RENDER_INFO, SWF_INFO}; use crate::{CALLSTACK, RENDER_INFO, SWF_INFO};
@ -186,11 +186,22 @@ impl PlayingContent {
} }
} }
#[derive(Clone)]
struct WinitWaker(EventLoopProxy<RuffleEvent>);
impl PollRequester for WinitWaker {
fn request_poll(&self) {
if self.0.send_event(RuffleEvent::TaskPoll).is_err() {
tracing::error!("Couldn't request poll - event loop is closed");
}
}
}
/// Represents a current Player and any associated state with that player, /// Represents a current Player and any associated state with that player,
/// which may be lost when this Player is closed (dropped) /// which may be lost when this Player is closed (dropped)
struct ActivePlayer { struct ActivePlayer {
player: Arc<Mutex<Player>>, player: Arc<Mutex<Player>>,
executor: Arc<WinitAsyncExecutor>, executor: Arc<WinitAsyncExecutor<WinitWaker>>,
} }
impl ActivePlayer { impl ActivePlayer {
@ -244,7 +255,7 @@ impl ActivePlayer {
} }
} }
let (executor, future_spawner) = WinitAsyncExecutor::new(event_loop.clone()); let (executor, future_spawner) = WinitAsyncExecutor::new(WinitWaker(event_loop.clone()));
let movie_url = content.initial_swf_url().clone(); let movie_url = content.initial_swf_url().clone();
let readable_name = content.name(); let readable_name = content.name();
let navigator = ExternalNavigatorBackend::new( let navigator = ExternalNavigatorBackend::new(