Skip to content

Commit b9e164a

Browse files
zeenixrylev
andcommitted
status quo: Barbara writes a runtime-agnostic library
This is a story from the async vision story session of April 16, 2021. Co-authored-by: Ryan Levick <[email protected]>
1 parent 926057d commit b9e164a

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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

Comments
 (0)