---
# System prepended metadata

title: shiny future sketch session

---

# shiny future sketch session

## participants

name, github, twitter

* Niko Matsakis (nikomatsakis, nikomatsakis)
* Simon Farnsworth (farnz, TrueFarnz)
* Emmanuel Antony (emmanuelantony2000, @emmanuelantony5)
* Zeeshan Ali (zeenix everywhere)
* Jon Gjengset, @jonhoo
* Erich (erichgess on github)
* Sean McArthur (@seanmonstar)
* Alice Ryhl (Darksonn)

## topic

topic: sync/async/blocking

## status quo

[status quo stories](https://rust-lang.github.io/wg-async-foundations/vision/status_quo.html)

* https://rust-lang.github.io/wg-async-foundations/vision/status_quo/alan_finds_database_drops_hard.html
    * natural place where sync/async end up interacting
    * drop is sync but you're doing it on async things
    * https://github.com/rust-lang/wg-async-foundations/pull/167 -- one possible story in this area
* https://github.com/rust-lang/wg-async-foundations/pull/164#issuecomment-824028298
    * trait that is designed for sync
    * the impl of it wants to be async inside of it
    * rustls doesn't have this concept
* https://github.com/rust-lang/wg-async-foundations/blob/master/src/vision/status_quo/barbara_bridges_sync_and_async.md
    * problem of trying to do async things inside of an iterator
* https://rust-lang.github.io/wg-async-foundations/vision/status_quo/alan_thinks_he_needs_async_locks.html
    * holding sync locks across a yield point can cause deadlocks; other things are in sync code and not interacting with the executor
    * if your mutex guard is `Send`, you can get this using sync locks purely in async code
    * if you use `spawn_local` to spawn `!Send` tasks, you can have similar problems
* https://rust-lang.github.io/wg-async-foundations/vision/status_quo/barbara_compares_some_cpp_code.html
    * core problem: have some function that is normally cheap but (unpredictably) very expensive
    * 
* Footgun lurking in FuturesUnordered and other concurrency-enabling streams [#2387](https://github.com/rust-lang/futures-rs/issues/2387)
    * if you don't poll your futures-unordered regularly, things inside it don't get polled at all
    * problem is that we are doing `while let Some(item) = futures_unordered.await` but then inside the loop you do a `await` that means `futures_unordered` is not going to get polled at all
    * async C++ developers spawn this into a task; in Rust, this is more painful because of the `'static` bounds and lack of scoped futures and things

## problem: futures-unordered

* sometimes you want to do async things in sync settings
* to address [#2387]--
    * could conceivably have scoped tasks to address this
    * this would make it easier to fix, but would it help to prevent the code to begin with?
        * in sync code, you would have same problem with a tcp listener-- why is it different?
        * in async code, it works in small test cases but fails in prod
            * somewhat specific to buffering effect
    * could you detect it dynamically?
        * some kind of tool that is looking for long delays between when it is woken (from the `Waker`) to when it is actually polled
            * look for hazards
        * jon: did a lot of this in noria ('which things have been woken up but not polled' or 'it took a long time')
        * alice: I often show people this little wrapper future you can put around your spawn calls that will help detect what tasks are blocking things
* "when do you spawn things that are long running"
    * often runtime dependent
* go trace tool (Erich)
    * allows you to trace the flow of communication between threads
    * really useful for tracking down small pauses etc; it's slow to run
    * https://about.sourcegraph.com/go/an-introduction-to-go-tool-trace-rhys-hiltner/
    * go compiler detects common deadlocks and fails compilation, also fatal runtime errors
* existing shiny story: barbara makes a wish

### rough story output

* story goes kind of like this
    * you write the buffered code
    * you run cargo test
    * you get a print-out like "hey this future is being awoken but the task is busy blocking on this other one, you probably want a spawn"
        * perhaps it shows up in your IDE

### alternative story

* you write the buffered code
* some lint detects this pattern

## problem: some functions are a bad idea

* lint-- pretty easy to do
* https://rust-lang.github.io/wg-async-foundations/vision/shiny_future/alans_trust_in_the_compiler_is_rewarded.html
    * note: sometimes it's ok to use the std/io traits, if you are writing 
* could do some dynamic checking as well, part of `cargo test`
    * though sometimes that's ok

## problem: some polls are taking too long

* measuring how much time a call to poll takes, look for outliers
* also: which things have been polled but have not returned

## problem: trying to use async code behind a sync interface

* when this actually becomes a problem:
    * you are in async code
    * the trait or something else forces you 
    * you need to do an async operation
* what are possible outcomes:
    * panic -- today
        * how can we expect users to fix it?
    * "works" -- 
        * but if thread is blocked
* what are the choices you could do today
    * at the async layer, you would do a spawn-blocking around the sync layer
        * and sync layer can do a block-on
        * but often you don't have access to the async->sync transition point, it's a non-local change
    * other option:
        * something like block-in-place
        * but then the futures that are higher up on the stack are unable to be polled
            * similar to the futures-unordered
* one option:
    * dynamic wizardry to fork off the sync call (cilk used to do this)

* this pattern would still be a problem:
```rust=
while let Some(x) = futures_unordered.next().await {
    spawn_blocking(|| ).await; // <-- 
}
```

* but if a future *inside* the `futures_unordered` had this issue, and used `spawn_blocking`, that would be ok

```rust=
let mut results = FuturesUnordered::new();
while let Some(x) = futures_unordered.next().await {
    results.push(spawn_blocking(|| ...));
}
results.for_each(|_| ()).await;
```

### story one: we do dark magic on the stack and try to make it work automatically

* we dynamically rewrite the stack in some amazing way to make it similar to `spawn_blo`

### story two: we guide user to an edit that works (a)

* you run `cargo test` and you get a helpful print-out

### story three

## assignments

* basic story flows
    * cargo test --trace -- figures out bad situations and how to help you (erich)
    * magic stack rewriting (seanmonstar/pnkfelix)
    * lint for problematic patterns 
    * 