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() } }