# 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 *