api: make EventLoopProxy wrap an opaque type

The proxy is intended to be Clone, thus use `Arc` for it internally and
don't require backends for it to be `Clone`. Use `EventLoopProxyProvider`
to hide the backend's proxy implementation details.

Co-authored-by: daxpedda <daxpedda@gmail.com>
This commit is contained in:
Kirill Chibisov 2024-11-12 10:56:20 +03:00 committed by GitHub
parent ae4c449670
commit 3a60cbaba5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 256 additions and 307 deletions

View file

@ -6,15 +6,13 @@ mod channel;
mod concurrent_queue;
mod dispatcher;
mod notifier;
mod waker;
mod wrapper;
use atomic_waker::AtomicWaker;
pub(crate) use atomic_waker::AtomicWaker;
use concurrent_queue::{ConcurrentQueue, PushError};
pub use self::abortable::{AbortHandle, Abortable, DropAbortHandle};
pub use self::channel::{channel, Receiver, Sender};
pub use self::dispatcher::{DispatchRunner, Dispatcher};
pub use self::notifier::{Notified, Notifier};
pub use self::waker::{Waker, WakerSpawner};
use self::wrapper::Wrapper;
pub(crate) use self::wrapper::Wrapper;

View file

@ -1,117 +0,0 @@
use std::future;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::Poll;
use super::super::main_thread::MainThreadMarker;
use super::{AtomicWaker, Wrapper};
pub struct WakerSpawner<T: 'static>(Wrapper<Handler<T>, Sender, ()>);
pub struct Waker<T: 'static>(Wrapper<Handler<T>, Sender, ()>);
struct Handler<T> {
value: T,
handler: fn(&T, bool),
}
#[derive(Clone)]
struct Sender(Arc<Inner>);
impl<T> WakerSpawner<T> {
pub fn new(main_thread: MainThreadMarker, value: T, handler: fn(&T, bool)) -> Self {
let inner = Arc::new(Inner {
awoken: AtomicBool::new(false),
waker: AtomicWaker::new(),
closed: AtomicBool::new(false),
});
let handler = Handler { value, handler };
let sender = Sender(Arc::clone(&inner));
Self(Wrapper::new(
main_thread,
handler,
|handler, _| {
let handler = handler.borrow();
let handler = handler.as_ref().unwrap();
(handler.handler)(&handler.value, true);
},
{
let inner = Arc::clone(&inner);
move |handler| async move {
while future::poll_fn(|cx| {
if inner.awoken.swap(false, Ordering::Relaxed) {
Poll::Ready(true)
} else {
inner.waker.register(cx.waker());
if inner.awoken.swap(false, Ordering::Relaxed) {
Poll::Ready(true)
} else {
if inner.closed.load(Ordering::Relaxed) {
return Poll::Ready(false);
}
Poll::Pending
}
}
})
.await
{
let handler = handler.borrow();
let handler = handler.as_ref().unwrap();
(handler.handler)(&handler.value, false);
}
}
},
sender,
|inner, _| {
inner.0.awoken.store(true, Ordering::Relaxed);
inner.0.waker.wake();
},
))
}
pub fn waker(&self) -> Waker<T> {
Waker(self.0.clone())
}
pub fn take(&self) -> bool {
debug_assert!(
MainThreadMarker::new().is_some(),
"this should only be called from the main thread"
);
self.0.with_sender_data(|inner| inner.0.awoken.swap(false, Ordering::Relaxed))
}
}
impl<T> Drop for WakerSpawner<T> {
fn drop(&mut self) {
self.0.with_sender_data(|inner| {
inner.0.closed.store(true, Ordering::Relaxed);
inner.0.waker.wake();
});
}
}
impl<T> Waker<T> {
pub fn wake(&self) {
self.0.send(())
}
}
impl<T> Clone for Waker<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
struct Inner {
awoken: AtomicBool,
waker: AtomicWaker,
closed: AtomicBool,
}

View file

