Web: async improvements (#3805)

- Internal: Fix dropping `Notifier` without sending a result causing `Future`s to never complete. This should never happen anyway, but now we get a panic instead of nothing if we hit a bug.
- Internal: Remove a bunch of `unwrap()`s that aren't required when correctly using `MainThreadMarker`.
- `Window::canvas()` is now able to return a reference instead of an owned value.

Extracted from #3801.
This commit is contained in:
daxpedda 2024-07-23 16:47:35 +02:00 committed by GitHub
parent 5ec934b1b0
commit ef580b817d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 238 additions and 219 deletions

View file

@ -1,15 +1,13 @@
use std::rc::Weak;
use super::runner::Execution;
use super::runner::WeakShared;
use crate::platform_impl::platform::r#async::Waker;
#[derive(Clone)]
pub struct EventLoopProxy {
runner: Waker<Weak<Execution>>,
runner: Waker<WeakShared>,
}
impl EventLoopProxy {
pub fn new(runner: Waker<Weak<Execution>>) -> Self {
pub fn new(runner: Waker<WeakShared>) -> Self {
Self { runner }
}

View file

@ -38,9 +38,9 @@ impl Clone for Shared {
type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
pub struct Execution {
struct Execution {
main_thread: MainThreadMarker,
proxy_spawner: WakerSpawner<Weak<Self>>,
proxy_spawner: WakerSpawner<WeakShared>,
control_flow: Cell<ControlFlow>,
poll_strategy: Cell<PollStrategy>,
wait_until_strategy: Cell<WaitUntilStrategy>,
@ -53,7 +53,7 @@ pub struct Execution {
window: web_sys::Window,
document: Document,
#[allow(clippy::type_complexity)]
all_canvases: RefCell<Vec<(WindowId, Weak<RefCell<backend::Canvas>>, DispatchRunner<Inner>)>>,
all_canvases: RefCell<Vec<(WindowId, Weak<backend::Canvas>, DispatchRunner<Inner>)>>,
redraw_pending: RefCell<HashSet<WindowId>>,
destroy_pending: RefCell<VecDeque<WindowId>>,
page_transition_event_handle: RefCell<Option<backend::PageTransitionEventHandle>>,
@ -116,7 +116,7 @@ impl Runner {
EventWrapper::Event(event) => (self.event_handler)(event),
EventWrapper::ScaleChange { canvas, size, scale } => {
if let Some(canvas) = canvas.upgrade() {
canvas.borrow().handle_scale_change(
canvas.handle_scale_change(
runner,
|event| (self.event_handler)(event),
size,
@ -137,12 +137,12 @@ impl Shared {
let document = window.document().expect("Failed to obtain document");
Shared(Rc::<Execution>::new_cyclic(|weak| {
let proxy_spawner = WakerSpawner::new(main_thread, weak.clone(), |runner, local| {
if let Some(runner) = runner.upgrade() {
Shared(runner).send_proxy_wake_up(local);
}
})
.expect("`EventLoop` has to be created in the main thread");
let proxy_spawner =
WakerSpawner::new(main_thread, WeakShared(weak.clone()), |runner, local| {
if let Some(runner) = runner.upgrade() {
runner.send_proxy_wake_up(local);
}
});
Execution {
main_thread,
@ -189,7 +189,7 @@ impl Shared {
pub fn add_canvas(
&self,
id: WindowId,
canvas: Weak<RefCell<backend::Canvas>>,
canvas: Weak<backend::Canvas>,
runner: DispatchRunner<Inner>,
) {
self.0.all_canvases.borrow_mut().push((id, canvas, runner));
@ -391,7 +391,7 @@ impl Shared {
// - not visible and we don't know if it intersects yet
// - visible and intersects
if let (false, Some(true) | None) | (true, Some(true)) =
(is_visible, canvas.borrow().is_intersecting)
(is_visible, canvas.is_intersecting.get())
{
runner.send_event(Event::WindowEvent {
window_id: *id,
@ -616,7 +616,7 @@ impl Shared {
// and potentially block other threads in the meantime.
for (_, window, runner) in self.0.all_canvases.borrow().iter() {
if let Some(window) = window.upgrade() {
runner.run();
runner.run(self.main_thread());
drop(window)
}
}
@ -705,7 +705,6 @@ impl Shared {
// In case any remaining `Window`s are still not dropped, we will need
// to explicitly remove the event handlers associated with their canvases.
if let Some(canvas) = canvas.upgrade() {
let mut canvas = canvas.borrow_mut();
canvas.remove_listeners();
}
}
@ -747,7 +746,7 @@ impl Shared {
DeviceEvents::WhenFocused => {
self.0.all_canvases.borrow().iter().any(|(_, canvas, _)| {
if let Some(canvas) = canvas.upgrade() {
canvas.borrow().has_focus.get()
canvas.has_focus.get()
} else {
false
}
@ -793,14 +792,23 @@ impl Shared {
self.0.wait_until_strategy.get()
}
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
pub(crate) fn waker(&self) -> Waker<WeakShared> {
self.0.proxy_spawner.waker()
}
}
#[derive(Clone, Debug)]
pub struct WeakShared(Weak<Execution>);
impl WeakShared {
pub fn upgrade(&self) -> Option<Shared> {
self.0.upgrade().map(Shared)
}
}
pub(crate) enum EventWrapper {
Event(Event),
ScaleChange { canvas: Weak<RefCell<backend::Canvas>>, size: PhysicalSize<u32>, scale: f64 },
ScaleChange { canvas: Weak<backend::Canvas>, size: PhysicalSize<u32>, scale: f64 },
}
impl From<Event> for EventWrapper {

View file

@ -1,16 +1,16 @@
use std::cell::{Cell, RefCell};
use std::cell::Cell;
use std::clone::Clone;
use std::collections::vec_deque::IntoIter as VecDequeIter;
use std::collections::VecDeque;
use std::iter;
use std::rc::{Rc, Weak};
use std::rc::Rc;
use web_sys::Element;
use super::super::monitor::MonitorHandle;
use super::super::KeyEventExtra;
use super::device::DeviceId;
use super::runner::{EventWrapper, Execution};
use super::runner::{EventWrapper, WeakShared};
use super::window::WindowId;
use super::{backend, runner, EventLoopProxy};
use crate::event::{
@ -80,9 +80,8 @@ impl ActiveEventLoop {
CustomCursorFuture(CustomCursor::new_async(self, source.inner))
}
pub fn register(&self, canvas: &Rc<RefCell<backend::Canvas>>, id: WindowId) {
pub fn register(&self, canvas: &Rc<backend::Canvas>, id: WindowId) {
let canvas_clone = canvas.clone();
let mut canvas = canvas.borrow_mut();
#[cfg(any(feature = "rwh_04", feature = "rwh_05"))]
canvas.set_attribute("data-raw-handle", &id.0.to_string());
@ -561,7 +560,6 @@ impl ActiveEventLoop {
let canvas = canvas_clone.clone();
move |new_size| {
let canvas = canvas.borrow();
canvas.set_current_size(new_size);
if canvas.old_size() != new_size {
canvas.set_old_size(new_size);
@ -579,7 +577,7 @@ impl ActiveEventLoop {
canvas.on_intersection(move |is_intersecting| {
// only fire if visible while skipping the first event if it's intersecting
if backend::is_visible(runner.document())
&& !(is_intersecting && canvas_clone.borrow().is_intersecting.is_none())
&& !(is_intersecting && canvas_clone.is_intersecting.get().is_none())
{
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
@ -587,7 +585,7 @@ impl ActiveEventLoop {
});
}
canvas_clone.borrow_mut().is_intersecting = Some(is_intersecting);
canvas_clone.is_intersecting.set(Some(is_intersecting));
});
let runner = self.runner.clone();
@ -654,7 +652,7 @@ impl ActiveEventLoop {
self.runner.wait_until_strategy()
}
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
pub(crate) fn waker(&self) -> Waker<WeakShared> {
self.runner.waker()
}