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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
//! An executor to run futures in the idle cycles of a Glib/Gtk application.
//!
//! This crate provides a function that programs a future to be run using the
//! _idle_ time of the main loop, as provided by [`glib::idle_add`](../glib/source/fn.idle_add.html).
//!
//! It works by registering an idle function to poll the future once, and whenever the future is
//! awoken, it will register another idle function to be continued, and so on.
//!
//! The main advantage of this approach is that the future is run in the main loop.
//! As such, it does not need to be `Send`, it does not require any kind of synchronization
//! to access shared state, and it can even manipulate GUI objects directly.
//!
//! Naturally, you still need to be careful, because if you set up several idle futures at the same
//! time they will be run intermixed.
//!
//! # What can it be used for?
//!
//! You can use this crate to implement async I/O that handles the GUI without the need of
//! synchronization. For example you can download assets or updates from the internet, or provide
//! an API for automating your GUI.
//!
//! You can also use async functions to implement background tasks that run when the program is
//! idle. They can be interrupted at any time and they can update the GUI directly. A similar
//! technique can be used to easily build a progress bar for a long-running process. Just add
//! something like this call here and there:
//! ```
//! async_std::task::yield_now().await;
//! ```
//!
//! # Working with other async frameworks
//!
//! You can run almost any future as an idle future, but if it uses functionality from any async
//! framework (`async_std`, `smol`, `tokio`...) it will most likely need to be initialized first,
//! or else your futures will not advance. This is because these frameworks usually work by creating
//! a background thread that does the actual polling and wakes up the other futures. If this
//! background thread is not created, nothing will be polled and your futures will stall forever.
//!
//! You may think that interacting with other fraworks is going to be difficult. But actually it is
//! super easy, barely an inconvenience:
//!
//! ## `async_std`
//!
//! Spawning any async task will bootstrap the runtime, so just this from your `main` is enough:
//! ```
//! async_std::task::spawn(async {});
//! ```
//!
//! ## `smol`
//!
//! There must be at least one thread runing an async job, and it will do that and poll all the
//! futures. So the code would be something like this, that will spawn a never-ending future:
//! ```
//! std::thread::spawn(|| smol::run(futures::future::pending::<()>()));
//! ```
//! If you also use `async_std`, since it uses `smol` under the hood, you just need one of these.
//!
//! ## `tokio`
//!
//! In `tokio` futures need to be spawn from a tokio reactor. It is enough if you run your main loop from there:
//! ```
//!     let mut rt = tokio::runtime::Builder::new()
//!       .threaded_scheduler()
//!       .enable_all()
//!       .build()
//!       .unwrap();
//!    rt.block_on(async { gtk::main() });
//!```
//!
//!Or you can decorate your `main` function with:
//!```
//!#[tokio::main]
//!async fn main() -> Result<(), Box<dyn std::error::Error>> {
//!    //...
//!}
//!```

use futures::prelude::*;
use std::{
    future::Future,
    sync::Arc,
    rc::Rc,
    cell::Cell,
    pin::Pin,
    task::{
        RawWaker,
        RawWakerVTable,
        Waker,
        Context,
        Poll,
    }
};

//The raw pointer in the RawWaker will be a pointer to an Arc-allocated GIdleTask
struct GIdleTask {
    future: Cell<Option<Pin<Box<dyn Future<Output = ()> + 'static>>>>,
}

#[inline]
unsafe fn increment_arc_count(ptr: *const ()) {
    let rc = Arc::from_raw(ptr as *const GIdleTask);
    std::mem::forget(rc.clone());
    std::mem::forget(rc);
}
#[inline]
unsafe fn decrement_arc_count(ptr: *const()) {
    Arc::from_raw(ptr as *const GIdleTask);
}

unsafe fn gwaker_clone(ptr: *const ()) -> RawWaker {
    increment_arc_count(ptr);
    RawWaker::new(
        ptr,
        &GWAKER_VTABLE
    )
}
unsafe fn gwaker_wake(ptr: *const ()) {
    //poll_idle consumes one reference count, as wake requires, so nothing to do
    poll_idle(ptr);
}
unsafe fn gwaker_wake_by_ref(ptr: *const ()) {
    //poll_idle consumes one reference count, so we have to increment it here one
    increment_arc_count(ptr);
    poll_idle(ptr);
}
unsafe fn gwaker_drop(ptr: *const ()) {
    decrement_arc_count(ptr);
}

static GWAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
    gwaker_clone,
    gwaker_wake,
    gwaker_wake_by_ref,
    gwaker_drop,
);

//Actually the inner future is not Send, but GIdleTask is private to this module, so if
//we are careful we can move it between threads, as long as we only use the future in the
//main thread.
unsafe impl Send for GIdleTask {}
unsafe impl Sync for GIdleTask {}

//poll_idle() can be called from an arbitrary thread, because Waker is Send,
//but once we are in the glib::source::idle_add() callback we are in the main loop.
//When it ends, Waker::drop decrements the counter for the Arc<GIdleTask>.
fn poll_idle(ptr: *const ()) {
    let task = unsafe { &*(ptr as *const GIdleTask) };
    glib::source::idle_add(move || {
        let raw = RawWaker::new(
            task as *const GIdleTask as *const (),
            &GWAKER_VTABLE
        );
        let waker = unsafe { Waker::from_raw(raw) };

        //It is unlikely but the call to poll could call gtk::main_iteration() or similar and
        //reenter the this idle call. We avoid reentering the future by taking it from the
        //GIdleTask and storing it later if it returns pending.
        //This has the additional advantage that the future is automatically fused.
        let mut op_future = task.future.take();

        //If the future has finished, drop it, a home-made fuse.
        if let Some(future) = op_future.as_mut() {
            let mut ctx = Context::from_waker(&waker);
            match future.as_mut().poll(&mut ctx) {
                Poll::Ready(()) => {}
                Poll::Pending => {
                    task.future.set(op_future);
                }
            }
        }
        glib::source::Continue(false)
    });
}

/// Spawns an idle future.
///
/// This function registers the given future to be run in the idle time of the main Glib loop.
///
/// It must be called from the main Glib loop or it will panic. Since the future will be run in the
/// same thread, it does not need to be `Send`.
///
/// The future can return any value, but it is discarded.
///
/// Currently there is no way to cancel a future. If you need that you can use `futures::future::AbortHandle`.
///
pub fn spawn<T, F>(future: F) -> Handle<T>
where T: 'static,
      F: Future<Output = T> + 'static
{
    //Check that we are in the main loop
    assert!(glib::MainContext::default().is_owner(), "idle_spawn can only be called from the glib main loop");

    let res: Rc<Cell<Option<T>>> = Rc::new(Cell::new(None));
    let task = Arc::new(GIdleTask {
        future: Cell::new(Some(Box::pin(future.map({
            let res = res.clone();
            move |t| {
                res.set(Some(t))
            }
        }))))
    });
    let wtask = Arc::downgrade(&task);
    let ptr = Arc::into_raw(task) as *const ();
    poll_idle(ptr);
    Handle {
        task: wtask,
        res,
    }
}

/// A handle to a running idle future.
///
/// You can use this handle to cancel the future and to retrieve the return
/// value of a future that has finished.
///
/// If the Handle is dropped, the future will keep on running, and there will
/// be no way to cancel it or get the return value.
pub struct Handle<T> {
    task: std::sync::Weak<GIdleTask>,
    res: Rc<Cell<Option<T>>>,
}

impl<T> Handle<T> {
    /// If the future has finished, it returns `Some(t)`.
    /// If it has not finished, the future is dropped and it returns `None`.
    pub fn cancel(self) -> Option<T> {
        if let Some(task) = self.task.upgrade() {
            task.future.take();
        }
        self.res.take()
    }
    /// Returns `true` if the future has finished, `false` otherwise.
    /// If it returns `true` then you can be sure that [`cancel`](#method.cancel) will return `Some(t)`.
    pub fn has_finished(&self) -> bool {
        match self.res.take() {
            None => false,
            Some(x) => {
                self.res.set(Some(x));
                true
            }
        }
    }
    /// Converts this handle into a future that is satisfied when the original job finishes.
    /// It returns the value returned by the original future.
    pub async fn future(self) -> T {
        if let Some(task) = self.task.upgrade() {
            if let Some(fut) = task.future.take() {
                fut.await;
            }
        }
        //This unwrap() cannot fail: if the future has finished, then self.res must be Some,
        //because the last action of the future is assigning to it.
        //And the future cannot be cancelled, because both Handle::cancel() and Handle::future()
        //consume self, thus only one of them can be used.
        self.res.take().unwrap()
    }
}