README.md
1 # tor-rtcompat 2 3 Compatibility between different async runtimes for Arti. 4 5 ## Overview 6 7 Rust's support for asynchronous programming is powerful, but still 8 a bit immature: there are multiple powerful runtimes you can use, 9 but they do not expose a consistent set of interfaces. 10 11 The [`futures`] API abstracts much of the differences among these 12 runtime libraries, but there are still areas where no standard API 13 yet exists, including: 14 - Network programming. 15 - Time and delays. 16 - Launching new tasks 17 - Blocking until a task is finished. 18 19 Additionally, the `AsyncRead` and `AsyncWrite` traits provide by 20 [`futures`] are not the same as those provided by `tokio`, and 21 require compatibility wrappers to use. 22 23 To solve these problems, the `tor-rtcompat` crate provides a set 24 of traits that represent a runtime's ability to perform these 25 tasks, along with implementations for these traits for the `tokio` 26 and `async-std` runtimes. In the future we hope to add support 27 for other runtimes as needed. 28 29 This crate is part of 30 [Arti](https://gitlab.torproject.org/tpo/core/arti/), a project to 31 implement [Tor](https://www.torproject.org/) in Rust. 32 As such, it does not currently include (or 33 plan to include) any functionality beyond what Arti needs to 34 implement Tor. 35 36 We hope that in the future this crate can be replaced (or mostly 37 replaced) with standardized and general-purpose versions of the 38 traits it provides. 39 40 ## Using `tor-rtcompat` 41 42 The `tor-rtcompat` crate provides several traits that 43 encapsulate different runtime capabilities. 44 45 * A runtime is a [`BlockOn`] if it can block on a future. 46 * A runtime is a [`SleepProvider`] if it can make timer futures that 47 become Ready after a given interval of time. 48 * A runtime is a [`CoarseTimeProvider`] if it provides a monotonic clock 49 which is fast to query, 50 but perhaps has lower-precision or lower-accuracy. 51 * A runtime is a [`NetStreamProvider`]`<std::net::SocketAddr>` if it can make and receive TCP 52 connections 53 * A runtime is a [`TlsProvider`] if it can make TLS connections. 54 55 For convenience, the [`Runtime`] trait derives from all the traits 56 above, plus [`futures::task::Spawn`] and [`Send`]. 57 58 You can get a [`Runtime`] in several ways: 59 60 * If you already have an asynchronous backend (for example, one 61 that you built with tokio by running with 62 `#[tokio::main]`), you can wrap it as a [`Runtime`] with 63 [`PreferredRuntime::current()`]. 64 65 * If you want to construct a default runtime that you won't be 66 using for anything besides Arti, you can use [`PreferredRuntime::create()`]. 67 68 Both of the above methods use the "preferred runtime", which is usually Tokio. 69 However, changing the set of Cargo features available can affect this; see 70 [`PreferredRuntime`] for more. 71 72 * If you want to use a runtime with an explicitly chosen backend, 73 name its type directly as [`async_std::AsyncStdNativeTlsRuntime`], 74 [`async_std::AsyncStdRustlsRuntime`], [`tokio::TokioNativeTlsRuntime`], 75 or [`tokio::TokioRustlsRuntime`]. To construct one of these runtimes, 76 call its `create()` method. Or if you have already constructed a 77 Tokio runtime that you want to use, you can wrap it as a 78 [`Runtime`] explicitly with `current()`. 79 80 ## Advanced usage: implementing runtimes yourself 81 82 You might want to implement some of the traits above (especially [`NetStreamProvider`] and 83 [`TlsProvider`]) if you're embedding Arti, and want more control over the resources it uses. 84 For example, you might want to perform actions when TCP connections open and close, replace the 85 TLS stack with your own, or proxy TCP connections over your own custom transport. 86 87 This can be more easily accomplished using the [`CompoundRuntime`] type, which lets you 88 create a [`Runtime`] from various implementors of the various traits (which don't all need to 89 be the same). 90 91 See [`arti-client/examples/hook-tcp.rs`](https://gitlab.torproject.org/tpo/core/arti/-/blob/main/crates/arti-client/examples/hook-tcp.rs) 92 for a full example of this. 93 94 ## Cargo features 95 96 Features supported by this crate: 97 98 * `tokio` -- build with [Tokio](https://tokio.rs/) support 99 * `async-std` -- build with [async-std](https://async.rs/) support 100 * `native-tls` -- build with the [native-tls](https://github.com/sfackler/rust-native-tls) 101 crate for TLS support 102 * `static` -- link the native TLS library statically (enables the `vendored` feature of the 103 `native-tls` crate). 104 * `rustls` -- build with the [rustls](https://github.com/rustls/rustls) crate for TLS support. Note that `rustls` uses the `ring` crate, which uses 105 the old (3BSD/SSLEay) OpenSSL license, which may introduce licensing 106 compatibility issues. 107 108 By default, *this* crate doesn't enable any features. However, you're almost certainly 109 using this as part of the `arti-client` crate, which will enable `tokio` and `native-tls` in 110 its default configuration. 111 112 ## Design FAQ 113 114 ### Why support `async_std`? 115 116 Although Tokio currently a more popular and widely supported 117 asynchronous runtime than `async_std` is, we believe that it's 118 critical to build Arti against multiple runtimes. 119 120 By supporting multiple runtimes, we avoid making tokio-specific 121 assumptions in our code, which we hope will make it easier to port 122 to other environments (like WASM) in the future. 123 124 ### Why a `Runtime` trait, and not a set of functions? 125 126 We could simplify this code significantly by removing most of the 127 traits it exposes, and instead just exposing a single 128 implementation. For example, instead of exposing a 129 [`BlockOn`] trait to represent blocking until a task is 130 done, we could just provide a single global `block_on` function. 131 132 That simplification would come at a cost, however. First of all, 133 it would make it harder for us to use Rust's "feature" system 134 correctly. Current features are supposed to be _additive only_, 135 but if had a single global runtime, then support for different 136 backends would be _mutually exclusive_. (That is, you couldn't 137 have both the tokio and async-std features building at the same 138 time.) 139 140 Secondly, much of our testing in the rest of Arti relies on the 141 ability to replace [`Runtime`]s. By treating a runtime as an 142 object, we can override a runtime's view of time, or of the 143 network, in order to test asynchronous code effectively. 144 (See the [`tor-rtmock`] crate for examples.) 145 146 License: MIT OR Apache-2.0