diff --git a/desktop/src/executor.rs b/desktop/src/executor.rs index c4de50894..19e06343d 100644 --- a/desktop/src/executor.rs +++ b/desktop/src/executor.rs @@ -1,6 +1,5 @@ //! Async executor -use crate::custom_event::RuffleEvent; use crate::task::Task; use async_channel::{unbounded, Receiver, Sender}; 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::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; -use winit::event_loop::EventLoopProxy; + +pub trait PollRequester: Clone { + fn request_poll(&self); +} new_key_type! { // 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` /// does not clone the underlying task. #[derive(Clone)] -struct TaskHandle { +struct TaskHandle { /// The arena handle for a given task. handle: TaskKey, @@ -29,12 +31,12 @@ struct TaskHandle { /// /// Weak reference ensures that the executor along /// with its tasks is dropped properly. - executor: Weak, + executor: Weak>, } -impl TaskHandle { +impl TaskHandle { /// Construct a handle to a given task. - fn for_task(task: TaskKey, executor: Weak) -> Self { + fn for_task(task: TaskKey, executor: Weak>) -> Self { Self { handle: task, executor, @@ -97,28 +99,29 @@ impl TaskHandle { /// /// This is part of the vtable methods of our `RawWaker` impl. 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::::from_const_ptr(almost_self).expect("non-null context ptr"); selfish.raw_waker() } /// Wake the given task, then drop it. 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::::box_from_const_ptr(almost_self).expect("non-null context ptr"); selfish.wake(); } /// Wake the given task. 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::::from_const_ptr(almost_self).expect("non-null context ptr"); selfish.wake(); } /// Drop the async executor. unsafe fn drop_as_ptr(almost_self: *const ()) { - let _ = TaskHandle::box_from_const_ptr(almost_self).expect("non-null context ptr"); + let _ = TaskHandle::::box_from_const_ptr(almost_self).expect("non-null context ptr"); } const VTABLE: RawWakerVTable = RawWakerVTable::new( @@ -129,7 +132,7 @@ impl TaskHandle { ); } -pub struct WinitAsyncExecutor { +pub struct WinitAsyncExecutor { /// List of all spawned tasks. task_queue: RwLock>>, @@ -140,29 +143,29 @@ pub struct WinitAsyncExecutor { self_ref: Weak, /// Event injector for the main thread event loop. - event_loop: EventLoopProxy, + poll_requester: R, /// Whether we have already queued a `TaskPoll` event. waiting_for_poll: AtomicBool, } -impl WinitAsyncExecutor { +impl WinitAsyncExecutor { /// Construct a new executor for the winit event loop. /// /// This function returns the executor itself, plus the `Sender` necessary /// to spawn new tasks. - pub fn new(event_loop: EventLoopProxy) -> (Arc, WinitFutureSpawner) { + pub fn new(poll_requester: R) -> (Arc, WinitFutureSpawner) { let (send, recv) = unbounded(); let new_self = Arc::new_cyclic(|self_ref| Self { task_queue: RwLock::new(SlotMap::with_key()), channel: recv, self_ref: self_ref.clone(), - event_loop: event_loop.clone(), + poll_requester: poll_requester.clone(), waiting_for_poll: AtomicBool::new(false), }); ( 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() { task.set_ready(); if !self.waiting_for_poll.swap(true, Ordering::SeqCst) { - if self.event_loop.send_event(RuffleEvent::TaskPoll).is_err() { - tracing::warn!("A task was queued on an event loop that has already ended. It will not be polled."); - } + self.poll_requester.request_poll(); } else { tracing::info!("Double polling"); } @@ -247,32 +248,28 @@ pub trait FutureSpawner { fn spawn(&self, future: OwnedFuture<(), Error>); } -pub struct WinitFutureSpawner { +pub struct WinitFutureSpawner { channel: Sender>, - event_loop: EventLoopProxy, + poll_requester: R, } -impl WinitFutureSpawner { +impl WinitFutureSpawner { pub fn send_and_poll( channel: Sender>, - event_loop: EventLoopProxy, - ) -> WinitFutureSpawner { + request_poll: R, + ) -> WinitFutureSpawner { WinitFutureSpawner { channel, - event_loop, + poll_requester: request_poll, } } } -impl FutureSpawner for WinitFutureSpawner { +impl FutureSpawner for WinitFutureSpawner { fn spawn(&self, future: OwnedFuture<(), Error>) { self.channel .send_blocking(future) .expect("working channel send"); - if self.event_loop.send_event(RuffleEvent::TaskPoll).is_err() { - tracing::warn!( - "A task was queued on an event loop that has already ended. It will not be polled." - ); - } + self.poll_requester.request_poll() } } diff --git a/desktop/src/player.rs b/desktop/src/player.rs index 1dad576f3..ded2271f6 100644 --- a/desktop/src/player.rs +++ b/desktop/src/player.rs @@ -3,7 +3,7 @@ use crate::backends::{ ExternalNavigatorBackend, }; use crate::custom_event::RuffleEvent; -use crate::executor::WinitAsyncExecutor; +use crate::executor::{PollRequester, WinitAsyncExecutor}; use crate::gui::MovieView; use crate::preferences::GlobalPreferences; use crate::{CALLSTACK, RENDER_INFO, SWF_INFO}; @@ -186,11 +186,22 @@ impl PlayingContent { } } +#[derive(Clone)] +struct WinitWaker(EventLoopProxy); + +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, /// which may be lost when this Player is closed (dropped) struct ActivePlayer { player: Arc>, - executor: Arc, + executor: Arc>, } 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 readable_name = content.name(); let navigator = ExternalNavigatorBackend::new(