4 ms·
So, in other languages, async introduces two function "colors": sync and async. But in Rust you also have unique "colors" for each async runtime? I must be mis
by aaaaaaaaaaab 4y ago
So, in other languages, async introduces two function "colors": sync and async. But in Rust you also have unique "colors" for each async runtime?
I must be misunderstanding something, because this sounds like pretty hare-brained...
- ben-schaaf 4y agoYou get the same thing in every language where you can choose a runtime. Whether the colors are enforced by the compiler is another matter, but since you can only have one runtime per thread and each runtime has a different API you inherently get runtime colors.
- aaaaaaaaaaab 4y ago>you can only have one runtime per thread Why? Why can’t a single thread run multiple runtimes’ event loops? It’s pretty limited what an event loop can do: it can wait on a set of FDs, or sleep for some time (or until woken up). So if the various runtimes’ event loops could implement a common interface, then I don’t see why they couldn’t share a single thread…
- ben-schaaf 4y ago> It’s pretty limited what an event loop can do: it can wait on a set of FDs, or sleep for some time (or until woken up). But that's what the runtime is. There's multiple strategies for implementing an event loop (as well as multiple async platform APIs to use), which all directly affect the lower-level abstractions. By replacing the event loop and lower-level abstractions with one common implementation all you've done is add another competing runtime. You can't have more than one event loop and thus you can't have more than one runtime.
- mcronce 4y agoIf you've got a thread running one event loop, how could you signal to it to temporarily break out of that loop and run a different event loop for a while?
- aaaaaaaaaaab 4y agoThe while(1) part of the event loop would be outside, provided by the stdlib. Each runtime would provide a set of FDs that they want to monitor, and a function to execute a single iteration of their event loop. The while(1) loop would then be calling each runtime in a round-robin fashion, then go sleep while waiting on the union of all the runtimes’ FD sets. It would be sort of an “event loop of event loops”.
- stormbrew 4y agoWhat you've just described is ... an executor runtime. Even if you boil it down to doing "only" that, you still can only have one of that per thread, and there isn't just one way to implement that.
- saurik 4y agoThis isn't actually a problem most of the time because we can spawn new threads, and so in C++ with co_await I have like three or four different "runtimes" all working at once with their own thread pools and their own I/O loops and I have no issue mixing and matching their behaviors using the obvious syntax. The issue with Rust seems to come down to something much deeper involving either an overzealous attempt to force people to think in terms of "executors" and "runtimes" for entire stacks of coroutines instead of individual tasks or (at best) as a result of decisions made with respect to how the memory management of the mechanism would operate in the context of Rust.
- ibraheemdev 4y agoThis is a consequence of runtimes relying on global variables that their core future types are dependent on. Creating abstractions to solve this problem is one of the main goals of the the async working group [0]. [0]: https://github.com/rust-lang/wg-async https://github.com/rust-lang/wg-async
- stormbrew 4y agoIt would be really interesting to see an executor that didn't rely on global state, but instead had you manage an executor object for these things, and see if it's actually really all that bad. Everyone just kind of jumped straight on doing executors with global state and that design space is just completely unexplored. It would help a lot with being able to discover and define what standard traits might be needed to make leaf async libraries more portable.
- ReflectedImage 4y agoMore colors the better! Right?
- yazaddaruvala 4y agoThis is only temporary. It’s because the standard library hasn’t exposed interfaces for everything in async land just yet. It’s not a trivial problem, but it’s mostly a matter of the std-lib’s developers being overly defensive of std. Which is a hindrance for many things in the medium term, but in the long term likely a good choice. Although I wish they’d find a better balance between the chicken and egg problem for “we need production usage of the interfaces to validate them as standard” vs “we need standard interfaces so it’s ergonomic to use them in production”.
- lvass 4y agoAsync/await should be locked behind a compiler flag and not used by libraries that aren't experimental. Instead it already contaminated the entire ecosystem with to-be-deprecated dependencies that are hard to remove.
- aaaaaaaaaaab 4y agoYep. I can’t help but see this as a python2.7 vs python3 story in the long run…
- stormbrew 4y agoRust wouldn't be anywhere near where it is now if it had gated and blocked async like this. Maybe it would be 'better' in some abstract sense, but it'd be a better language almost no one would be using, like basically every other systems language that's attempted to do what rust is doing other than C++. And if it were gated like this, we wouldn't even know about these kinds of problems, because no one would be using it and we wouldn't see them in practice. If anything, rust does too much gating of features for too long. So many things have been sitting behind "needs stabilization" for years, living in a catch-22 of "we don't know if this is a good idea because no one uses it" vs. "no one uses it because using nightly is scary". I'm quite glad this one managed to escape that trap.
- pornel 4y agoThe original "colors" article talked about two things: 1. Inability for sync functions to use async functions, which can have big consequences for API design and ability to refactor applications. 2. Author's opinion on how async syntax should (not) look like. Rust can mitigate the first problem. It can have `block_on(async)` and `spawn_sync(fn)` which allows bridging between "colors" of functions, so functions aren't forever stuck with their "color". This is something that JS can't do without massive hacks, and is the objectively important aspect of "colors". The other thing was about difference in calling syntax. That is just a design choice and a subjective preference. Rust prefers locally explicit syntax. It cares about low-level details and intentionally avoids having implicit magic, especially for major behaviors affecting control flow, safety of stack pointers, and risk of deadlocks. Regarding runtimes: in practice it's easy to just stick to tokio. It's 8x more popular than the second contender, and there aren't any important libraries that don't work with tokio. Rust can have multiple runtimes in the same program. You can have futures running on different runtimes await each other, it's just wasteful (you get multiple event loops, thread pools, etc.), which means it's best to pick one and stick with it.
- aaaaaaaaaaab 4y ago>in practice it's easy to just stick to tokio. It's 8x more popular than the second contender, and there aren't any important libraries that don't work with tokio. For now. Python2.7 used to be 8x more popular than python3.
- pornel 4y agoThat’s an empty speculation, and the analogy is flawed. Other runtimes aren’t tokio’s imposed replacement. Runtimes create self reinforcing network effect, so it’s more likely that all other contenders will die out completely.