|
| 1 | +# 😱 Status quo stories: Barbara writes a runtime-agnostic library |
| 2 | + |
| 3 | + |
| 4 | +## 🚧 Warning: Draft status 🚧 |
| 5 | + |
| 6 | +This is a draft "status quo" story submitted as part of the brainstorming period. It is derived from |
| 7 | +real-life experiences of actual Rust users and is meant to reflect some of the challenges that Async |
| 8 | +Rust programmers face today. |
| 9 | + |
| 10 | +If you would like to expand on this story, or adjust the answers to the FAQ, feel free to open a PR |
| 11 | +making edits (but keep in mind that, as they reflect peoples' experiences, status quo stories |
| 12 | +[cannot be wrong], only inaccurate). Alternatively, you may wish to |
| 13 | +[add your own status quo story][htvsq]! |
| 14 | + |
| 15 | +## The story |
| 16 | + |
| 17 | +Barbara and Alan work at AmoolgeSoft, where many teams are switching from Java to Rust. These teams |
| 18 | +have many different use cases and various adoption stories. Some teams are happy users of tokio, |
| 19 | +others happy users of async-std, and others still are using custom runtimes for highly specialized |
| 20 | +use cases. |
| 21 | + |
| 22 | +Barbara is tasked with writing a library for a custom protocol, SLOW (only in use at AmoogleSoft) |
| 23 | +and enlists the help of Alan in doing so. Alan is already aware that [not all libraries in Rust work |
| 24 | +with all runtimes][nalirwwar]. Alan and Barbara start by writing a parser which works on |
| 25 | +`std::io::Read` and get their tests working with `String`s. After this they contemplate the question |
| 26 | +of how to accept a TCP connection. |
| 27 | + |
| 28 | +### Incompatible `AsyncRead` traits |
| 29 | + |
| 30 | +Alan asks Barbara what is the async equivalent is of `std::io::Read`, and Barbara sighs and says |
| 31 | +that there isn't one. Barbara brings up tokio's and the [futures crate]'s versions of `AsyncRead`. |
| 32 | +Barbara decides not to talk about `AsyncBufRead` for now. |
| 33 | + |
| 34 | +Barbara and Alan decide to use the future's `AsyncRead` for no other reason other than it is |
| 35 | +runtime-agnostic. Barbara tells Alan not to worry as they can translate between the two. With |
| 36 | +[some](ahwas) [effort](bnah) they convert their parser to using `AsyncRead`. |
| 37 | + |
| 38 | +Alan, excited about the progress they've made, starts working on hooking this up to actual TCP |
| 39 | +streams. Alan looks at async-std and tokio and notices their interfaces for TCP are quite different. |
| 40 | +Alan waits for Barbara to save the day. |
| 41 | + |
| 42 | +Barbara helps abstract over TCP listener and TCP stream (**TODO:** code example). One big hurdle is |
| 43 | +that tokio uses `AsyncRead` from their own crate and not the one from `futures` crate. |
| 44 | + |
| 45 | +### Task spawning |
| 46 | + |
| 47 | +After getting the TCP handling part working, they now want to spawn tasks for handling each incoming |
| 48 | +TCP connection. Again, to their disappointment, they find that there's no runtime-agnostic way to do |
| 49 | +that. |
| 50 | + |
| 51 | +Unsure on how to do this, they do some searching and find the [`agnostik`] crate. They reject it |
| 52 | +because this only supports N number of runtimes and their custom runtime is not one of them. |
| 53 | +However it gives them the idea to provide a trait for specifying how to spawn tasks on the runtime. |
| 54 | +Barbara points out that this has disadvantage of [working against orphan rules] meaning that either |
| 55 | +they have to implement the trait for all known runtimes (defeating the purpose of the exercise) or |
| 56 | +force the user to use new types. |
| 57 | + |
| 58 | +They punt on this question by implementing the trait for each of the known runtimes. They're |
| 59 | +disappointed that this means their library actually isn't runtime agnostic. |
| 60 | + |
| 61 | +### The need for timers |
| 62 | + |
| 63 | +To make things further complicated, they also are in need for a timer API. They could abstract |
| 64 | +runtime-specific timer APIs in their existing trait they use for spawning, but they find a |
| 65 | +runtime-agnostic library. It works but is pretty heavy in that it spawns an OS thread (from a pool) |
| 66 | +every time they want to sleep. They become sadder. |
| 67 | + |
| 68 | +### Channels |
| 69 | + |
| 70 | +They need channels as well but after long searches and discussions on help channels, they learn of |
| 71 | +a few runtime-agnostic implementations: `async-channel`, `futures-channel`, and trimmed down ( |
| 72 | +through feature flags) `async-std`/`tokio`. They pick one and it seems to work well. They become |
| 73 | +less sadder. |
| 74 | + |
| 75 | +### First release |
| 76 | + |
| 77 | +They get things working but it was a difficult journey to get to the first release. Some of their |
| 78 | +users find the APIs harder to use than their runtime-specific libs. |
| 79 | + |
| 80 | +## 🤔 Frequently Asked Questions |
| 81 | + |
| 82 | +*Here are some standard FAQ to get you started. Feel free to add more!* |
| 83 | + |
| 84 | +### **Why did you choose Barbara to tell this story?** |
| 85 | +[Barbara] has years of rust experience that she brings to bear in her async learning experiences. |
| 86 | + |
| 87 | +### **What are the morals of the story?** |
| 88 | + |
| 89 | +* People have to roll their own implementations which can lead to often subtle differences between |
| 90 | + runtimes (For example TCPListeners in `async-std` and `tokio`). |
| 91 | +* Orphan rules and no standard traits guarantee that a truly agnostic library is not possible. |
| 92 | +* Takes way more time than writing synchronous protocols. |
| 93 | +* It's a hard goal to achieve. |
| 94 | +* Leads to poorer APIs sometimes (both in ease of use and **performance**). |
| 95 | +* More API design considerations need to go into making an generic async library than a generic sync library. |
| 96 | + |
| 97 | +### **What are the sources for this story?** |
| 98 | +Personal experiences of the author from adding async API in [`zbus`] crate, except for `AsyncRead`, |
| 99 | +which is based on common knowledge in async Rust community. |
| 100 | + |
| 101 | +### **How would this story have played out differently for the other characters?** |
| 102 | +Alan, Grace, and Niklaus would be overwhelmed and will likely want to give up. |
| 103 | + |
| 104 | +### What are other related stories? |
| 105 | + |
| 106 | +**TODO:** |
| 107 | + |
| 108 | +### What are the downside of using runtime agnostic crates? |
| 109 | + |
| 110 | +Some things can be implemented very efficiently in a runtime-agnostic way but even then you can't |
| 111 | +integrate deeply into the runtime. For example, see tokio’s pre-emption strategy, which relies on |
| 112 | +deep integration with the runtime. |
| 113 | + |
| 114 | +### What other runtime utilities are generally needed? |
| 115 | +* [async-locks][async-locks-story] |
| 116 | + |
| 117 | +[status quo stories]: ./status_quo.md |
| 118 | +[Barbara]: ../characters/barbara.md |
| 119 | +[htvsq]: ../how_to_vision/status_quo.md |
| 120 | +[ahwas]: https://rust-lang.github.io/wg-async-foundations/vision/status_quo/alan_hates_writing_a_stream.html |
| 121 | +[bnah]: https://rust-lang.github.io/wg-async-foundations/vision/status_quo/barbara_needs_async_helpers.html |
| 122 | +[working against orphan rules]: https://github.com/rust-lang/wg-async-foundations/issues/180 |
| 123 | +[futures crate]: https://crates.io/crates/futures |
| 124 | +[nalirwwar]: https://rust-lang.github.io/wg-async-foundations/vision/status_quo/alan_picks_web_server.html#first-problem-incompatible-runtimes |
| 125 | +[`agnostik`]: https://crates.io/crates/agnostik |
| 126 | +[`zbus`]: https://crates.io/crates/zbus/2.0.0-beta.3 |
| 127 | +[async-locks-story]: https://rust-lang.github.io/wg-async-foundations/vision/status_quo/alan_thinks_he_needs_async_locks.html |
0 commit comments