Add EventLoopProxy::into_waker

This commit is contained in:
Mads Marquart
2025-03-17 13:16:37 +01:00
parent 488c036a05
commit 2a391b348a
3 changed files with 82 additions and 9 deletions

View File

@@ -1,9 +1,12 @@
use std::os::raw::c_void; use std::os::raw::c_void;
use std::ptr::NonNull;
use std::sync::Arc; use std::sync::Arc;
use std::task::{RawWaker, RawWakerVTable, Waker};
use objc2::MainThreadMarker; use objc2::MainThreadMarker;
use objc2_core_foundation::{ use objc2_core_foundation::{
kCFRunLoopCommonModes, CFIndex, CFRetained, CFRunLoop, CFRunLoopSource, CFRunLoopSourceContext, kCFRunLoopCommonModes, CFIndex, CFRetained, CFRunLoop, CFRunLoopSource, CFRunLoopSourceContext,
Type,
}; };
use winit_core::event_loop::EventLoopProxyProvider; use winit_core::event_loop::EventLoopProxyProvider;
@@ -119,4 +122,54 @@ impl EventLoopProxyProvider for EventLoopProxy {
// main loop may be sleeping (and `CFRunLoopSourceSignal` won't wake it). // main loop may be sleeping (and `CFRunLoopSourceSignal` won't wake it).
self.main_loop.wake_up(); self.main_loop.wake_up();
} }
fn waker(&self) -> Waker {
const VTABLE: RawWakerVTable =
RawWakerVTable::new(clone_waker, wake, wake_by_ref, drop_waker);
unsafe fn clone_waker(data: *const ()) -> RawWaker {
// SAFETY: The poiner came from `CFRunLoopSource` and is valid and non-null.
let source = unsafe { &*data.cast::<CFRunLoopSource>() };
// Increment reference count.
let source = source.retain();
// Pass ownership to the raw waker.
let data: *const CFRunLoopSource = CFRetained::into_raw(source).as_ptr();
RawWaker::new(data.cast(), &VTABLE)
}
unsafe fn wake(data: *const ()) {
unsafe { wake_by_ref(data) };
unsafe { drop_waker(data) };
}
unsafe fn wake_by_ref(data: *const ()) {
// SAFETY: The poiner came from `CFRunLoopSource` and is valid and non-null.
let source = unsafe { &*data.cast::<CFRunLoopSource>() };
// Signal the source, which ends up later invoking `perform` on the main thread.
//
// Multiple signals in quick succession are automatically coalesced into a single
// signal.
source.signal();
let main_loop = CFRunLoop::main().unwrap();
// Let the main thread know there's a new event.
//
// This is required since we may be (probably are) running on a different thread, and
// the main loop may be sleeping (and `CFRunLoopSourceSignal` won't wake it).
main_loop.wake_up();
}
unsafe fn drop_waker(data: *const ()) {
let source = data.cast::<CFRunLoopSource>().cast_mut();
// SAFETY: The poiner came from `CFRunLoopSource` and is valid and non-null.
// We take ownership of a retain count here.
let _source = unsafe { CFRetained::from_raw(NonNull::new_unchecked(source)) };
}
let data: *const CFRunLoopSource = CFRetained::into_raw(self.source.clone()).as_ptr();
unsafe { Waker::from_raw(RawWaker::new(data.cast(), &VTABLE)) }
}
} }

View File

@@ -4,6 +4,7 @@ pub mod run_on_demand;
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::task::Waker;
use std::time::Duration; use std::time::Duration;
use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle}; use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
@@ -150,6 +151,13 @@ impl EventLoopProxy {
self.proxy.wake_up(); self.proxy.wake_up();
} }
/// Get a [`Waker`] that calls [`wake_up`] on the proxy when awoken.
///
/// This may be useful to `async` code or otherwise interoperating with `std`.
pub fn waker(&self) -> Waker {
self.proxy.waker()
}
pub fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self { pub fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self {
Self { proxy } Self { proxy }
} }
@@ -158,6 +166,9 @@ impl EventLoopProxy {
pub trait EventLoopProxyProvider: Send + Sync + Debug { pub trait EventLoopProxyProvider: Send + Sync + Debug {
/// See [`EventLoopProxy::wake_up`] for details. /// See [`EventLoopProxy::wake_up`] for details.
fn wake_up(&self); fn wake_up(&self);
/// See [`EventLoopProxy::waker`] for details.
fn waker(&self) -> Waker;
} }
/// A proxy for the underlying display handle. /// A proxy for the underlying display handle.

View File

@@ -2,6 +2,7 @@ use std::cell::Cell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{mpsc, Arc, Mutex}; use std::sync::{mpsc, Arc, Mutex};
use std::task::Waker;
use std::time::Instant; use std::time::Instant;
use std::{iter, mem, slice}; use std::{iter, mem, slice};
@@ -276,7 +277,6 @@ impl EventState {
pub struct EventLoop { pub struct EventLoop {
windows: Vec<(Arc<RedoxSocket>, EventState)>, windows: Vec<(Arc<RedoxSocket>, EventState)>,
window_target: ActiveEventLoop, window_target: ActiveEventLoop,
user_events_receiver: mpsc::Receiver<()>,
} }
impl EventLoop { impl EventLoop {
@@ -577,7 +577,7 @@ impl EventLoop {
i += 1; i += 1;
} }
while self.user_events_receiver.try_recv().is_ok() { if self.wake_up.swap(false, Ordering::Relaxed) {
app.proxy_wake_up(&self.window_target); app.proxy_wake_up(&self.window_target);
} }
@@ -666,17 +666,26 @@ impl EventLoop {
#[derive(Debug)] #[derive(Debug)]
pub struct EventLoopProxy { pub struct EventLoopProxy {
user_events_sender: mpsc::SyncSender<()>, waker: Waker,
pub(super) wake_socket: TimeSocket,
} }
impl EventLoopProxyProvider for EventLoopProxy { impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) { fn wake_up(&self) {
// When we fail to send the event it means that we haven't woken up to read the previous self.waker.wake_by_ref();
// event.
if self.user_events_sender.try_send(()).is_ok() {
self.wake_socket.wake().unwrap();
} }
fn into_waker(self) -> Waker {
self.waker
}
}
impl std::task::Wake for TimeSocket {
fn wake(self: Arc<TimeSocket>) {
TimeSocket::wake(&*self).unwrap();
}
fn wake_by_ref(self: &Arc<TimeSocket>) {
TimeSocket::wake(&**self).unwrap();
} }
} }