├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── futures-borrow ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── borrow.rs ├── futures-test ├── Cargo.toml └── src │ ├── harness.rs │ └── lib.rs └── futures-watch ├── Cargo.toml ├── README.md ├── src ├── lib.rs └── then_stream.rs └── tests └── watch.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: rust 3 | sudo: false 4 | 5 | matrix: 6 | include: 7 | # Minimum rustc version. This should not be changed without a github issue 8 | # to discuss 9 | - rust: 1.21.0 10 | - rust: nightly 11 | 12 | script: 13 | # Run tests for all crates in the workspace. 14 | - cargo test --all 15 | 16 | # Run benchmarks on nightly 17 | - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo bench --all; fi 18 | 19 | # Run any code samples in the readme 20 | - rustdoc --test README.md -L target/debug/deps 21 | 22 | # Build docs 23 | - cargo doc --no-deps 24 | 25 | # Deploy docs 26 | deploy: 27 | provider: pages 28 | skip_cleanup: true 29 | github_token: $GH_TOKEN 30 | target_branch: gh-pages 31 | local_dir: target/doc 32 | on: 33 | branch: master 34 | repo: carllerche/better-future 35 | rust: nightly 36 | 37 | env: 38 | global: 39 | secure: ri4trDseNsS8ruEHGvTcp6AeOitp/on7r1vuLFhmnpXjqbkvKLaY2z6LpTKsQhTDEygs2NN8t3Lzn33jxCYCBsEmWum9ssHlHOY0UwxvENfvUX2T4+u49L5yztuk2cwZHNuSZqAm2d8RR1TqtRR/WtoVr7nOe/Yeftim49oVTY5TuTIQiwFIpBXcMWleqf5jKz37gY00p3MULOVdoxqlMiLH8SZvLv/CcBuMHFuPsqnv6cEzIa7Yc752bPUduCWdLrRe3+bQyTylK+DLeb/3H2pA7T6Tg9y3BOuCg0HX6nK1bbXKuwd9ovhs/gPvplsMpzI2+ybXQS/UK5SQQrWJ49Gm+RMlOPtBUeDjrAF7d0qoPr+neGy/ncQQnwLeuSCpCWZl9icCNFh4iY2Z6MHubQoOQ5YNTlWsuG4bRTND+TuaVhJ18mrupblJUvSlg02rPul/luamwBFXoQ9DHEepKi163P6tZVHiGLmXCL258CwSZNMjoEdgkV+gQgc5WQVRhq0dP+BvVA/93VimmLc/ln/Z/z+Cv3ghBsdz8Le+1iPsFnSRMcjzkH1HfRNDmrhl3yqd4AzcRU76e9/fR6CGfrbuxEcAW6892yiNPkWyNl6pJ+Oz+Bp4QMfU8HFNrS00UYJ0moL6KcgDuj/Y1UvjrN7OE3/b+zy56VHjsdJ/W0I= 40 | 41 | notifications: 42 | email: 43 | on_success: never 44 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "futures-borrow", 4 | "futures-test", 5 | "futures-watch", 6 | ] 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Carl Lerche 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Better Future 2 | 3 | We all wish for a better future -- self-driving cars, fusion power, and UBI. 4 | This repository does not provide any of this. Instead, it has a collection of 5 | utilities that may make working with [futures-rs](http://github.com/alexcrichton/futures-rs) 6 | easier. 7 | 8 | [![Travis Build Status](https://travis-ci.org/carllerche/better-future.svg?branch=master)](https://travis-ci.org/carllerche/better-future) 9 | 10 | ### `futures-borrow` 11 | 12 | Future-aware cell that can move borrows into futures and closures. 13 | 14 | [Documentation](https://carllerche.github.io/better-future/futures_borrow/) 15 | 16 | ```rust 17 | extern crate futures; 18 | extern crate futures_borrow; 19 | 20 | use futures::*; 21 | use futures_borrow::Borrow; 22 | 23 | fn main() { 24 | let borrow = Borrow::new("hello".to_string()); 25 | 26 | // Acquire a borrow 27 | let b = borrow.try_borrow().unwrap(); 28 | 29 | // The borrow is in use 30 | assert!(!borrow.is_ready()); 31 | 32 | // Use the borrow in a closure 33 | future::ok::<_, ()>(()).and_then(move |_| { 34 | println!("value={}", &*b); 35 | Ok(()) 36 | }).wait().unwrap(); 37 | 38 | // A new borrow can be made 39 | assert!(borrow.is_ready()); 40 | } 41 | ``` 42 | 43 | ### `futures-test` 44 | 45 | Utilities for testing futures based code. 46 | 47 | [Documentation](https://carllerche.github.io/better-future/futures_test/) 48 | 49 | ```rust 50 | extern crate futures; 51 | extern crate futures_test; 52 | 53 | use futures::*; 54 | use futures::sync::mpsc; 55 | use futures_test::Harness; 56 | 57 | fn main() { 58 | let (tx, rx) = mpsc::channel(10); 59 | let mut rx = Harness::new(rx); 60 | 61 | assert!(!rx.is_notified()); 62 | // The future is polled out of a task context. 63 | assert!(!rx.poll_next().unwrap().is_ready()); 64 | 65 | tx.send("hello").wait().unwrap(); 66 | 67 | assert!(rx.is_notified()); 68 | } 69 | ``` 70 | 71 | ### `futures-watch` 72 | 73 | A multi-consumer, single producer cell that receives notifications when the 74 | inner value is changed. This allows for efficiently broadcasting values to 75 | multiple watchers. This can be useful for situations like updating configuration 76 | values throughout a system. 77 | 78 | [Documentation](https://carllerche.github.io/better-future/futures_watch/) 79 | 80 | ```rust 81 | extern crate futures; 82 | extern crate futures_watch; 83 | 84 | use futures::{Future, Stream}; 85 | use futures_watch::Watch; 86 | use std::thread; 87 | 88 | fn main() { 89 | let (watch, mut store) = Watch::new("hello"); 90 | 91 | thread::spawn(move || { 92 | store.store("goodbye"); 93 | }); 94 | 95 | watch.into_future() 96 | .and_then(|(_, watch)| { 97 | assert_eq!(*watch.borrow(), "goodbye"); 98 | Ok(()) 99 | }) 100 | .wait().unwrap(); 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /futures-borrow/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /futures-borrow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "futures-borrow" 3 | version = "0.1.0" 4 | authors = ["Carl Lerche "] 5 | 6 | [dependencies] 7 | futures = "0.1" 8 | 9 | [dev-dependencies] 10 | futures-test = { version = "0.1", path = "../futures-test" } 11 | -------------------------------------------------------------------------------- /futures-borrow/README.md: -------------------------------------------------------------------------------- 1 | # Futures Borrow 2 | 3 | Future-aware cell that can move borrows into futures and closures. 4 | 5 | A future-aware borrow allows a value to be borrowed such that the borrow can be 6 | moved into closures passed to `Future` combinators. 7 | 8 | ## Usage 9 | 10 | To use `futures-borrow`, first add this to your `Cargo.toml`: 11 | 12 | ```toml 13 | [dependencies] 14 | futures-borrow = { git = "https://github.com/carllerche/better-future" } 15 | ``` 16 | 17 | Next, add this to your crate: 18 | 19 | ```rust 20 | extern crate futures; 21 | extern crate futures_borrow; 22 | 23 | use futures::*; 24 | use futures_borrow::Borrow; 25 | 26 | fn main() { 27 | let borrow = Borrow::new("hello".to_string()); 28 | 29 | // Acquire a borrow 30 | let b = borrow.try_borrow().unwrap(); 31 | 32 | // The borrow is in use 33 | assert!(!borrow.is_ready()); 34 | 35 | // Use the borrow in a closure 36 | future::ok::<_, ()>(()).and_then(move |_| { 37 | println!("value={}", &*b); 38 | Ok(()) 39 | }).wait().unwrap(); 40 | 41 | // A new borrow can be made 42 | assert!(borrow.is_ready()); 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /futures-borrow/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Futures-aware borrow cell. 2 | //! 3 | //! Given the asynchronous nature of working with futures, managing borrows that 4 | //! live across callbacks can be difficult. This is because lifetimes cannot be 5 | //! moved into closures that are `'static` (i.e. most of the futures-rs 6 | //! combinators). 7 | //! 8 | //! `Borrow` provides runtime checked borrowing, similar to `RefCell`, however 9 | //! `Borrow` also provides `Future` task notifications when borrows are dropped. 10 | 11 | extern crate futures; 12 | 13 | use futures::{Poll, Async}; 14 | use futures::task::AtomicTask; 15 | 16 | use std::{fmt, ops, thread}; 17 | use std::any::Any; 18 | use std::cell::UnsafeCell; 19 | use std::sync::Arc; 20 | use std::sync::atomic::AtomicUsize; 21 | use std::sync::atomic::Ordering::{Acquire, Release}; 22 | 23 | /// A mutable memory location with future-aware dynamically checked borrow 24 | /// rules. 25 | /// 26 | /// Safe borrowing of data across `Future` tasks requires that the data is 27 | /// stored in stable memory. To do this, `Borrow` internally creates an `Arc` to 28 | /// store the data. 29 | /// 30 | /// See crate level documentation for more details. 31 | pub struct Borrow { 32 | /// The borrow state. 33 | /// 34 | /// The state is stored in an `Arc` in order to ensure that it does not move 35 | /// to a different memory location while it is being borrowed. 36 | inner: Arc>, 37 | } 38 | 39 | /// Holds a borrowed value obtained from `Borrow`. 40 | /// 41 | /// When this value is dropped, the borrow is released, notiying any pending 42 | /// tasks. 43 | pub struct BorrowGuard { 44 | /// The borrowed ref. This could be a pointer to an inner field of the `T` 45 | /// stored by `Borrow`. 46 | value_ptr: *mut T, 47 | 48 | /// Borrowed state 49 | handle: BorrowHandle, 50 | } 51 | 52 | /// Error produced by a failed `poll_borrow` call. 53 | #[derive(Debug)] 54 | pub struct BorrowError { 55 | _priv: (), 56 | } 57 | 58 | /// Error produced by a failed `try_borrow` call. 59 | #[derive(Debug)] 60 | pub struct TryBorrowError { 61 | is_poisoned: bool, 62 | } 63 | 64 | struct Inner { 65 | /// The value that can be valued 66 | value: UnsafeCell, 67 | 68 | /// Borrow state 69 | state: State, 70 | } 71 | 72 | struct BorrowHandle { 73 | /// The borrow state 74 | state_ptr: *const State, 75 | 76 | /// Holds a handle to the Arc, which prevents it from being dropped. 77 | _inner: Arc, 78 | } 79 | 80 | struct State { 81 | /// Tracks if the value is currently borrowed or poisoned. 82 | borrowed: AtomicUsize, 83 | 84 | /// The task to notify once the borrow is released 85 | task: AtomicTask, 86 | } 87 | 88 | const UNUSED: usize = 0; 89 | const BORROWED: usize = 1; 90 | const POISONED: usize = 2; 91 | 92 | // ===== impl Borrow ===== 93 | 94 | impl Borrow { 95 | /// Create a new `Borrow` containing `value`. 96 | pub fn new(value: T) -> Borrow { 97 | Borrow { 98 | inner: Arc::new(Inner { 99 | value: UnsafeCell::new(value), 100 | state: State { 101 | borrowed: AtomicUsize::new(UNUSED), 102 | task: AtomicTask::new(), 103 | }, 104 | }), 105 | } 106 | } 107 | 108 | /// Returns `true` if the value is not already borrowed. 109 | pub fn is_ready(&self) -> bool { 110 | match self.inner.state.borrowed.load(Acquire) { 111 | UNUSED => true, 112 | BORROWED => false, 113 | POISONED => true, 114 | _ => unreachable!(), 115 | } 116 | } 117 | 118 | /// Returns `Ready` when the value is not already borrowed. 119 | /// 120 | /// When `Ready` is returned, the next call to `poll_borrow` or `try_borrow` 121 | /// is guaranteed to succeed. When `NotReady` is returned, the current task 122 | /// will be notified once the outstanding borrow is released. 123 | pub fn poll_ready(&mut self) -> Poll<(), BorrowError> { 124 | self.inner.state.task.register(); 125 | 126 | match self.inner.state.borrowed.load(Acquire) { 127 | UNUSED => Ok(Async::Ready(())), 128 | BORROWED => Ok(Async::NotReady), 129 | POISONED => Err(BorrowError::new()), 130 | _ => unreachable!(), 131 | } 132 | } 133 | 134 | /// Attempt to borrow the value, returning `NotReady` if it cannot be 135 | /// borrowed. 136 | pub fn poll_borrow(&mut self) -> Poll, BorrowError> { 137 | self.inner.state.task.register(); 138 | 139 | match self.inner.state.borrowed.compare_and_swap(UNUSED, BORROWED, Acquire) { 140 | UNUSED => { 141 | // Lock acquired, fall through 142 | } 143 | BORROWED => return Ok(Async::NotReady), 144 | POISONED => return Err(BorrowError::new()), 145 | _ => unreachable!(), 146 | } 147 | 148 | let value_ptr = self.inner.value.get(); 149 | let handle = BorrowHandle { 150 | state_ptr: &self.inner.state as *const State, 151 | _inner: self.inner.clone() as Arc, 152 | }; 153 | 154 | Ok(Async::Ready(BorrowGuard { 155 | value_ptr, 156 | handle, 157 | })) 158 | } 159 | 160 | /// Attempt to borrow the value, returning `Err` if it cannot be borrowed. 161 | pub fn try_borrow(&self) -> Result, TryBorrowError> { 162 | match self.inner.state.borrowed.compare_and_swap(UNUSED, BORROWED, Acquire) { 163 | UNUSED => { 164 | // Lock acquired, fall through 165 | } 166 | BORROWED => return Err(TryBorrowError::new(false)), 167 | POISONED => return Err(TryBorrowError::new(true)), 168 | _ => unreachable!(), 169 | } 170 | 171 | let value_ptr = self.inner.value.get(); 172 | let handle = BorrowHandle { 173 | state_ptr: &self.inner.state as *const State, 174 | _inner: self.inner.clone() as Arc, 175 | }; 176 | 177 | Ok(BorrowGuard { 178 | value_ptr, 179 | handle, 180 | }) 181 | } 182 | 183 | /// Make a new `BorrowGuard` for a component of the borrowed data. 184 | /// 185 | /// The `BorrowGuard` is already mutably borrowed, so this cannot fail. 186 | pub fn map(mut r: BorrowGuard, f: F) -> BorrowGuard 187 | where F: FnOnce(&mut T) -> &mut U, 188 | { 189 | let u = f(&mut *r) as *mut U; 190 | 191 | BorrowGuard { 192 | value_ptr: u, 193 | handle: r.handle, 194 | } 195 | } 196 | 197 | /// Make a new `BorrowGuard` for a component of the borrowed data. 198 | /// 199 | /// The `BorrowGuard` is already mutably borrowed, so this cannot fail. 200 | pub fn try_map(mut r: BorrowGuard, f: F) 201 | -> Result, (BorrowGuard, E)> 202 | where F: FnOnce(&mut T) -> Result<&mut U, E> 203 | { 204 | 205 | let res = f(&mut *r) 206 | .map(|u| u as *mut U); 207 | 208 | match res { 209 | Ok(u) => { 210 | Ok(BorrowGuard { 211 | value_ptr: u, 212 | handle: r.handle, 213 | }) 214 | } 215 | Err(e) => { 216 | Err((r, e)) 217 | } 218 | } 219 | } 220 | } 221 | 222 | impl Default for Borrow { 223 | fn default() -> Borrow { 224 | Borrow::new(T::default()) 225 | } 226 | } 227 | 228 | impl fmt::Debug for Borrow { 229 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 230 | match self.try_borrow() { 231 | Ok(guard) => { 232 | fmt.debug_struct("Borrow") 233 | .field("data", &*guard) 234 | .finish() 235 | } 236 | Err(e) => { 237 | if e.is_poisoned() { 238 | fmt.debug_struct("Borrow") 239 | .field("data", &"Poisoned") 240 | .finish() 241 | } else { 242 | fmt.debug_struct("Borrow") 243 | .field("data", &"<>") 244 | .finish() 245 | } 246 | }, 247 | } 248 | } 249 | } 250 | 251 | unsafe impl Send for Borrow { } 252 | unsafe impl Sync for Borrow { } 253 | 254 | // ===== impl BorrowGuard ===== 255 | 256 | impl ops::Deref for BorrowGuard { 257 | type Target = T; 258 | 259 | fn deref(&self) -> &T { 260 | unsafe { &*self.value_ptr } 261 | } 262 | } 263 | 264 | impl ops::DerefMut for BorrowGuard { 265 | fn deref_mut(&mut self) -> &mut T { 266 | unsafe { &mut *self.value_ptr } 267 | } 268 | } 269 | 270 | impl fmt::Debug for BorrowGuard { 271 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 272 | fmt.debug_struct("BorrowGuard") 273 | .field("data", &**self) 274 | .finish() 275 | } 276 | } 277 | 278 | unsafe impl Send for BorrowGuard { } 279 | unsafe impl Sync for BorrowGuard { } 280 | 281 | // ===== impl BorrowHandle ===== 282 | 283 | impl Drop for BorrowHandle { 284 | fn drop(&mut self) { 285 | let state = unsafe { &*self.state_ptr }; 286 | 287 | if thread::panicking() { 288 | state.borrowed.store(POISONED, Release); 289 | } else { 290 | state.borrowed.store(UNUSED, Release); 291 | } 292 | 293 | state.task.notify(); 294 | } 295 | } 296 | 297 | // ===== impl BorrowError ===== 298 | 299 | impl BorrowError { 300 | fn new() -> BorrowError { 301 | BorrowError { 302 | _priv: (), 303 | } 304 | } 305 | } 306 | 307 | // ===== impl TryBorrowError ===== 308 | 309 | impl TryBorrowError { 310 | fn new(is_poisoned: bool) -> TryBorrowError { 311 | TryBorrowError { 312 | is_poisoned, 313 | } 314 | } 315 | 316 | pub fn is_poisoned(&self) -> bool { 317 | self.is_poisoned 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /futures-borrow/tests/borrow.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate futures_borrow; 3 | extern crate futures_test; 4 | 5 | use futures_borrow::*; 6 | use futures_test::Harness; 7 | 8 | #[test] 9 | fn test_basic_borrow() { 10 | let mut s = Borrow::new("hello".to_string()); 11 | 12 | { 13 | // Ready immediately 14 | let mut ready = Harness::poll_fn(|| s.poll_ready()); 15 | assert!(ready.poll().unwrap().is_ready()); 16 | } 17 | 18 | // borrow 19 | let mut b = s.try_borrow().unwrap(); 20 | 21 | { 22 | // Can't double borrow 23 | assert!(s.try_borrow().is_err()); 24 | 25 | let mut ready = Harness::poll_fn(|| s.poll_ready()); 26 | 27 | // Not ready 28 | assert!(!ready.poll().unwrap().is_ready()); 29 | 30 | b.push_str("-world"); 31 | 32 | drop(b); 33 | 34 | // Ready notified 35 | assert!(ready.is_notified()); 36 | } 37 | 38 | { 39 | // Now ready 40 | let mut ready = Harness::poll_fn(|| s.poll_ready()); 41 | assert!(ready.poll().unwrap().is_ready()); 42 | } 43 | 44 | // Borrow again 45 | let b = s.try_borrow().unwrap(); 46 | 47 | assert_eq!(*b, "hello-world"); 48 | } 49 | 50 | #[test] 51 | fn test_borrow_map() { 52 | let s = Borrow::new(vec!["hello".to_string()]); 53 | 54 | // Borrow 55 | let b = s.try_borrow().unwrap(); 56 | let mut b = Borrow::map(b, |vec| &mut vec[0]); 57 | 58 | // Can't double borrow 59 | assert!(s.try_borrow().is_err()); 60 | 61 | b.push_str("-world"); 62 | drop(b); 63 | 64 | let b = s.try_borrow().unwrap(); 65 | 66 | assert_eq!(b[0], "hello-world"); 67 | } 68 | -------------------------------------------------------------------------------- /futures-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "futures-test" 3 | version = "0.1.0" 4 | authors = ["Carl Lerche "] 5 | publish = false 6 | 7 | [dependencies] 8 | futures = "0.1" 9 | -------------------------------------------------------------------------------- /futures-test/src/harness.rs: -------------------------------------------------------------------------------- 1 | use futures::{Future, Stream, Poll, Async}; 2 | use futures::executor::{spawn, Spawn, Notify}; 3 | 4 | use std::time::{Duration, Instant}; 5 | use std::sync::{Arc, Mutex, Condvar}; 6 | use std::sync::atomic::{AtomicUsize, Ordering}; 7 | 8 | /// Wraps a future, providing an API to interact with it while off task. 9 | /// 10 | /// This wrapper is intended to use from the context of tests. The future is 11 | /// effectively wrapped by a task and this harness tracks received notifications 12 | /// as well as provides APIs to perform non-blocking polling as well blocking 13 | /// polling with or without timeout. 14 | #[derive(Debug)] 15 | pub struct Harness { 16 | spawn: Spawn, 17 | notify: Arc, 18 | } 19 | 20 | /// Error produced by `TestHarness` operations with timeout. 21 | #[derive(Debug)] 22 | pub struct TimeoutError { 23 | /// If `None`, represents a timeout error 24 | inner: Option, 25 | } 26 | 27 | #[derive(Debug)] 28 | struct ThreadNotify { 29 | state: AtomicUsize, 30 | mutex: Mutex<()>, 31 | condvar: Condvar, 32 | } 33 | 34 | const IDLE: usize = 0; 35 | const NOTIFY: usize = 1; 36 | const SLEEP: usize = 2; 37 | 38 | impl Harness { 39 | /// Wraps `obj` in a test harness, enabling interacting with the future 40 | /// while not on a `Task`. 41 | pub fn new(obj: T) -> Self { 42 | Harness { 43 | spawn: spawn(obj), 44 | notify: Arc::new(ThreadNotify::new()), 45 | } 46 | } 47 | 48 | pub fn with(&mut self, f: F) -> R 49 | where F: FnOnce(&mut Self) -> R, 50 | { 51 | f(self) 52 | } 53 | 54 | /// Returns `true` if the inner future has received a readiness notification 55 | /// since the last action has been performed. 56 | pub fn is_notified(&self) -> bool { 57 | self.notify.is_notified() 58 | } 59 | 60 | /// Returns a reference to the inner future. 61 | pub fn get_ref(&self) -> &T { 62 | self.spawn.get_ref() 63 | } 64 | 65 | /// Returns a mutable reference to the inner future. 66 | pub fn get_mut(&mut self) -> &mut T { 67 | self.spawn.get_mut() 68 | } 69 | 70 | /// Consumes `self`, returning the inner future. 71 | pub fn into_inner(self) -> T { 72 | self.spawn.into_inner() 73 | } 74 | } 75 | 76 | impl Harness<::futures::future::PollFn> 77 | where F: FnMut() -> Poll, 78 | { 79 | /// Wraps the `poll_fn` in a harness. 80 | pub fn poll_fn(f: F) -> Self { 81 | Harness::new(::futures::future::poll_fn(f)) 82 | } 83 | } 84 | 85 | impl Harness { 86 | /// Polls the inner future. 87 | /// 88 | /// This function returns immediately. If the inner future is not currently 89 | /// ready, `NotReady` is returned. Readiness notifications are tracked and 90 | /// can be queried using `is_notified`. 91 | pub fn poll(&mut self) -> Poll { 92 | self.spawn.poll_future_notify(&self.notify, 0) 93 | } 94 | 95 | /// Waits for the internal future to complete, blocking this thread's 96 | /// execution until it does. 97 | pub fn wait(&mut self) -> Result { 98 | self.notify.clear(); 99 | 100 | loop { 101 | match self.spawn.poll_future_notify(&self.notify, 0)? { 102 | Async::NotReady => self.notify.park(), 103 | Async::Ready(e) => return Ok(e), 104 | } 105 | } 106 | } 107 | 108 | /// Waits for the internal future to complete, blocking this thread's 109 | /// execution for at most `dur`. 110 | pub fn wait_timeout(&mut self, dur: Duration) 111 | -> Result> 112 | { 113 | let until = Instant::now() + dur; 114 | 115 | self.notify.clear(); 116 | 117 | loop { 118 | let res = self.spawn.poll_future_notify(&self.notify, 0) 119 | .map_err(TimeoutError::new); 120 | 121 | match res? { 122 | Async::NotReady => { 123 | let now = Instant::now(); 124 | 125 | if now >= until { 126 | return Err(TimeoutError::timeout()); 127 | } 128 | 129 | self.notify.park_timeout(Some(until - now)); 130 | } 131 | Async::Ready(e) => return Ok(e), 132 | } 133 | } 134 | } 135 | } 136 | 137 | impl Harness { 138 | /// Polls the inner future. 139 | /// 140 | /// This function returns immediately. If the inner future is not currently 141 | /// ready, `NotReady` is returned. Readiness notifications are tracked and 142 | /// can be queried using `is_notified`. 143 | pub fn poll_next(&mut self) -> Poll, T::Error> { 144 | self.spawn.poll_stream_notify(&self.notify, 0) 145 | } 146 | } 147 | 148 | impl TimeoutError { 149 | fn new(inner: T) -> Self { 150 | TimeoutError { inner: Some(inner) } 151 | } 152 | 153 | fn timeout() -> Self { 154 | TimeoutError { inner: None } 155 | } 156 | 157 | pub fn is_timeout(&self) -> bool { 158 | self.inner.is_none() 159 | } 160 | 161 | /// Consumes `self`, returning the inner error. Returns `None` if `self` 162 | /// represents a timeout. 163 | pub fn into_inner(self) -> Option { 164 | self.inner 165 | } 166 | } 167 | 168 | impl ThreadNotify { 169 | fn new() -> Self { 170 | ThreadNotify { 171 | state: AtomicUsize::new(IDLE), 172 | mutex: Mutex::new(()), 173 | condvar: Condvar::new(), 174 | } 175 | } 176 | 177 | /// Clears any previously received notify, avoiding potential spurrious 178 | /// notifications. This should only be called immediately before running the 179 | /// task. 180 | fn clear(&self) { 181 | self.state.store(IDLE, Ordering::SeqCst); 182 | } 183 | 184 | fn is_notified(&self) -> bool { 185 | match self.state.load(Ordering::SeqCst) { 186 | IDLE => false, 187 | NOTIFY => true, 188 | _ => unreachable!(), 189 | } 190 | } 191 | 192 | fn park(&self) { 193 | self.park_timeout(None); 194 | } 195 | 196 | fn park_timeout(&self, dur: Option) { 197 | // If currently notified, then we skip sleeping. This is checked outside 198 | // of the lock to avoid acquiring a mutex if not necessary. 199 | match self.state.compare_and_swap(NOTIFY, IDLE, Ordering::SeqCst) { 200 | NOTIFY => return, 201 | IDLE => {}, 202 | _ => unreachable!(), 203 | } 204 | 205 | // The state is currently idle, so obtain the lock and then try to 206 | // transition to a sleeping state. 207 | let mut m = self.mutex.lock().unwrap(); 208 | 209 | // Transition to sleeping 210 | match self.state.compare_and_swap(IDLE, SLEEP, Ordering::SeqCst) { 211 | NOTIFY => { 212 | // Notified before we could sleep, consume the notification and 213 | // exit 214 | self.state.store(IDLE, Ordering::SeqCst); 215 | return; 216 | } 217 | IDLE => {}, 218 | _ => unreachable!(), 219 | } 220 | 221 | // Track (until, remaining) 222 | let mut time = dur.map(|dur| (Instant::now() + dur, dur)); 223 | 224 | loop { 225 | m = match time { 226 | Some((until, rem)) => { 227 | let (guard, _) = self.condvar.wait_timeout(m, rem).unwrap(); 228 | let now = Instant::now(); 229 | 230 | if now >= until { 231 | // Timed out... exit sleep state 232 | self.state.store(IDLE, Ordering::SeqCst); 233 | return; 234 | } 235 | 236 | time = Some((until, until - now)); 237 | guard 238 | } 239 | None => self.condvar.wait(m).unwrap(), 240 | }; 241 | 242 | // Transition back to idle, loop otherwise 243 | if NOTIFY == self.state.compare_and_swap(NOTIFY, IDLE, Ordering::SeqCst) { 244 | return; 245 | } 246 | } 247 | } 248 | } 249 | 250 | impl Notify for ThreadNotify { 251 | fn notify(&self, _unpark_id: usize) { 252 | // First, try transitioning from IDLE -> NOTIFY, this does not require a 253 | // lock. 254 | match self.state.compare_and_swap(IDLE, NOTIFY, Ordering::SeqCst) { 255 | IDLE | NOTIFY => return, 256 | SLEEP => {} 257 | _ => unreachable!(), 258 | } 259 | 260 | // The other half is sleeping, this requires a lock 261 | let _m = self.mutex.lock().unwrap(); 262 | 263 | // Transition from SLEEP -> NOTIFY 264 | match self.state.compare_and_swap(SLEEP, NOTIFY, Ordering::SeqCst) { 265 | SLEEP => {} 266 | _ => return, 267 | } 268 | 269 | // Wakeup the sleeper 270 | self.condvar.notify_one(); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /futures-test/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | 3 | pub mod harness; 4 | 5 | pub use harness::Harness; 6 | -------------------------------------------------------------------------------- /futures-watch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "futures-watch" 3 | version = "0.1.0" 4 | authors = ["Carl Lerche "] 5 | publish = false 6 | 7 | [dependencies] 8 | futures = "0.1" 9 | fnv = "1.0.5" 10 | 11 | [dev-dependencies] 12 | futures-test = { path = "../futures-test" } 13 | -------------------------------------------------------------------------------- /futures-watch/README.md: -------------------------------------------------------------------------------- 1 | # Watch 2 | 3 | A multi consumer cell that receives notifications when the inner value is 4 | changed. 5 | -------------------------------------------------------------------------------- /futures-watch/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A multi-consumer, single producer cell that receives notifications when the inner value is 2 | //! changed. 3 | //! 4 | //! # Usage 5 | //! 6 | //! [`Watch::new`] returns a [`Watch`] / [`Store`] pair. These are the consumer and 7 | //! producer halves of the cell. The watch cell is created with an initial 8 | //! value. Calls to [`Watch::borrow`] will always yield the latest value. 9 | //! 10 | //! ``` 11 | //! # use futures_watch::*; 12 | //! let (watch, store) = Watch::new("hello"); 13 | //! assert_eq!(*watch.borrow(), "hello"); 14 | //! # drop(store); 15 | //! ``` 16 | //! 17 | //! Using the [`Store`] handle, the cell value can be updated. 18 | //! 19 | //! ``` 20 | //! # use futures_watch::*; 21 | //! let (watch, mut store) = Watch::new("hello"); 22 | //! store.store("goodbye"); 23 | //! assert_eq!(*watch.borrow(), "goodbye"); 24 | //! ``` 25 | //! 26 | //! [`Watch`] handles are future-aware and will receive notifications whenever 27 | //! the inner value is changed. 28 | //! 29 | //! ``` 30 | //! # extern crate futures; 31 | //! # extern crate futures_watch; 32 | //! # pub fn main() { 33 | //! # use futures::*; 34 | //! # use futures_watch::*; 35 | //! # use std::thread; 36 | //! # use std::time::Duration; 37 | //! let (watch, mut store) = Watch::new("hello"); 38 | //! 39 | //! thread::spawn(move || { 40 | //! thread::sleep(Duration::from_millis(100)); 41 | //! store.store("goodbye"); 42 | //! }); 43 | //! 44 | //! watch.into_future() 45 | //! .and_then(|(_, watch)| { 46 | //! assert_eq!(*watch.borrow(), "goodbye"); 47 | //! Ok(()) 48 | //! }) 49 | //! .wait().unwrap(); 50 | //! # } 51 | //! ``` 52 | //! 53 | //! [`Watch::borrow`] will yield the most recently stored value. All 54 | //! intermediate values are dropped. 55 | //! 56 | //! ``` 57 | //! # use futures_watch::*; 58 | //! let (watch, mut store) = Watch::new("hello"); 59 | //! 60 | //! store.store("two"); 61 | //! store.store("three"); 62 | //! 63 | //! assert_eq!(*watch.borrow(), "three"); 64 | //! ``` 65 | //! 66 | //! # Cancellation 67 | //! 68 | //! [`Store::poll_cancel`] allows the producer to detect when all [`Watch`] 69 | //! handles have been dropped. This indicates that there is no further interest 70 | //! in the values being produced and work can be stopped. 71 | //! 72 | //! When the [`Store`] is dropped, the watch handles will be notified and 73 | //! [`Watch::is_final`] will return true. 74 | //! 75 | //! # Thread safety 76 | //! 77 | //! Both [`Watch`] and [`Store`] are thread safe. They can be moved to other 78 | //! threads and can be used in a concurrent environment. Clones of [`Watch`] 79 | //! handles may be moved to separate threads and also used concurrently. 80 | //! 81 | //! [`Watch`]: struct.Watch.html 82 | //! [`Store`]: struct.Store.html 83 | //! [`Watch::new`]: struct.Watch.html#method.new 84 | //! [`Watch::borrow`]: struct.Watch.html#method.borrow 85 | //! [`Watch::is_final`]: struct.Watch.html#method.is_final 86 | //! [`Store::poll_cancel`]: struct.Store.html#method.poll_cancel 87 | 88 | #![deny(warnings, missing_docs, missing_debug_implementations)] 89 | 90 | extern crate fnv; 91 | extern crate futures; 92 | 93 | use fnv::FnvHashMap; 94 | use futures::{Stream, Sink, Poll, Async, AsyncSink, StartSend}; 95 | use futures::task::AtomicTask; 96 | 97 | use std::{mem, ops}; 98 | use std::sync::{Arc, Weak, Mutex, RwLock, RwLockReadGuard}; 99 | use std::sync::atomic::AtomicUsize; 100 | use std::sync::atomic::Ordering::SeqCst; 101 | 102 | /// Uses a `Watch` to produce a `Stream` of mapped values. 103 | pub mod then_stream; 104 | 105 | pub use then_stream::Then; 106 | 107 | /// A future-aware cell that receives notifications when the inner value is 108 | /// changed. 109 | /// 110 | /// `Watch` implements `Stream`, yielding `()` whenever the inner value is 111 | /// changed. This allows a user to monitor this stream to get notified of change 112 | /// events. 113 | /// 114 | /// `Watch` handles may be cloned in order to create additional watchers. Each 115 | /// watcher operates independently and can be used to notify separate tasks. 116 | /// Each watcher handle must be used from only a single task. 117 | /// 118 | /// See crate level documentation for more details. 119 | #[derive(Debug)] 120 | pub struct Watch { 121 | /// Pointer to the shared state 122 | shared: Arc>, 123 | 124 | /// Pointer to the watcher's internal state 125 | inner: Arc, 126 | 127 | /// Watcher ID. 128 | id: u64, 129 | 130 | /// Last observed version 131 | ver: usize, 132 | } 133 | 134 | /// Update the inner value of a `Watch` cell. 135 | /// 136 | /// The [`store`] function sets the inner value of the cell, returning the 137 | /// previous value. Alternatively, `Store` implements `Sink` such that values 138 | /// pushed into the `Sink` are stored in the cell. 139 | /// 140 | /// See crate level documentation for more details. 141 | /// 142 | /// [`store`]: #method.store 143 | #[derive(Debug)] 144 | pub struct Store { 145 | shared: Weak>, 146 | } 147 | 148 | /// Borrowed reference 149 | /// 150 | /// See [`Watch::borrow`] for more details. 151 | /// 152 | /// [`Watch::borrow`]: struct.Watch.html#method.borrow 153 | #[derive(Debug)] 154 | pub struct Ref<'a, T: 'a> { 155 | inner: RwLockReadGuard<'a, T>, 156 | } 157 | 158 | /// Errors produced by `Watch`. 159 | #[derive(Debug)] 160 | pub struct WatchError { 161 | _p: (), 162 | } 163 | 164 | /// Errors produced by `Store`. 165 | #[derive(Debug)] 166 | pub struct StoreError { 167 | inner: T, 168 | } 169 | 170 | #[derive(Debug)] 171 | struct Shared { 172 | /// The most recent value 173 | value: RwLock, 174 | 175 | /// The current version 176 | /// 177 | /// The lowest bit represents a "closed" state. The rest of the bits 178 | /// represent the current version. 179 | version: AtomicUsize, 180 | 181 | /// All watchers 182 | watchers: Mutex, 183 | 184 | /// Task to notify when all watchers drop 185 | cancel: AtomicTask, 186 | } 187 | 188 | #[derive(Debug)] 189 | struct Watchers { 190 | next_id: u64, 191 | watchers: FnvHashMap>, 192 | } 193 | 194 | #[derive(Debug)] 195 | struct WatchInner { 196 | task: AtomicTask, 197 | } 198 | 199 | const CLOSED: usize = 1; 200 | 201 | // ===== impl Watch ===== 202 | 203 | impl Watch { 204 | /// Create a new watch cell, returning the consumer / producer halves. 205 | /// 206 | /// All values stored by the `Store` will become visible to the `Watch` 207 | /// handles. Only the last value stored is made available to the `Watch` 208 | /// half. All intermediate values are dropped. 209 | /// 210 | /// # Examples 211 | /// 212 | /// ``` 213 | /// # use futures_watch::*; 214 | /// let (watch, mut store) = Watch::new("hello"); 215 | /// store.store("goodbye"); 216 | /// assert_eq!(*watch.borrow(), "goodbye"); 217 | /// ``` 218 | pub fn new(init: T) -> (Watch, Store) { 219 | let inner = Arc::new(WatchInner::new()); 220 | 221 | // Insert the watcher 222 | let mut watchers = FnvHashMap::with_capacity_and_hasher(0, Default::default()); 223 | watchers.insert(0, inner.clone()); 224 | 225 | let shared = Arc::new(Shared { 226 | value: RwLock::new(init), 227 | version: AtomicUsize::new(0), 228 | watchers: Mutex::new(Watchers { 229 | next_id: 1, 230 | watchers, 231 | }), 232 | cancel: AtomicTask::new(), 233 | }); 234 | 235 | let store = Store { 236 | shared: Arc::downgrade(&shared), 237 | }; 238 | 239 | let watch = Watch { 240 | shared, 241 | inner, 242 | id: 0, 243 | ver: 0, 244 | }; 245 | 246 | (watch, store) 247 | } 248 | 249 | /// Returns true if the current value represents the final value 250 | /// 251 | /// A value becomes final once the `Store` handle is dropped. This indicates 252 | /// that there can no longer me any values stored in the cell. 253 | /// 254 | /// # Examples 255 | /// 256 | /// ``` 257 | /// # use futures_watch::*; 258 | /// let (watch, store) = Watch::new("hello"); 259 | /// 260 | /// assert!(!watch.is_final()); 261 | /// drop(store); 262 | /// assert!(watch.is_final()); 263 | /// ``` 264 | pub fn is_final(&self) -> bool { 265 | CLOSED == self.shared.version.load(SeqCst) & CLOSED 266 | } 267 | 268 | /// Returns a reference to the inner value 269 | /// 270 | /// Outstanding borrows hold a read lock on the inner value. This means that 271 | /// long lived borrows could cause the produce half to block. It is 272 | /// recommended to keep the borrow as short lived as possible. 273 | /// 274 | /// # Examples 275 | /// 276 | /// ``` 277 | /// # use futures_watch::*; 278 | /// let (watch, _) = Watch::new("hello"); 279 | /// assert_eq!(*watch.borrow(), "hello"); 280 | /// ``` 281 | pub fn borrow(&self) -> Ref { 282 | let inner = self.shared.value.read().unwrap(); 283 | Ref { inner } 284 | } 285 | 286 | /// Convert this watch into a stream of values produced by an `M`-typed map function. 287 | pub fn then_stream>(self, then: M) -> then_stream::ThenStream { 288 | then_stream::ThenStream::new(self, then) 289 | } 290 | } 291 | 292 | /// A stream of inner value change events. 293 | /// 294 | /// Whenever the inner value of the cell is updated by the `Store` handle, `()` 295 | /// is yielded by this stream. 296 | impl Stream for Watch { 297 | type Item = (); 298 | type Error = WatchError; 299 | 300 | fn poll(&mut self) -> Poll, Self::Error> { 301 | // Make sure the task is up to date 302 | self.inner.task.register(); 303 | 304 | let version = self.shared.version.load(SeqCst); 305 | 306 | if CLOSED == version & CLOSED { 307 | // The `Store` handle has been dropped. 308 | return Ok(None.into()); 309 | } 310 | 311 | if self.ver == version { 312 | return Ok(Async::NotReady); 313 | } 314 | 315 | // Track the latest version 316 | self.ver = version; 317 | 318 | Ok(Some(()).into()) 319 | } 320 | } 321 | 322 | impl Clone for Watch { 323 | fn clone(&self) -> Self { 324 | let inner = Arc::new(WatchInner::new()); 325 | let shared = self.shared.clone(); 326 | 327 | let id = { 328 | let mut watchers = shared.watchers.lock().unwrap(); 329 | let id = watchers.next_id; 330 | 331 | watchers.next_id += 1; 332 | watchers.watchers.insert(id, inner.clone()); 333 | 334 | id 335 | }; 336 | 337 | let ver = self.ver; 338 | 339 | Watch { 340 | shared: shared, 341 | inner, 342 | id, 343 | ver, 344 | } 345 | } 346 | } 347 | 348 | impl Drop for Watch { 349 | fn drop(&mut self) { 350 | let mut watchers = self.shared.watchers.lock().unwrap(); 351 | watchers.watchers.remove(&self.id); 352 | } 353 | } 354 | 355 | impl WatchInner { 356 | fn new() -> Self { 357 | WatchInner { task: AtomicTask::new() } 358 | } 359 | } 360 | 361 | // ===== impl Store ===== 362 | 363 | impl Store { 364 | /// Store a new value in the cell, notifying all watchers. The previous 365 | /// value is returned. 366 | /// 367 | /// # Examples 368 | /// 369 | /// ``` 370 | /// # use futures_watch::*; 371 | /// let (watch, mut borrow) = Watch::new("hello"); 372 | /// assert_eq!(borrow.store("goodbye").unwrap(), "hello"); 373 | /// assert_eq!(*watch.borrow(), "goodbye"); 374 | /// ``` 375 | pub fn store(&mut self, value: T) -> Result> { 376 | let shared = match self.shared.upgrade() { 377 | Some(shared) => shared, 378 | // All `Watch` handles have been canceled 379 | None => return Err(StoreError::new(value)), 380 | }; 381 | 382 | // Replace the value 383 | let value = { 384 | let mut lock = shared.value.write().unwrap(); 385 | mem::replace(&mut *lock, value) 386 | }; 387 | 388 | // Update the version. 2 is used so that the CLOSED bit is not set. 389 | shared.version.fetch_add(2, SeqCst); 390 | 391 | // Notify all watchers 392 | notify_all(&*shared); 393 | 394 | // Return the old value 395 | Ok(value) 396 | } 397 | 398 | /// Returns `Ready` when all watchers have dropped. 399 | /// 400 | /// This allows the producer to get notified when interest in the produced 401 | /// values is canceled and immediately stop doing work. 402 | pub fn poll_cancel(&mut self) -> Poll<(), ()> { 403 | match self.shared.upgrade() { 404 | Some(shared) => { 405 | shared.cancel.register(); 406 | Ok(Async::NotReady) 407 | } 408 | None => { 409 | Ok(Async::Ready(())) 410 | } 411 | } 412 | } 413 | } 414 | 415 | impl Sink for Store { 416 | type SinkItem = T; 417 | type SinkError = StoreError; 418 | 419 | fn start_send(&mut self, item: T) -> StartSend> { 420 | let _ = self.store(item)?; 421 | Ok(AsyncSink::Ready) 422 | } 423 | 424 | fn poll_complete(&mut self) -> Poll<(), StoreError> { 425 | Ok(().into()) 426 | } 427 | } 428 | 429 | /// Notify all watchers of a change 430 | fn notify_all(shared: &Shared) { 431 | let watchers = shared.watchers.lock().unwrap(); 432 | 433 | for watcher in watchers.watchers.values() { 434 | // Notify the task 435 | watcher.task.notify(); 436 | } 437 | } 438 | 439 | impl Drop for Store { 440 | fn drop(&mut self) { 441 | if let Some(shared) = self.shared.upgrade() { 442 | shared.version.fetch_or(CLOSED, SeqCst); 443 | notify_all(&*shared); 444 | } 445 | } 446 | } 447 | 448 | // ===== impl Ref ===== 449 | 450 | impl<'a, T: 'a> ops::Deref for Ref<'a, T> { 451 | type Target = T; 452 | 453 | fn deref(&self) -> &T { 454 | self.inner.deref() 455 | } 456 | } 457 | 458 | // ===== impl Shared ===== 459 | 460 | impl Drop for Shared { 461 | fn drop(&mut self) { 462 | self.cancel.notify(); 463 | } 464 | } 465 | 466 | // ===== impl StoreError ===== 467 | 468 | impl StoreError { 469 | fn new(inner: T) -> Self { 470 | StoreError { inner } 471 | } 472 | } 473 | -------------------------------------------------------------------------------- /futures-watch/src/then_stream.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll, Stream}; 2 | 3 | use {Watch, WatchError}; 4 | 5 | /// Maps borrowed references to `T` into an `Item`. 6 | pub trait Then { 7 | /// The output type. 8 | type Output; 9 | 10 | /// What you get when Map fails. 11 | type Error; 12 | 13 | /// Produces a new Output value. 14 | fn then(&mut self, t: Result<&T, WatchError>) -> Result; 15 | } 16 | 17 | /// Each time the underlying `Watch` is updated, the stream maps over the most-recent 18 | /// value. 19 | #[derive(Debug)] 20 | pub struct ThenStream> { 21 | watch: Watch, 22 | then: M, 23 | } 24 | 25 | // ==== impl ThenStream ==== 26 | 27 | impl> ThenStream { 28 | pub(crate) fn new(watch: Watch, then: M) -> Self { 29 | Self { watch, then } 30 | } 31 | } 32 | 33 | impl> Stream for ThenStream { 34 | type Item = >::Output; 35 | type Error = >::Error; 36 | 37 | fn poll(&mut self) -> Poll, Self::Error> { 38 | let result = match self.watch.poll() { 39 | Ok(Async::Ready(Some(()))) => { 40 | self.then.then(Ok(&*self.watch.borrow())) 41 | } 42 | Err(e) => { 43 | self.then.then(Err(e)) 44 | } 45 | Ok(Async::NotReady) => { 46 | return Ok(Async::NotReady); 47 | } 48 | Ok(Async::Ready(None)) => { 49 | return Ok(Async::Ready(None)); 50 | } 51 | }; 52 | 53 | result.map(Some).map(Async::Ready) 54 | } 55 | } 56 | 57 | impl> Clone for ThenStream { 58 | fn clone(&self) -> Self { 59 | Self::new(self.watch.clone(), self.then.clone()) 60 | } 61 | } 62 | 63 | // ==== impl Then ==== 64 | 65 | impl Then for F 66 | where 67 | for<'t> F: FnMut(Result<&T, WatchError>) -> Result, 68 | { 69 | type Output = O; 70 | type Error = E; 71 | 72 | fn then(&mut self, t: Result<&T, WatchError>) -> Result { 73 | (self)(t) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /futures-watch/tests/watch.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate futures_test; 3 | extern crate futures_watch; 4 | 5 | use futures::{Stream}; 6 | use futures_test::Harness; 7 | use futures_watch::*; 8 | 9 | #[test] 10 | fn smoke() { 11 | let (mut watch, mut store) = Watch::new("one"); 12 | 13 | // Check the value 14 | assert_eq!(*watch.borrow(), "one"); 15 | assert!(!watch.is_final()); 16 | 17 | Harness::poll_fn(|| watch.poll()).with(|harness| { 18 | assert!(!harness.poll().unwrap().is_ready()); 19 | 20 | // Change the value. 21 | assert_eq!(store.store("two").unwrap(), "one"); 22 | 23 | // The watch was notified 24 | assert!(harness.poll().unwrap().is_ready()); 25 | }); 26 | 27 | assert!(!watch.is_final()); 28 | assert_eq!(*watch.borrow(), "two"); 29 | 30 | Harness::poll_fn(|| watch.poll()).with(|harness| { 31 | assert!(!harness.poll().unwrap().is_ready()); 32 | 33 | // Dropping `store` notifies watches 34 | drop(store); 35 | 36 | // The watch was notified 37 | assert!(harness.poll().unwrap().is_ready()); 38 | }); 39 | 40 | assert!(watch.is_final()); 41 | assert_eq!(*watch.borrow(), "two"); 42 | } 43 | 44 | #[test] 45 | fn multiple_watches() { 46 | let (mut watch1, mut store) = Watch::new("one"); 47 | let mut watch2 = watch1.clone(); 48 | 49 | { 50 | let mut h1 = Harness::poll_fn(|| watch1.poll()); 51 | let mut h2 = Harness::poll_fn(|| watch2.poll()); 52 | 53 | assert!(!h1.poll().unwrap().is_ready()); 54 | 55 | // Change the value. 56 | assert_eq!(store.store("two").unwrap(), "one"); 57 | 58 | // The watch was notified 59 | assert!(h1.poll().unwrap().is_ready()); 60 | assert!(h2.poll().unwrap().is_ready()); 61 | } 62 | 63 | assert_eq!(*watch1.borrow(), "two"); 64 | assert_eq!(*watch2.borrow(), "two"); 65 | } 66 | --------------------------------------------------------------------------------