use pool::Pool; use task::Task; use std::mem; use std::ops; use std::sync::Arc; use futures::executor::Notify; /// Implements the future `Notify` API. /// /// This is how external events are able to signal the task, informing it to try /// to poll the future again. #[derive(Debug)] pub(crate) struct Notifier { pub pool: Arc, } /// A guard that ensures that the inner value gets forgotten. #[derive(Debug)] struct Forget(Option); impl Notify for Notifier { fn notify(&self, id: usize) { trace!("Notifier::notify; id=0x{:x}", id); unsafe { let ptr = id as *const Task; // We did not actually take ownership of the `Arc` in this function // so we must ensure that the Arc is forgotten. let task = Forget::new(Arc::from_raw(ptr)); // TODO: Unify this with Task::notify if task.schedule() { // TODO: Check if the pool is still running // // Bump the ref count let task = task.clone(); let _ = self.pool.submit(task, &self.pool); } } } fn clone_id(&self, id: usize) -> usize { let ptr = id as *const Task; // This function doesn't actually get a strong ref to the task here. // However, the only method we have to convert a raw pointer -> &Arc // is to call `Arc::from_raw` which returns a strong ref. So, to // maintain the invariants, `t1` has to be forgotten. This prevents the // ref count from being decremented. let t1 = Forget::new(unsafe { Arc::from_raw(ptr) }); // The clone is forgotten so that the fn exits without decrementing the ref // count. The caller of `clone_id` ensures that `drop_id` is called when // the ref count needs to be decremented. let _ = Forget::new(t1.clone()); id } fn drop_id(&self, id: usize) { unsafe { let ptr = id as *const Task; let _ = Arc::from_raw(ptr); } } } // ===== impl Forget ===== impl Forget { fn new(t: T) -> Self { Forget(Some(t)) } } impl ops::Deref for Forget { type Target = T; fn deref(&self) -> &T { self.0.as_ref().unwrap() } } impl Drop for Forget { fn drop(&mut self) { mem::forget(self.0.take()); } }