async_component_winit/executor/
mod.rs

1//! Specialized async Executor built on top of winit event loop for running [`AsyncComponent`]
2
3pub mod signal;
4#[cfg(target_arch = "wasm32")]
5mod wasm;
6
7use std::{
8    sync::atomic::Ordering,
9    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
10};
11
12use async_component_core::{context::ComponentStream, AsyncComponent};
13use futures::{Stream, StreamExt};
14use winit::{
15    event::Event,
16    event_loop::{ControlFlow, EventLoop},
17};
18
19use crate::WinitComponent;
20
21use ref_extended::ref_extended;
22
23use self::signal::WinitSignal;
24
25/// Reserved zero sized user event struct used for waking winit eventloop
26#[derive(Debug, Clone, Copy)]
27#[non_exhaustive]
28pub struct ExecutorPollEvent;
29
30/// Executor implemented on top of winit eventloop using user event.
31///
32/// See [`WinitSignal`] for more detail how it utilize winit user event.
33#[derive(Debug)]
34pub struct WinitExecutor {
35    event_loop: Option<EventLoop<ExecutorPollEvent>>,
36
37    state_signal: WinitSignal,
38}
39
40impl WinitExecutor {
41    /// Create new [`WinitExecutor`]
42    pub fn new(event_loop: EventLoop<ExecutorPollEvent>) -> Self {
43        let state_signal = WinitSignal::new(event_loop.create_proxy());
44
45        Self {
46            event_loop: Some(event_loop),
47
48            state_signal,
49        }
50    }
51
52    fn poll_stream(&'static self, mut stream: impl Stream + Unpin) -> Poll<()> {
53        if self
54            .state_signal
55            .scheduled
56            .compare_exchange(true, false, Ordering::AcqRel, Ordering::Acquire)
57            .is_ok()
58        {
59            if let Poll::Ready(Some(_)) =
60                stream.poll_next_unpin(&mut Context::from_waker(&unsafe {
61                    Waker::from_raw(create_raw_waker(&self.state_signal))
62                }))
63            {
64                self.state_signal.scheduled.store(true, Ordering::Release);
65            }
66
67            Poll::Ready(())
68        } else {
69            Poll::Pending
70        }
71    }
72
73    /// Initializes the winit event loop and run component.
74    ///
75    /// See [`EventLoop`] for more detail about winit event loop
76    pub fn run<C: AsyncComponent + WinitComponent + 'static>(
77        mut self,
78        func: impl FnOnce() -> C,
79    ) -> ! {
80        let event_loop = self.event_loop.take().unwrap();
81
82        let mut stream = ComponentStream::new(func);
83
84        let executor = self;
85        ref_extended!(|&executor| event_loop.run(move |event, _, control_flow| {
86            let mut stream = stream.enter();
87
88            match event {
89                Event::MainEventsCleared => {
90                    stream
91                        .component_mut()
92                        .on_event(&mut Event::MainEventsCleared, control_flow);
93
94                    if let ControlFlow::ExitWithCode(_) = control_flow {
95                        return;
96                    }
97
98                    match executor.poll_stream(&mut stream) {
99                        Poll::Ready(_) => {
100                            control_flow.set_poll();
101                        }
102
103                        Poll::Pending => {
104                            control_flow.set_wait();
105                        }
106                    }
107                }
108
109                // Handled in Event::MainEventsCleared
110                Event::UserEvent(_) => {}
111
112                _ => {
113                    stream
114                        .component_mut()
115                        .on_event(&mut event.map_nonuser_event().unwrap(), control_flow);
116                }
117            }
118        }))
119    }
120}
121
122fn create_raw_waker(signal: &'static WinitSignal) -> RawWaker {
123    unsafe fn waker_clone(this: *const ()) -> RawWaker {
124        create_raw_waker(&*(this as *const WinitSignal))
125    }
126
127    unsafe fn waker_wake(this: *const ()) {
128        let this = &*(this as *const WinitSignal);
129        this.wake_by_ref();
130    }
131
132    unsafe fn waker_wake_by_ref(this: *const ()) {
133        let this = &*(this as *const WinitSignal);
134        this.wake_by_ref();
135    }
136
137    unsafe fn waker_drop(_: *const ()) {}
138
139    RawWaker::new(
140        signal as *const _ as *const (),
141        &RawWakerVTable::new(waker_clone, waker_wake, waker_wake_by_ref, waker_drop),
142    )
143}