winit_runtime/executor/
handle.rs

1/*
2 * Created on Thu Aug 17 2023
3 *
4 * Copyright (c) storycraft. Licensed under the MIT Licence.
5 */
6
7use std::thread::{self, ThreadId};
8
9use async_task::{Runnable, Task};
10use futures_intrusive::timer::TimerFuture;
11use futures_lite::Future;
12use instant::Duration;
13use parking_lot::Mutex;
14use winit::event_loop::{EventLoop, EventLoopProxy};
15
16use crate::timer::ExecutorTimer;
17
18use super::event::ExecutorEvent;
19
20/// Handle task spawning and timer
21#[derive(Debug)]
22pub struct ExecutorHandle {
23    thread_id: ThreadId,
24    proxy: Mutex<EventLoopProxy<ExecutorEvent>>,
25
26    pub(super) timer: ExecutorTimer,
27}
28
29impl ExecutorHandle {
30    pub(crate) fn new(event_loop: &EventLoop<ExecutorEvent>) -> Self {
31        Self {
32            thread_id: thread::current().id(),
33            proxy: Mutex::new(event_loop.create_proxy()),
34
35            timer: ExecutorTimer::new(),
36        }
37    }
38
39    /// Exit event loop with exit code
40    pub async fn exit(&self) -> ! {
41        self.proxy.lock().send_event(ExecutorEvent::Exit).unwrap();
42        futures_lite::future::pending().await
43    }
44
45    /// Create Future waiting for given duration.
46    pub fn wait(&self, delay: Duration) -> TimerFuture {
47        let fut = self.timer.delay(delay);
48
49        self.proxy.lock().send_event(ExecutorEvent::Wake).unwrap();
50
51        fut
52    }
53
54    /// Create Future waiting for given timestamp
55    pub fn wait_deadline(&self, timestamp: u64) -> TimerFuture {
56        let fut = self.timer.deadline(timestamp);
57
58        self.proxy.lock().send_event(ExecutorEvent::Wake).unwrap();
59
60        fut
61    }
62
63    /// Spawn a new task, running on runtime thread
64    ///
65    /// Because it can be called on outside of runtime thread, the Future and its output must be [`Send`]
66    pub fn spawn<Fut>(&self, fut: Fut) -> Task<Fut::Output>
67    where
68        Fut: Future + Send + 'static,
69        Fut::Output: Send + 'static,
70    {
71        // SAFETY: Future and its output is both Send and 'static
72        unsafe { self.spawn_unchecked(fut) }
73    }
74
75    /// Spawn and run new task, on runtime thread.
76    ///
77    /// Unlike `ExecutorHandle::spawn` this method check if this method called on runtime's thread and will panic if it didn't.
78    /// Therefore the Future and its output does not need to be [`Send`]
79    pub fn spawn_local<Fut>(&self, fut: Fut) -> Task<Fut::Output>
80    where
81        Fut: Future + 'static,
82        Fut::Output: 'static,
83    {
84        if thread::current().id() != self.thread_id {
85            panic!("Cannot call spawn_local outside of event loop thread");
86        }
87
88        // SAFETY: Future runs on same thread and its output is 'static
89        unsafe { self.spawn_unchecked(fut) }
90    }
91
92    /// Spawn and run new task, without checking Future and its output's bound.
93    ///
94    /// # Safety
95    /// If [`Future`] and its output is
96    /// 1. not [`Send`]: Must be called on main thread.
97    /// 2. non 'static: References to Future must outlive.
98    pub unsafe fn spawn_unchecked<Fut>(&self, fut: Fut) -> Task<Fut::Output>
99    where
100        Fut: Future,
101    {
102        let (runnable, task) = self.spawn_raw_unchecked(fut);
103        runnable.schedule();
104
105        task
106    }
107
108    /// # Safety
109    /// See [`ExecutorHandle::spawn_unchecked`]
110    pub(super) unsafe fn spawn_raw_unchecked<Fut>(&self, fut: Fut) -> (Runnable, Task<Fut::Output>)
111    where
112        Fut: Future,
113    {
114        let proxy = self.proxy.lock().clone();
115
116        async_task::spawn_unchecked(fut, move |runnable| {
117            let _ = proxy.send_event(ExecutorEvent::PollTask(runnable));
118        })
119    }
120}