@ -10,7 +10,6 @@ pub(crate) mod runner;
mod state;
mod window_target;
pub(crate) use proxy::EventLoopProxy;
pub(crate) use window_target::{ActiveEventLoop, OwnedDisplayHandle};
pub struct EventLoop {

View file

@ -1,17 +1,101 @@
use super::runner::WeakShared;
use crate::platform_impl::platform::r#async::Waker;
use std::future;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::Poll;
#[derive(Clone)]
pub struct EventLoopProxy {
runner: Waker<WeakShared>,
use super::super::main_thread::MainThreadMarker;
use crate::event_loop::EventLoopProxyProvider;
use crate::platform_impl::web::event_loop::runner::WeakShared;
use crate::platform_impl::web::r#async::{AtomicWaker, Wrapper};
pub struct EventLoopProxy(Wrapper<WeakShared, Arc<State>, ()>);
struct State {
awoken: AtomicBool,
waker: AtomicWaker,
closed: AtomicBool,
}
impl EventLoopProxy {
pub fn new(runner: Waker<WeakShared>) -> Self {
Self { runner }
pub fn new(main_thread: MainThreadMarker, runner: WeakShared) -> Self {
let state = Arc::new(State {
awoken: AtomicBool::new(false),
waker: AtomicWaker::new(),
closed: AtomicBool::new(false),
});
Self(Wrapper::new(
main_thread,
runner,
|runner, _| {
let runner = runner.borrow();
let runner = runner.as_ref().unwrap();
if let Some(runner) = runner.upgrade() {
runner.send_proxy_wake_up(true);
}
},
{
let state = Arc::clone(&state);
move |runner| async move {
while future::poll_fn(|cx| {
if state.awoken.swap(false, Ordering::Relaxed) {
Poll::Ready(true)
} else {
state.waker.register(cx.waker());
if state.awoken.swap(false, Ordering::Relaxed) {
Poll::Ready(true)
} else {
if state.closed.load(Ordering::Relaxed) {
return Poll::Ready(false);
}
Poll::Pending
}
}
})
.await
{
let runner = runner.borrow();
let runner = runner.as_ref().unwrap();
if let Some(runner) = runner.upgrade() {
runner.send_proxy_wake_up(false);
}
}
}
},
state,
|state, _| {
state.awoken.store(true, Ordering::Relaxed);
state.waker.wake();
},
))
}
pub fn wake_up(&self) {
self.runner.wake();
pub fn take(&self) -> bool {
debug_assert!(
MainThreadMarker::new().is_some(),
"this should only be called from the main thread"
);
self.0.with_sender_data(|state| state.awoken.swap(false, Ordering::Relaxed))
}
}
impl Drop for EventLoopProxy {
fn drop(&mut self) {
self.0.with_sender_data(|state| {
state.closed.store(true, Ordering::Relaxed);
state.waker.wake();
});
}
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.0.send(())
}
}

View file

@ -3,6 +3,7 @@ use std::collections::{HashSet, VecDeque};
use std::iter;
use std::ops::Deref;
use std::rc::{Rc, Weak};
use std::sync::Arc;
use wasm_bindgen::prelude::Closure;
use wasm_bindgen::JsCast;
@ -13,13 +14,14 @@ use super::super::event;
use super::super::main_thread::MainThreadMarker;
use super::super::monitor::MonitorHandler;
use super::backend;
use super::proxy::EventLoopProxy;
use super::state::State;
use crate::dpi::PhysicalSize;
use crate::event::{DeviceEvent, ElementState, Event, RawKeyEvent, StartCause, WindowEvent};
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::platform::web::{PollStrategy, WaitUntilStrategy};
use crate::platform_impl::platform::backend::EventListenerHandle;
use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
use crate::platform_impl::platform::r#async::DispatchRunner;
use crate::platform_impl::platform::window::Inner;
use crate::window::WindowId;
@ -37,7 +39,7 @@ type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
struct Execution {
main_thread: MainThreadMarker,
proxy_spawner: WakerSpawner<WeakShared>,
event_loop_proxy: Arc<EventLoopProxy>,
control_flow: Cell<ControlFlow>,
poll_strategy: Cell<PollStrategy>,
wait_until_strategy: Cell<WaitUntilStrategy>,
@ -140,12 +142,7 @@ impl Shared {
let document = window.document().expect("Failed to obtain document");
Shared(Rc::<Execution>::new_cyclic(|weak| {
let proxy_spawner =
WakerSpawner::new(main_thread, WeakShared(weak.clone()), |runner, local| {
if let Some(runner) = runner.upgrade() {
runner.send_proxy_wake_up(local);
}
});
let proxy_spawner = EventLoopProxy::new(main_thread, WeakShared(weak.clone()));
let monitor = MonitorHandler::new(
main_thread,
@ -156,7 +153,7 @@ impl Shared {
Execution {
main_thread,
proxy_spawner,
event_loop_proxy: Arc::new(proxy_spawner),
control_flow: Cell::new(ControlFlow::default()),
poll_strategy: Cell::new(PollStrategy::default()),
wait_until_strategy: Cell::new(WaitUntilStrategy::default()),
@ -653,7 +650,7 @@ impl Shared {
// Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle.
events.extend(
self.0
.proxy_spawner
.event_loop_proxy
.take()
.then_some(Event::UserWakeUp)
.map(EventWrapper::from),
@ -818,8 +815,8 @@ impl Shared {
self.0.wait_until_strategy.get()
}
pub(crate) fn waker(&self) -> Waker<WeakShared> {
self.0.proxy_spawner.waker()
pub(crate) fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.0.event_loop_proxy
}
pub(crate) fn weak(&self) -> WeakShared {

View file

@ -2,13 +2,14 @@ use std::cell::Cell;
use std::clone::Clone;
use std::iter;
use std::rc::Rc;
use std::sync::Arc;
use web_sys::Element;
use super::super::monitor::MonitorPermissionFuture;
use super::super::{lock, KeyEventExtra};
use super::runner::{EventWrapper, WeakShared};
use super::{backend, runner, EventLoopProxy};
use super::runner::EventWrapper;
use super::{backend, runner};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{ElementState, Event, KeyEvent, TouchPhase, WindowEvent};
use crate::event_loop::{
@ -19,7 +20,7 @@ use crate::keyboard::ModifiersState;
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::platform::web::{CustomCursorFuture, PollStrategy, WaitUntilStrategy};
use crate::platform_impl::platform::cursor::CustomCursor;
use crate::platform_impl::platform::r#async::Waker;
use crate::platform_impl::web::event_loop::proxy::EventLoopProxy;
use crate::platform_impl::Window;
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId};
@ -476,15 +477,15 @@ impl ActiveEventLoop {
self.runner.monitor().has_detailed_monitor_permission()
}
pub(crate) fn waker(&self) -> Waker<WeakShared> {
self.runner.waker()
pub(crate) fn event_loop_proxy(&self) -> Arc<EventLoopProxy> {
self.runner.event_loop_proxy().clone()
}
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = EventLoopProxy::new(self.waker());
RootEventLoopProxy { event_loop_proxy }
let event_loop_proxy = self.event_loop_proxy();
RootEventLoopProxy::new(event_loop_proxy)
}
fn create_window(

View file

@ -38,8 +38,7 @@ pub(crate) use cursor::{
};
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle,
PlatformSpecificEventLoopAttributes,
ActiveEventLoop, EventLoop, OwnedDisplayHandle, PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::keyboard::KeyEventExtra;
pub(crate) use self::monitor::{