1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
 * Created on Thu Aug 17 2023
 *
 * Copyright (c) storycraft. Licensed under the MIT Licence.
 */

use std::thread::{ThreadId, self};

use async_task::{Runnable, Task};
use futures_intrusive::timer::TimerFuture;
use futures_lite::Future;
use instant::Duration;
use winit::event_loop::{EventLoopProxy, EventLoop};

use crate::timer::ExecutorTimer;

use super::event::ExecutorEvent;

/// Handle task spawning and timer
#[derive(Debug)]
pub struct ExecutorHandle {
    thread_id: ThreadId,
    proxy: EventLoopProxy<ExecutorEvent>,

    pub(super) timer: ExecutorTimer,
}

impl ExecutorHandle {
    pub(crate) fn new(event_loop: &EventLoop<ExecutorEvent>) -> Self {
        Self {
            thread_id: thread::current().id(),
            proxy: event_loop.create_proxy(),

            timer: ExecutorTimer::new(),
        }
    }

    /// Exit event loop with exit code
    pub async fn exit(&self, code: i32) -> ! {
        self.proxy.send_event(ExecutorEvent::Exit(code)).unwrap();
        futures_lite::future::pending().await
    }

    /// Create Future waiting for given duration.
    pub fn wait(&self, delay: Duration) -> TimerFuture {
        let fut = self.timer.delay(delay);

        self.proxy.send_event(ExecutorEvent::Wake).unwrap();

        fut
    }

    /// Create Future waiting for given timestamp
    pub fn wait_deadline(&self, timestamp: u64) -> TimerFuture {
        let fut = self.timer.deadline(timestamp);

        self.proxy.send_event(ExecutorEvent::Wake).unwrap();

        fut
    }

    /// Spawn a new task, running on runtime thread
    /// 
    /// Because it can be called on outside of runtime thread, the Future and its output must be [`Send`]
    pub fn spawn<Fut>(&self, fut: Fut) -> Task<Fut::Output>
    where
        Fut: Future + Send + 'static,
        Fut::Output: Send + 'static,
    {
        // SAFETY: Future and its output is both Send and 'static
        unsafe { self.spawn_unchecked(fut) }
    }

    /// Spawn and run new task, on runtime thread.
    /// 
    /// Unlike `ExecutorHandle::spawn` this method check if this method called on runtime's thread and will panic if it didn't.
    /// Therefore the Future and its output does not need to be [`Send`]
    pub fn spawn_local<Fut>(&self, fut: Fut) -> Task<Fut::Output>
    where
        Fut: Future + 'static,
        Fut::Output: 'static,
    {
        if thread::current().id() != self.thread_id {
            panic!("Cannot call spawn_local outside of event loop thread");
        }

        // SAFETY: Future runs on same thread and its output is 'static
        unsafe { self.spawn_unchecked(fut) }
    }

    /// Spawn and run new task, without checking Future and its output's bound.
    /// 
    /// # Safety
    /// If [`Future`] and its output is
    /// 1. not [`Send`]: Must be called on main thread.
    /// 2. non 'static: References to Future must outlive.
    pub unsafe fn spawn_unchecked<Fut>(&self, fut: Fut) -> Task<Fut::Output>
    where
        Fut: Future,
    {
        let (runnable, task) = self.spawn_raw_unchecked(fut);
        runnable.schedule();

        task
    }

    /// # Safety
    /// See [`ExecutorHandle::spawn_unchecked`]
    pub(super) unsafe fn spawn_raw_unchecked<Fut>(&self, fut: Fut) -> (Runnable, Task<Fut::Output>)
    where
        Fut: Future,
    {
        let proxy = self.proxy.clone();

        async_task::spawn_unchecked(fut, move |runnable| {
            let _ = proxy.send_event(ExecutorEvent::PollTask(runnable));
        })
    }
}