Skip to content

Commit 130e012

Browse files
committed
status quo: Barbara writes a runtime-agnostic library
This is a story from the async vision story session of April 16, 2021.
1 parent 926057d commit 130e012

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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 some
36+
effort (**TODO:** maybe link a story about `Pin`? which one?) they convert their parser to using
37+
`AsyncRead`.
38+
39+
Alan, excited about the progress they've made, starts working on hooking this up to actual TCP
40+
streams. Alan looks at async-std and tokio and notices their interfaces for TCP are quite different.
41+
Alan waits for Barbara to save the day.
42+
43+
Barbara helps abstract over TCP listener and TCP stream (**TODO:** code example). One big hurdle is
44+
that tokio uses `AsyncRead` from their own crate and not the one from `futures` crate.
45+
46+
### Task spawning
47+
48+
After getting the TCP handling part working, they now want to spawn tasks for handling each incoming
49+
TCP connection. Again, to their disappointment, they find that there's no runtime-agnostic way to do
50+
that.
51+
52+
Unsure on how to do this, they do some searching and find the [`agnostik`] crate. They reject it
53+
because this only supports N number of runtimes and their custom runtime is not one of them.
54+
However it gives them the idea to provide a trait for specifying how to spawn tasks on the runtime.
55+
Barbara points out that this has disadvantage of working against orphan rules meaning that either
56+
they have to implement the trait for all known runtimes (defeating the purpose of the exercise) or
57+
force the user to use new types.
58+
59+
They punt on this question by implementing the trait for each of the known runtimes. They're
60+
disappointed that this means their library actually isn't runtime agnostic.
61+
62+
### The need for timers
63+
64+
To make things further complicated, they also are in need for a timer API. They could abstract
65+
runtime-specific timer APIs in their existing trait they use for spawning, but they find a
66+
runtime-agnostic library. It works but is pretty heavy in that it spawns an OS thread (from a pool)
67+
every time they want to sleep. They become sadder.
68+
69+
### Channels
70+
71+
They need channels as well but after long searches and discussions on help channels, they learn of
72+
a few runtime-agnostic implementations: `async-channel`, `futures-channel`, and trimmed down (
73+
through feature flags) `async-std`/`tokio`. They pick one and it seems to work well. They become
74+
less sadder.
75+
76+
### First release
77+
78+
They get things working but it was a difficult journey to get to the first release. Some of their
79+
users find the APIs harder to use than their runtime-specific libs.
80+
81+
## 🤔 Frequently Asked Questions
82+
83+
*Here are some standard FAQ to get you started. Feel free to add more!*
84+
85+
### **Why did you choose Barbara to tell this story?**
86+
[Barbara] has years of rust experience that she brings to bear in her async learning experiences.
87+
88+
### **What are the morals of the story?**
89+
90+
* People have to roll their own implementations which can lead to often subtle differences between
91+
runtimes (For example TCPListeners in `async-std` and `tokio`).
92+
* Orphan rules and no standard traits guarantee that a truly agnostic library is not possible.
93+
* Takes way more time than writing synchronous protocols.
94+
* It's a hard goal to achieve.
95+
* Leads to poorer APIs sometimes (both in ease of use and **performance**).
96+
* More thought is needed to write such libraries.
97+
98+
### **What are the sources for this story?**
99+
Personal experiences of the author from adding async API in [`zbus`] crate, except for `AsyncRead`,
100+
which is based on common knowledge in async Rust community.
101+
102+
### **How would this story have played out differently for the other characters?**
103+
Alan, Grace, and Niklaus would be overwhelmed and will likely want to give up.
104+
105+
### What are other related stories?
106+
107+
**TODO:**
108+
109+
### What are the downside of using runtime agnostic crates?
110+
111+
Some things can be implemented very efficiently in a runtime-agnostic way but even then you can't
112+
integrate deeply into the runtime. For example, see tokio’s pre-emption strategy, which relies on
113+
deep integration with the runtime.
114+
115+
### What other runtime utilities are generally needed?
116+
* [async-locks][async-locks-story]
117+
118+
[status quo stories]: ./status_quo.md
119+
[Barbara]: ../characters/barbara.md
120+
[htvsq]: ../how_to_vision/status_quo.md
121+
[futures crate]: https://crates.io/crates/futures
122+
[nalirwwar]: https://rust-lang.github.io/wg-async-foundations/vision/status_quo/alan_picks_web_server.html#first-problem-incompatible-runtimes
123+
[`agnostik`]: https://crates.io/crates/agnostik
124+
[`zbus`]: https://crates.io/crates/zbus/2.0.0-beta.3
125+
[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)