gdnative_async/
future.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4use std::task::{Context, Poll};
5
6use atomic_waker::AtomicWaker;
7use crossbeam_channel::{Receiver, Sender};
8
9pub(crate) fn make<T>() -> (Yield<T>, Resume<T>) {
10    let (arg_send, arg_recv) = crossbeam_channel::bounded(1);
11    let waker = Arc::default();
12
13    let future = Yield {
14        waker: Arc::clone(&waker),
15        arg_recv,
16    };
17
18    let resume = Resume { waker, arg_send };
19
20    (future, resume)
21}
22
23/// Future that can be `await`ed for a signal or a `resume` call from Godot. See
24/// [`Context`](crate::Context) for methods that return this future.
25pub struct Yield<T> {
26    waker: Arc<AtomicWaker>,
27    arg_recv: Receiver<T>,
28}
29
30impl<T: Send> Future for Yield<T> {
31    type Output = T;
32    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
33        match self.arg_recv.try_recv() {
34            Ok(arg) => Poll::Ready(arg),
35            Err(_) => {
36                self.waker.register(cx.waker());
37                Poll::Pending
38            }
39        }
40    }
41}
42
43pub(crate) struct Resume<T> {
44    waker: Arc<AtomicWaker>,
45    arg_send: Sender<T>,
46}
47
48impl<T: Send> Resume<T> {
49    /// Resume the task with a given argument from GDScript.
50    pub fn resume(self, arg: T) {
51        self.arg_send
52            .send(arg)
53            .expect("sender should not become disconnected");
54
55        self.waker.wake();
56    }
57}