Live data from Hacker News

Tokio 1.0 – async runtime for Rust

tokio.rs

321–330 of 422 posts

Re: Tokio 1.0 – async runtime for Rust

#321

One of the issues I have with the rust async story is that colored functions require (at least the way they’re handled in rust and similar languages) a separate standard library (Microsoft really outdid themselves providing a synchronous and asynchronous version of the BCL when they shipped async support, but that was also largely made possible by the fact that the underlying OS APIs were all fairly asynchronous at t…

> For those that don’t know, on Linux there is^H^H was really no such thing as properly async file system access (eg libc faked it in a similar fashion for aio) That's not quite right - there didn't use to be AIO for buffered filesystem IO and for most operations beyond read/write. But unbuffered reads/writes have been doable asynchronously for quite a long time, via io_submit/libaio. Without falling back to threads.…

> But unbuffered reads/writes have been doable asynchronously for quite a long time

That is not quite right because it is dependent on the filesystem. This is why scylla database requires XFS, because they rely on actual async file io using io_submit.

Re: Tokio 1.0 – async runtime for Rust

#322
post #77

I don't really get these modern async APIs. In languages like Javascript I thought they only made sense because JS interpreters are (historically) single-threaded, so you really have no choice but async to express some concepts. Fine. But in Rust you can just spawn threads, share data through channels or mutexes, use OS-provided async IO primitives to poll file descriptors and do event-driven programming etc... I tri…

I really like the Golang runtime. It uses coroutines (async) to speed up things (no context switches, low memory overhead) and automatically moves IO to threads where they can wait.

Re: Tokio 1.0 – async runtime for Rust

#323
post #160

Earlier quoted context omitted.

Ah that's a good point, it's true that the borrow checker can sometimes get in the way for event-driven architectures and async IO borrows. That being said I can't shake the feeling that going for something like Tokio in such a case is a bit like healing a paper cut by amputating the arm. Sure, technically you don't have the original problem anymore...

Can you elaborate a bit what it is that you find difficult or undesirable about Tokio? Or async/await + some runtime in general? So, I can relate to not wanting to pull in the dependency. But otherwise it seems pretty straightforward to me. You just macro-decorate the main function, sprinkle some async/await around, maybe add a join or a mutex somewhere, and then pretty much forget all about event loops, messaging, t…

> just macro-decorate the main function, sprinkle some async/await around, maybe add a join or a mutex somewhere, and then pretty much forget all about event loops, messaging, threads and whatnot

that is ... not how I have experienced it. I work on building highly concurrent systems every day but async drives me insane. to me the fundamental issue is that although the code now reads linearly, it no longer executes linearly (or reasonably close to linearly), which is 1000x more confusing.

the other thing, when I'm using rust to build something high performance, part of the reason is it provides greater control. I just can't square that with macro-decorating my main function, and handing over the core control-flow to someone else's runtime.

Re: Tokio 1.0 – async runtime for Rust

#324
post #211
post #181

Earlier quoted context omitted.

And once you get to a limit, the alternative to rewriting everything in non-blocking code might be to put multiple instances of your blocking app on multiple VMs/k8s/whatever behind a load balancer.

Or, you just take the initial leap and write async from the start. For languages with decent abstractions such as async/await, it really isn't hard when you've done it for a while, and I'd make the same argument as one of the parent posters in that it's great "documentation". K8s is brings way more complexity and headache, so it's kind of funny that you suggest that before using async/await.

> For languages with decent abstractions such as async/await, it really isn't hard when you've done it for a while

https://lucumr.pocoo.org/2016/10/30/i-dont-understand-asynci...

Re: Tokio 1.0 – async runtime for Rust

#325
post #77

I don't really get these modern async APIs. In languages like Javascript I thought they only made sense because JS interpreters are (historically) single-threaded, so you really have no choice but async to express some concepts. Fine. But in Rust you can just spawn threads, share data through channels or mutexes, use OS-provided async IO primitives to poll file descriptors and do event-driven programming etc... I tri…

I think async was inevitable for Rust, for better or for worse. My experience is that just using OS-provided threads isn't good enough for say a high performance webserver—compare the pre-tokio hyper benchmark results to the post-tokio ones for example. And Go-like green threads aren't really possible in Rust given the choice to have no runtime. (Having a stack for every coroutine also probably isn't as good for squeezing out that last bit of performance. Every stack/guard page means more TLB pressure, for example, and programs can spend a lot of time on TLB cache misses.) Rust aims to be suitable for high-performance, low-level environments, so here we are.

I agree async has usability problems. Hopefully they'll get better over time; I'm looking forward to eventually having generators rather than dealing with futures::stream::StreamExt and the like.

I don't think everyone needs to use async all the time. Take a web app, for example. If the webserver is directly Internet-facing, it has to deal with lots of keepalive connections, so the core webserver logic (hyper or equivalent) should be async. But if you're not dealing with too many active requests and don't care about the performance difference, I don't think there's any reason you shouldn't have all your request handling just use threads, sending replies to hyper with a channel and blocking when necessary.

Last I checked I couldn't find an ergonomic and efficient "half-async" bounded channel implementation. By which I mean one that allows you to treat the sender as blocking and the receiver as async, or vice versa. That'd be really useful for writing synchronous programs that use async libraries. I certainly don't see any reason one couldn't exist. Maybe it already does and I missed it.

Re: Tokio 1.0 – async runtime for Rust

#326
post #146
post #77

I don't really get these modern async APIs. In languages like Javascript I thought they only made sense because JS interpreters are (historically) single-threaded, so you really have no choice but async to express some concepts. Fine. But in Rust you can just spawn threads, share data through channels or mutexes, use OS-provided async IO primitives to poll file descriptors and do event-driven programming etc... I tri…

I had the same thing initially. The upside of async over a simple event loop is, in my experience, when things become less simple, and you end up with hard-to-read little state machines all over the place. With async, you can have your event loop, but the state machines are handled by the compiler. Code is like threaded code. That can be very convenient. Threads, obviously, accomplish the same thing, and arguably mor…

I've been amused to watch how Rust now does a simple blocking HTTP request. A few years ago, you used the "hyper" crate, which was a convenient wrapper around the "http" crate. Now, you're supposed to use "reqwest", which is a convenient wrapper around the "hyper" crate.

"Reqwest" uses the Tokio machinery, even for a blocking request. If you turn on "Trace" level logging, you can watch it start up a thread pool and go through a 35-step process, using all the async and futures machinery, to do one synchronous request. Log messages include "handshake complete, spawning background dispatcher task" and "signaled close for runtime thread (ThreadId(2))"

This seems excessive.

Re: Tokio 1.0 – async runtime for Rust

#327
post #227

Earlier quoted context omitted.

Loom is going to be a game changer. Hopefully they release it soon.

Didn't they start with green threads way back when?

Yes they did, and gave up because of serious problems with that approach. (Interestingly, so did Rust, more recently).

Re: Tokio 1.0 – async runtime for Rust

#328

Earlier quoted context omitted.

> One is they make your functions colored This is definitely a good thing. All computations should me "marked" as total or effectful with various possible effects (blocking, async, nondeterministic, possibly non-terminating etc etc). Reasoning about your program is hard when each computation is a blackbox possibly containing any side-effects which could cause unpredictable changes in the control flow and result in a…

> This is definitely a good thing. All computations should me "marked" as total or effectful with various possible effects (blocking, async, nondeterministic, possibly non-terminating etc etc). Marking functions for side-effects would be a good thing but it isn't what function coloring means in this context. An async function and a normal function are semantically the same, they just have different syntaxes and you c…

> An async function and a normal function are semantically the same

But they are not, AsyncIO and BlockingIO are different side effects, thus you have different types of computation, that's exactly what I'm talking about. In languages with monads or algebraic effects these would have different types.

You don't say that Lists and Arrays are semantically the same, despite being similar sequential collections, they still have separate types for a reason. Though it's good to be able to abstract over them.

And in languages with monads we can parametrize over various effect types by using tagless final approach, which allows us to write computations which could be interpreted in contexts of various effects (in this case, Async and Sync), just as we parametrize containers with types of content (in [1] there is an example of how we can parametrize computation over various async implementations), but still these are different effects.

[1] https://kubuszok.com/2019/io-monad-which-why-and-how/#typed-...

Re: Tokio 1.0 – async runtime for Rust

#329
post #293

I'm hopeful that this leads to some focus on the ergonomics of "waiting for async things from sync code". Lots of "handlers" in the universe have synchronous interfaces, so if you want to implement them you end up needing to poll/wait on async from a regular function. I swear that every time I poke at Rust, I seem to find some way to cut my fingers... My specific example is writing a fuse handler (now with cberner/fu…

Tokio has a version of block_on too that you can use with those libraries.

Re: Tokio 1.0 – async runtime for Rust

#330
post #141

Earlier quoted context omitted.

How big is it?

Here are the compiled-in dependencies: $ cargo tree -e no-dev,no-build --no-dedupe -p tokio tokio v1.0.0 ├── bytes v1.0.0 ├── libc v0.2.81 ├── memchr v2.3.4 ├── mio v0.7.6 │ ├── libc v0.2.81 │ └── log v0.4.11 │ └── cfg-if v0.1.10 ├── num_cpus v1.13.0 │ └── libc v0.2.81 ├── once_cell v1.5.2 ├── parking_lot v0.11.1 │ ├── instant v0.1.9 │ │ └── cfg-if v1.0.0 │ ├── lock_api v0.4.2 │ │ └── scopeguard v1.1.0 │ └── parking_…

Most of that is parking_lot, which is an optional feature not even included in the full feature.
Post reply on HN