Skip to main content

async_compat/
lib.rs

1//! Compatibility adapter between tokio and futures.
2//!
3//! There are two kinds of compatibility issues between [tokio] and [futures]:
4//!
5//! 1. Tokio's types cannot be used outside tokio context, so any attempt to use them will panic.
6//!     - Solution: If you apply the [`Compat`] adapter to a future, the future will manually
7//!       enter the context of a global tokio runtime. If a runtime is already available via tokio
8//!       thread-locals, then it will be used. Otherwise, a new single-threaded runtime will be
9//!       created on demand. That does *not* mean the future is polled by the tokio runtime - it
10//!       only means the future sets a thread-local variable pointing to the global tokio runtime so
11//!       that tokio's types can be used inside it.
12//! 2. Tokio and futures have similar but different I/O traits `AsyncRead`, `AsyncWrite`,
13//!    `AsyncBufRead`, and `AsyncSeek`.
14//!     - Solution: When the [`Compat`] adapter is applied to an I/O type, it will implement traits
15//!       of the opposite kind. That's how you can use tokio-based types wherever futures-based
16//!       types are expected, and the other way around.
17//!
18//! You can apply the [`Compat`] adapter using the [`Compat::new()`] constructor or using any
19//! method from the [`CompatExt`] trait.
20//!
21//! # The `multi-thread` feature
22//!
23//! Because the fallback runtime is single-threaded, a task *spawned* from inside a [`Compat`]
24//! future runs on a current-thread scheduler, where `tokio::task::block_in_place` panics with
25//! `can call blocking only when running on the multi-threaded runtime`. That call is how an
26//! async wrapper around a synchronous library runs a blocking call without stalling the tasks
27//! queued behind it, so a dependency written that way cannot be driven through [`Compat`]
28//! unless a multi-threaded runtime is already ambient.
29//!
30//! Enabling the `multi-thread` feature backs the fallback runtime with a multi-threaded scheduler
31//! instead, so spawned tasks run on real worker threads and `block_in_place` behaves as it would
32//! in any other tokio program. The trade-off is that the runtime starts a worker pool (sized to
33//! the available parallelism) rather than a single thread, so it is off by default.
34//!
35//! The feature affects the process-wide fallback runtime, and cargo features are additive, so
36//! enabling it anywhere in a dependency graph enables it for every [`Compat`] user in that binary.
37//! Nothing that works without the feature stops working with it - a multi-threaded scheduler is
38//! strictly more permissive - but it is worth knowing that the choice is not local to one crate.
39//!
40//! # Examples
41//!
42//! This program reads lines from stdin and echoes them into stdout, except it's not going to work:
43//!
44//! ```compile_fail
45//! fn main() -> std::io::Result<()> {
46//!     futures::executor::block_on(async {
47//!         let stdin = tokio::io::stdin();
48//!         let mut stdout = tokio::io::stdout();
49//!
50//!         // The following line will not work for two reasons:
51//!         // 1. Runtime error because stdin and stdout are used outside tokio context.
52//!         // 2. Compilation error due to mismatched `AsyncRead` and `AsyncWrite` traits.
53//!         futures::io::copy(stdin, &mut stdout).await?;
54//!         Ok(())
55//!     })
56//! }
57//! ```
58//!
59//! To get around the compatibility issues, apply the [`Compat`] adapter to `stdin`, `stdout`, and
60//! [`futures::io::copy()`]:
61//!
62//! ```
63//! use async_compat::CompatExt;
64//!
65//! fn main() -> std::io::Result<()> {
66//!     futures::executor::block_on(async {
67//!         let stdin = tokio::io::stdin();
68//!         let mut stdout = tokio::io::stdout();
69//!
70//!         futures::io::copy(stdin.compat(), &mut stdout.compat_mut()).compat().await?;
71//!         Ok(())
72//!     })
73//! }
74//! ```
75//!
76//! It is also possible to apply [`Compat`] to the outer future passed to
77//! [`futures::executor::block_on()`] rather than [`futures::io::copy()`] itself.
78//! When applied to the outer future, individual inner futures don't need the adapter because
79//! they're all now inside tokio context:
80//!
81//! ```no_run
82//! use async_compat::{Compat, CompatExt};
83//!
84//! fn main() -> std::io::Result<()> {
85//!     futures::executor::block_on(Compat::new(async {
86//!         let stdin = tokio::io::stdin();
87//!         let mut stdout = tokio::io::stdout();
88//!
89//!         futures::io::copy(stdin.compat(), &mut stdout.compat_mut()).await?;
90//!         Ok(())
91//!     }))
92//! }
93//! ```
94//!
95//! The compatibility adapter converts between tokio-based and futures-based I/O types in any
96//! direction. Here's how we can write the same program by using futures-based I/O types inside
97//! tokio:
98//!
99//! ```no_run
100//! use async_compat::CompatExt;
101//! use blocking::Unblock;
102//!
103//! #[tokio::main]
104//! async fn main() -> std::io::Result<()> {
105//!     let mut stdin = Unblock::new(std::io::stdin());
106//!     let mut stdout = Unblock::new(std::io::stdout());
107//!
108//!     tokio::io::copy(&mut stdin.compat_mut(), &mut stdout.compat_mut()).await?;
109//!     Ok(())
110//! }
111//! ```
112//!
113//! Finally, we can use any tokio-based crate from any other async runtime.
114//! Here are [reqwest] and [warp] as an example:
115//!
116//! ```no_run
117//! use async_compat::{Compat, CompatExt};
118//! use warp::Filter;
119//!
120//! fn main() {
121//!     futures::executor::block_on(Compat::new(async {
122//!         // Make an HTTP GET request.
123//!         let response = reqwest::get("https://www.rust-lang.org").await.unwrap();
124//!         println!("{}", response.text().await.unwrap());
125//!
126//!         // Start an HTTP server.
127//!         let routes = warp::any().map(|| "Hello from warp!");
128//!         warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
129//!     }))
130//! }
131//! ```
132//!
133//! [blocking]: https://docs.rs/blocking
134//! [futures]: https://docs.rs/futures
135//! [reqwest]: https://docs.rs/reqwest
136//! [tokio]: https://docs.rs/tokio
137//! [warp]: https://docs.rs/warp
138//! [`futures::io::copy()`]: https://docs.rs/futures/0.3/futures/io/fn.copy.html
139//! [`futures::executor::block_on()`]: https://docs.rs/futures/0.3/futures/executor/fn.block_on.html
140
141#![allow(clippy::needless_doctest_main)]
142#![doc(
143    html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
144)]
145#![doc(
146    html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
147)]
148
149use std::future::Future;
150use std::io;
151use std::pin::Pin;
152use std::task::{Context, Poll};
153#[cfg(not(feature = "multi-thread"))]
154use std::thread;
155
156use futures_core::ready;
157use once_cell::sync::Lazy;
158use pin_project_lite::pin_project;
159
160/// Applies the [`Compat`] adapter to futures and I/O types.
161pub trait CompatExt {
162    /// Applies the [`Compat`] adapter by value.
163    ///
164    /// # Examples
165    ///
166    /// ```
167    /// use async_compat::CompatExt;
168    ///
169    /// let stdout = tokio::io::stdout().compat();
170    /// ```
171    fn compat(self) -> Compat<Self>
172    where
173        Self: Sized;
174
175    /// Applies the [`Compat`] adapter by shared reference.
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// use async_compat::CompatExt;
181    ///
182    /// let original = tokio::io::stdout();
183    /// let stdout = original.compat_ref();
184    /// ```
185    fn compat_ref(&self) -> Compat<&Self>;
186
187    /// Applies the [`Compat`] adapter by mutable reference.
188    ///
189    /// # Examples
190    ///
191    /// ```
192    /// use async_compat::CompatExt;
193    ///
194    /// let mut original = tokio::io::stdout();
195    /// let stdout = original.compat_mut();
196    /// ```
197    fn compat_mut(&mut self) -> Compat<&mut Self>;
198}
199
200impl<T> CompatExt for T {
201    fn compat(self) -> Compat<Self>
202    where
203        Self: Sized,
204    {
205        Compat::new(self)
206    }
207
208    fn compat_ref(&self) -> Compat<&Self> {
209        Compat::new(self)
210    }
211
212    fn compat_mut(&mut self) -> Compat<&mut Self> {
213        Compat::new(self)
214    }
215}
216
217pin_project! {
218    /// Compatibility adapter for futures and I/O types.
219    #[derive(Clone)]
220    pub struct Compat<T> {
221        #[pin]
222        inner: Option<T>,
223        seek_pos: Option<io::SeekFrom>,
224    }
225
226    impl<T> PinnedDrop for Compat<T> {
227        fn drop(this: Pin<&mut Self>) {
228            if this.inner.is_some() {
229                // If the inner future wasn't moved out using into_inner,
230                // enter the tokio context while the inner value is dropped.
231                let _guard = get_runtime_handle().enter();
232                this.project().inner.set(None);
233            }
234        }
235    }
236}
237
238impl<T> Compat<T> {
239    /// Applies the compatibility adapter to a future or an I/O type.
240    ///
241    /// # Examples
242    ///
243    /// Apply it to a future:
244    ///
245    /// ```
246    /// use async_compat::Compat;
247    /// use std::time::Duration;
248    ///
249    /// futures::executor::block_on(Compat::new(async {
250    ///     // We can use tokio's timers because we're inside tokio context.
251    ///     tokio::time::sleep(Duration::from_secs(1)).await;
252    /// }));
253    /// ```
254    ///
255    /// Apply it to an I/O type:
256    ///
257    /// ```
258    /// use async_compat::{Compat, CompatExt};
259    /// use futures::prelude::*;
260    ///
261    /// # fn main() -> std::io::Result<()> {
262    /// futures::executor::block_on(Compat::new(async {
263    ///     // The `write_all` method comes from `futures::io::AsyncWriteExt`.
264    ///     Compat::new(tokio::io::stdout()).write_all(b"hello\n").await?;
265    ///     Ok(())
266    /// }))
267    /// # }
268    /// ```
269    pub fn new(t: T) -> Compat<T> {
270        Compat {
271            inner: Some(t),
272            seek_pos: None,
273        }
274    }
275
276    /// Gets a shared reference to the inner value.
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// use async_compat::Compat;
282    /// use tokio::net::UdpSocket;
283    ///
284    /// # fn main() -> std::io::Result<()> {
285    /// futures::executor::block_on(Compat::new(async {
286    ///     let socket = Compat::new(UdpSocket::bind("127.0.0.1:0").await?);
287    ///     let addr = socket.get_ref().local_addr()?;
288    ///     Ok(())
289    /// }))
290    /// # }
291    /// ```
292    pub fn get_ref(&self) -> &T {
293        self.inner
294            .as_ref()
295            .expect("inner is only None when Compat is about to drop")
296    }
297
298    /// Gets a mutable reference to the inner value.
299    ///
300    /// # Examples
301    ///
302    /// ```no_run
303    /// use async_compat::Compat;
304    /// use tokio::net::TcpListener;
305    ///
306    /// # fn main() -> std::io::Result<()> {
307    /// futures::executor::block_on(Compat::new(async {
308    ///     let mut listener = Compat::new(TcpListener::bind("127.0.0.1:0").await?);
309    ///     let (stream, addr) = listener.get_mut().accept().await?;
310    ///     let stream = Compat::new(stream);
311    ///     Ok(())
312    /// }))
313    /// # }
314    /// ```
315    pub fn get_mut(&mut self) -> &mut T {
316        self.inner
317            .as_mut()
318            .expect("inner is only None when Compat is about to drop")
319    }
320
321    fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
322        self.project()
323            .inner
324            .as_pin_mut()
325            .expect("inner is only None when Compat is about to drop")
326    }
327
328    /// Unwraps the compatibility adapter.
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// use async_compat::Compat;
334    ///
335    /// let stdout = Compat::new(tokio::io::stdout());
336    /// let original = stdout.into_inner();
337    /// ```
338    pub fn into_inner(mut self) -> T {
339        self.inner
340            .take()
341            .expect("inner is only None when Compat is about to drop")
342    }
343}
344
345impl<T: Future> Future for Compat<T> {
346    type Output = T::Output;
347
348    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
349        let _guard = get_runtime_handle().enter();
350        self.get_pin_mut().poll(cx)
351    }
352}
353
354impl<T: tokio::io::AsyncRead> futures_io::AsyncRead for Compat<T> {
355    fn poll_read(
356        self: Pin<&mut Self>,
357        cx: &mut Context<'_>,
358        buf: &mut [u8],
359    ) -> Poll<io::Result<usize>> {
360        let mut buf = tokio::io::ReadBuf::new(buf);
361        ready!(self.get_pin_mut().poll_read(cx, &mut buf))?;
362        Poll::Ready(Ok(buf.filled().len()))
363    }
364}
365
366impl<T: futures_io::AsyncRead> tokio::io::AsyncRead for Compat<T> {
367    fn poll_read(
368        self: Pin<&mut Self>,
369        cx: &mut Context<'_>,
370        buf: &mut tokio::io::ReadBuf<'_>,
371    ) -> Poll<io::Result<()>> {
372        let unfilled = buf.initialize_unfilled();
373        let poll = self.get_pin_mut().poll_read(cx, unfilled);
374        if let Poll::Ready(Ok(num)) = &poll {
375            buf.advance(*num);
376        }
377        poll.map_ok(|_| ())
378    }
379}
380
381impl<T: tokio::io::AsyncBufRead> futures_io::AsyncBufRead for Compat<T> {
382    fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
383        self.get_pin_mut().poll_fill_buf(cx)
384    }
385
386    fn consume(self: Pin<&mut Self>, amt: usize) {
387        self.get_pin_mut().consume(amt)
388    }
389}
390
391impl<T: futures_io::AsyncBufRead> tokio::io::AsyncBufRead for Compat<T> {
392    fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
393        self.get_pin_mut().poll_fill_buf(cx)
394    }
395
396    fn consume(self: Pin<&mut Self>, amt: usize) {
397        self.get_pin_mut().consume(amt)
398    }
399}
400
401impl<T: tokio::io::AsyncWrite> futures_io::AsyncWrite for Compat<T> {
402    fn poll_write(
403        self: Pin<&mut Self>,
404        cx: &mut Context<'_>,
405        buf: &[u8],
406    ) -> Poll<io::Result<usize>> {
407        self.get_pin_mut().poll_write(cx, buf)
408    }
409
410    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
411        self.get_pin_mut().poll_flush(cx)
412    }
413
414    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
415        self.get_pin_mut().poll_shutdown(cx)
416    }
417}
418
419impl<T: futures_io::AsyncWrite> tokio::io::AsyncWrite for Compat<T> {
420    fn poll_write(
421        self: Pin<&mut Self>,
422        cx: &mut Context<'_>,
423        buf: &[u8],
424    ) -> Poll<io::Result<usize>> {
425        self.get_pin_mut().poll_write(cx, buf)
426    }
427
428    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
429        self.get_pin_mut().poll_flush(cx)
430    }
431
432    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
433        self.get_pin_mut().poll_close(cx)
434    }
435}
436
437impl<T: tokio::io::AsyncSeek> futures_io::AsyncSeek for Compat<T> {
438    fn poll_seek(
439        mut self: Pin<&mut Self>,
440        cx: &mut Context,
441        pos: io::SeekFrom,
442    ) -> Poll<io::Result<u64>> {
443        if self.seek_pos != Some(pos) {
444            self.as_mut().get_pin_mut().start_seek(pos)?;
445            *self.as_mut().project().seek_pos = Some(pos);
446        }
447        let res = ready!(self.as_mut().get_pin_mut().poll_complete(cx));
448        *self.as_mut().project().seek_pos = None;
449        Poll::Ready(res)
450    }
451}
452
453impl<T: futures_io::AsyncSeek> tokio::io::AsyncSeek for Compat<T> {
454    fn start_seek(mut self: Pin<&mut Self>, pos: io::SeekFrom) -> io::Result<()> {
455        *self.as_mut().project().seek_pos = Some(pos);
456        Ok(())
457    }
458
459    fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<u64>> {
460        let pos = match self.seek_pos {
461            None => {
462                // tokio 1.x AsyncSeek recommends calling poll_complete before start_seek.
463                // We don't have to guarantee that the value returned by
464                // poll_complete called without start_seek is correct,
465                // so we'll return 0.
466                return Poll::Ready(Ok(0));
467            }
468            Some(pos) => pos,
469        };
470        let res = ready!(self.as_mut().get_pin_mut().poll_seek(cx, pos));
471        *self.as_mut().project().seek_pos = None;
472        Poll::Ready(res)
473    }
474}
475
476fn get_runtime_handle() -> tokio::runtime::Handle {
477    tokio::runtime::Handle::try_current().unwrap_or_else(|_| TOKIO1.handle().clone())
478}
479
480/// A current-thread scheduler parks unless something drives it, so a dedicated
481/// thread holds it open on a future that never completes. Tasks spawned into it
482/// therefore run on that one thread.
483#[cfg(not(feature = "multi-thread"))]
484static TOKIO1: Lazy<tokio::runtime::Runtime> = Lazy::new(|| {
485    thread::Builder::new()
486        .name("async-compat/tokio-1".into())
487        .spawn(|| TOKIO1.block_on(Pending))
488        .unwrap();
489    tokio::runtime::Builder::new_current_thread()
490        .enable_all()
491        .build()
492        .expect("cannot start tokio-1 runtime")
493});
494
495/// A multi-threaded scheduler drives its own workers, reactor and timer, so it
496/// needs no thread to hold it open. Spawned tasks run on real worker threads,
497/// which is what makes [`tokio::task::block_in_place`] usable inside them.
498#[cfg(feature = "multi-thread")]
499static TOKIO1: Lazy<tokio::runtime::Runtime> = Lazy::new(|| {
500    tokio::runtime::Builder::new_multi_thread()
501        .enable_all()
502        .thread_name("async-compat/tokio-1")
503        .build()
504        .expect("cannot start tokio-1 runtime")
505});
506
507#[cfg(not(feature = "multi-thread"))]
508struct Pending;
509
510#[cfg(not(feature = "multi-thread"))]
511impl Future for Pending {
512    type Output = ();
513
514    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
515        Poll::Pending
516    }
517}
518
519#[cfg(test)]
520mod tests {
521    use super::Lazy;
522    use crate::{CompatExt, TOKIO1};
523
524    #[test]
525    fn fallback_runtime_is_created_if_and_only_if_outside_tokio_context() {
526        // Use compat inside of a tokio context.
527        tokio::runtime::Builder::new_multi_thread()
528            .enable_all()
529            .build()
530            .unwrap()
531            .block_on(use_tokio().compat());
532
533        // We didn't need to create the fallback runtime, because we used compat
534        // inside of an existing tokio context.
535        assert!(Lazy::get(&TOKIO1).is_none());
536
537        // Use compat outside of a tokio context.
538        futures::executor::block_on(use_tokio().compat());
539
540        // We must have created the fallback runtime, because we used compat
541        // outside of a tokio context.
542        assert!(Lazy::get(&TOKIO1).is_some());
543    }
544
545    async fn use_tokio() {
546        tokio::time::sleep(std::time::Duration::from_micros(1)).await
547    }
548}