tk_easyloop/lib.rs
1//! A main loop wrapper around tokio to provide thread-local
2//! loop which:
3//!
4//! * Avoids padding a `Handle` in to every function
5//!
6//! * Mostly avoids common error: `thread 'foo' panicked at 'no Task is
7//! currently running'`, by providing convenient `run` function for all your
8//! code involving futures
9//!
10//!
11//! # Example
12//!
13//! ```no_run
14//! extern crate futures;
15//! extern crate tk_easyloop;
16//!
17//! use std::time::Duration;
18//! use tk_easyloop::{run, timeout};
19//!
20//! fn main() {
21//! run(|| {
22//! // should return some future, let's use a timeout
23//! timeout(Duration::new(1, 0))
24//! }).unwrap();
25//! }
26//! ```
27//!
28//! # Multi-threaded Example
29//!
30//! This crate uses thread-local storage for storing loop, but it doesn't
31//! mean multi-treading doesn't work. Multiple threads can be used too.
32//!
33//! ```
34//! extern crate tk_easyloop;
35//! use std::thread;
36//! use std::time::Duration;
37//! use tk_easyloop::{run, timeout};
38//!
39//! fn main() {
40//! let mut threads = Vec::new();
41//! for thread_no in 0..10 {
42//! threads.push(thread::spawn(move || {
43//! run(|| {
44//! timeout(Duration::new(1, 0))
45//! })
46//! }))
47//! }
48//! for t in threads {
49//! t.join().unwrap().unwrap();
50//! }
51//! }
52//! ```
53//!
54//! See ``examples/multi-threaded.rs`` for more comprehensive example.
55#![warn(missing_docs)]
56
57extern crate futures;
58extern crate tokio_core;
59#[macro_use] extern crate scoped_tls;
60
61use std::time::{Duration, Instant};
62
63use futures::{IntoFuture, Future, empty};
64use tokio_core::reactor::{Core, Handle, Timeout, Interval};
65
66
67scoped_thread_local! {
68 static HANDLE: Handle
69}
70
71/// Returns current loop handle
72///
73/// This only works if running inside the `run()` function of the main loop
74///
75/// # Panics
76///
77/// This function panics if there is no currently running loop (i.e. this
78/// function is not running from the inside of `run()`.
79pub fn handle() -> Handle {
80 HANDLE.with(|handle| handle.clone())
81}
82
83/// Returns `true` if there is an event loop currently running
84///
85/// This basically returns `false` if and only if `handle()` would panic
86pub fn is_running() -> bool {
87 HANDLE.is_set()
88}
89
90/// Run the main loop and initialize it by running a function
91///
92/// This is basically a shortcut for:
93///
94/// ```ignore
95/// let mut lp = Core::new().expect("create loop");
96/// lp.run(futures::lazy(f))
97/// ```
98///
99/// The difference between `run()` and `run_forever()` is merely a convenience.
100/// Basically, you want to use `run()` in client applications when you have
101/// a future that should complete to proceed. And `run_forever()` in server
102/// applications which spawns some listeners and never exits.
103///
104/// But also initializes thread-local loop handle for the time of loop run
105pub fn run<F: FnOnce() -> R, R: IntoFuture>(f: F)
106 -> Result<R::Item, R::Error>
107{
108 let mut lp = Core::new().expect("create loop");
109 HANDLE.set(&lp.handle(), || {
110 lp.run(futures::lazy(f))
111 })
112}
113
114/// Run the main loop and initialize it by running a function, which spawns
115/// more futures in the main loop. Then run loop indefinitely.
116///
117/// This is basically a shortcut for:
118///
119/// ```ignore
120/// let mut lp = Core::new().expect("create loop");
121/// lp.run(futures::lazy(f)
122/// .and_then(|_| empty()))
123/// ```
124///
125/// The difference between `run()` and `run_forever()` is merely a convenience.
126/// Basically, you want to use `run()` in client applications when you have
127/// a future that should complete to proceed. And `run_forever()` in server
128/// applications which spawns some listeners and never exits.
129///
130/// But also initializes thread-local loop handle for the time of loop run
131pub fn run_forever<F: FnOnce() -> Result<(), E>, E>(f: F) -> Result<(), E> {
132 let mut lp = Core::new().expect("create loop");
133 HANDLE.set(&lp.handle(), || {
134 lp.run(futures::lazy(f).and_then(|_| empty()))
135 })
136}
137
138/// Create a timeout tied to the current loop
139///
140/// This is a shortcut for:
141///
142/// ```ignore
143/// Timeout::new(dur, &handle()).unwrap()
144/// ```
145///
146/// # Panics
147///
148/// When no loop is running (`handle()` panics)
149///
150/// (Note: while we technically `unwrap()` constructor it never fails in
151/// current tokio)
152pub fn timeout(dur: Duration) -> Timeout {
153 HANDLE.with(|handle| {
154 Timeout::new(dur, handle).unwrap()
155 })
156}
157
158/// Create a timeout tied to the current loop
159///
160/// This is a shortcut for:
161///
162/// ```ignore
163/// Timeout::new_at(instant, &handle()).unwrap()
164/// ```
165///
166/// # Panics
167///
168/// When no loop is running (`handle()` panics)
169///
170/// (Note: while we technically `unwrap()` constructor it never fails in
171/// current tokio)
172pub fn timeout_at(instant: Instant) -> Timeout {
173 HANDLE.with(|handle| {
174 Timeout::new_at(instant, handle).unwrap()
175 })
176}
177
178/// Create an interval tied to the current loop
179///
180/// This is a shortcut for:
181///
182/// ```ignore,
183/// Interval::new(instant, &handle()).unwrap()
184/// ```
185///
186/// # Panics
187///
188/// When no loop is running (`handle()` panics)
189///
190/// (Note: while we technically `unwrap()` constructor it never fails in
191/// current tokio)
192pub fn interval(dur: Duration) -> Interval {
193 HANDLE.with(|handle| {
194 Interval::new(dur, handle).unwrap()
195 })
196}
197
198/// Spawn a future to the current main loop
199///
200/// This only works if running inside the `run()` function of the main loop
201///
202/// This is an equivalent of:
203///
204/// ```ignore
205/// handle().spawn(f)
206/// ```
207///
208/// # Panics
209///
210/// This function panics if there is no currently running loop (i.e. this
211/// function is not running from the inside of `run()`.
212pub fn spawn<F>(f: F)
213 where F: Future<Item=(), Error=()> + 'static
214{
215 HANDLE.with(|handle| handle.spawn(f))
216}
217
218/// Spawn a closure to the current main loop
219///
220/// This only works if running inside the `run()` function of the main loop
221///
222/// This is an equivalent of:
223///
224/// ```ignore
225/// handle().spawn_fn(f)
226/// ```
227///
228/// # Panics
229///
230/// This function panics if there is no currently running loop (i.e. this
231/// function is not running from the inside of `run()`.
232pub fn spawn_fn<F, R>(f: F)
233 where F: FnOnce() -> R + 'static,
234 R: IntoFuture<Item=(), Error=()> + 'static
235{
236 HANDLE.with(|handle| handle.spawn_fn(f))
237}