Skip to content

Commit 809f8f0

Browse files
authored
Merge pull request #38 from rylev/alan-stack-trouble
Add story of Alan running in trouble with large stack allocations
2 parents ec65e2a + 41f6e06 commit 809f8f0

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

src/vision/how_to_vision.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ The process for writing the vision doc encourages active collaboration and "posi
1818

1919
The brainstorming period runs until 2021-04-30:
2020

21-
* Folks [open status quo][hvsq] and (eventually) [shiny future][hvsf] narrative PRs.
21+
* Folks [open status quo][hvsq] and (eventually) [shiny future][hvsf] narrative [PRs][repo].
2222
* We collectively [comment] on these PRs, helping to improve them and make them more complete.
2323
* At the end of the brainstorming period, we will vote and give [awards] for things like "most amusing". (We'd like suggestions on the best categories!)
2424
* All PRs opened in this section will be merged into a "brainstorming" folder; they become the basis for the final document.
@@ -46,6 +46,7 @@ This meant to be a **living document**. We plan to revisit it regularly to track
4646
[awards]: ./how_to_vision/awards.md
4747
[wg leads]: ../welcome.md#leads
4848
[hva]: ./how_to_vision/applications.md
49+
[repo]: https://github.com/rust-lang/wg-async-foundations
4950

5051
## Wait, did somebody say awards?
5152

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 😱 Status quo stories: Alan runs into stack allocation trouble
2+
3+
[Alan]: ../characters/alan.md
4+
[Grace]: ../characters/grace.md
5+
[Niklaus]: ../characters/niklaus.md
6+
[Barbara]: ../characters/barbara.md
7+
8+
[Alan runs into stack allocation trouble and is able to fix problems]: TODO
9+
10+
### The problem
11+
12+
One day, as Alan is working on his async Rust project, he runs his application and hits an error:
13+
14+
```
15+
$ .\target\debug\application.exe
16+
thread 'main' has overflowed its stack
17+
```
18+
19+
Perplexed, Alan sees if anything with his application works by seeing if he can get output when the `--help` flag is passed, but he has no luck:
20+
21+
```
22+
$ .\target\debug\application.exe --help
23+
thread 'main' has overflowed its stack
24+
```
25+
26+
### Searching for the solution
27+
28+
Having really only ever seen stack overflow issues caused by recursive functions, Alan desperately tries to find the source of the bug but searching through the codebase for recursive functions only to find none. Having learned that Rust favors stack allocation over heap allocation (a concept Alan didn't really need to worry about before), he started manually looking through his code, searching for structs that looked "too large"; he wasn't able to find any candidates.
29+
30+
Confused, Alan reached out to Grace for her advice. She suggested making the stack size larger. Although she wasn't a Windows expert, she remembers hearing that stack sizes on Windows might be smaller than on Linux. After much searching, Alan discovers an option do just that: `RUSTFLAGS = "-C link-args=-Wl,-zstack-size=<size in bytes>"`.
31+
32+
While eventually Alan gets the program to run, the stack size must be set to 4GB before it does! This seems untenable, and Alan goes back to the drawing board.
33+
34+
Alan reaches out to Barbara for her expertise in Rust to see if she has something to suggest. Barbara recommends using `RUSTFLAGS = "-Zprint-type-sizes` to print some type sizes and see if anything jumps out. Barbara noted that if Alan does find a type that stands out, it's usually as easy as putting some boxes in that type to provide some indirection and not have everything be stack allocated. Alan never needs the nightly toolchain, but this option requires it so he installs it using `rustup`. After searching through types, one did stand out as being quite large. Ultimately, this was a red herring, and putting parts of it in `Box`es did not help.
35+
36+
### Finding the solution
37+
38+
After getting no where, Alan went home for the weekend defeated. On Monday, he decided to take another look. One piece of code, stuck out to him: the use of the `select!` macro from the `futures` crate. This macro allowed multiple futures to race against each other, returning the value of the first one to finish. This macro required the futures to be pinned which the docs had shown could be done by using `pin_mut!`. Alan didn't fully grasp what `pin_mut!` was actually doing when he wrote that code. The compiler had complained to him that the futures he was passing to `select!` needed to be pinned, and `pin_mut!` was what he found to make the compiler happy.
39+
40+
Looking back at the documents made it clear to Alan that this could potentially be the issue: `pin_mut!` pins futures to the stack. It was relatively clear that a possible solution would be to pin to the heap instead of the stack. Some more digging in the docs lead Alan to `Box::pin` which did just that. An extra heap allocation was of no consequence to him, so he gave it a try. Lo and behold, this fixed the issue!
41+
42+
While Alan knew enough about pinning to know how to satisfy the compiler, he didn't originally take the time to fully understand what the consequences were of using `pin_mut!` to pin his futures. Now he knows!
43+
44+
## 🤔 Frequently Asked Questions
45+
46+
* **What are the morals of the story?**
47+
* When coming from a background of GCed languages, taking the time to understand the allocation profile of a particular piece of code is not something Alan was used to doing.
48+
* It was hard to tell where in his code the stack was being exhausted. Alan had to rely on manually combing his code to find the culprit.
49+
* Pinning is relatively confusing, and although the code compiled, Alan didn't fully understand what he wrote and what consequences his decision to use `pin_mut!` would have.
50+
* **What are the sources for this story?**
51+
* This story is adapted from the experiences of the team working on the [Krustlet](https://github.com/deislabs/krustlet) project. You can read about this story in their own words [here](https://deislabs.io/posts/a-heaping-helping-of-stacks/).
52+
* **Why did you choose Alan to tell this story?**
53+
* The programmers this story was based on have an experience mostly in Go, a GCed language.
54+
* The story is rooted in the explicit choice of using stack vs heap allocation, a choice that in GCed languages is not in the hands of the programmer.
55+
* **How would this story have played out differently for the other characters?**
56+
* Grace would have likely had a similar hard time with this bug. While she's used to the tradeoffs of stack vs heap allocations, the analogy to the `Pin` API is not present in languages she's used to.
57+
* Barbara, as an expert in Rust, may have had the tools to understand that `pin_mut` is used for pinning to the stack while `Box::pin` is for pinning heap allocations.
58+
* This problem is somewhat subtle, so someone like Niklaus would probably have had a much harder time figuring this out (or even getting the code to compile in the first place).
59+
* **Could Alan have used another API to achieve the same objectives?**
60+
* Perhaps! Tokio's `select!` macro doesn't require explicit pinning of the futures it's provided, but it's unclear to this author whether it would have been smart enough to avoid pinning large futures to the stack. However, pinning is a part of the way one uses futures in Rust, so it's possible that such an issue would have arisen elsewhere.

0 commit comments

Comments
 (0)