Live data from Hacker News

Tokio 1.0 – async runtime for Rust

tokio.rs

191–200 of 422 posts

Re: Tokio 1.0 – async runtime for Rust

#191
post #179

Earlier quoted context omitted.

Wait, what? What happened to "fearless concurrency"? I thought this was supposed to be one of the borrow checker's selling points! https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.h...

I mean, it is, yes. That post is talking about threads. And the “fearless” name meant that it solves a lot of issues at compile time, which it still does in an async context. Like any static analysis, it’s a give and take between making sure your analysis is sound, while still allowing useful programs.

Whether it's kernel threads or green threads, the same patterns (locks, etc) are possible. Locks are supposed to be the borrow checker's bread and butter, because it can guarantee they are held before accessing shared state. But now you're saying "the borrow checker makes writing code without [async/await] difficult, inefficient, and unergonomic."

I'm not saying locks are better than async/await (although they are[1]). You're saying the borrow checker itself can't handle them in real world use?

[1] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...

Re: Tokio 1.0 – async runtime for Rust

#192
post #146

Earlier quoted context omitted.

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…

> A non-async function is "regular logic", it must complete without blocking. Maybe one can enforce this convention in the particular project, but there's no ecosystem-wide consensus on this, and in fact I don't want this to be consensus. I write blocking non-async functions every day. Why am I wrong to do so?

There is consensus in some ecosystems. Javascript absolutely maintains that invariant. There are (almost) no blocking functions in the javascript / node standard libraries and we work hard to keep it that way. Go maintains similar discipline at the OS syscall level.

I feel like the "what color is your function" thing is incomplete. There are arguably 3 types of functions:

- Functions which do all their work synchronously and return without blocking

- Async functions which contain an internal state machine

- Functions which block on expensive IO or long computations

Mixing blocking functions and async functions in the same kernel thread leads to various performance disasters. Javascript is so meticulous about not having blocking IO in part because its basically impossible to tell from a function's signature whether it will block the thread. Lua has this problem - callback oriented lua feels like a natural fit for the language, but lots of 3rd party libraries are packed with blocking calls. Writing asyncronous lua feels like fighting a river. You have to constantly guard against calling blocking code, and most API docs won't tell you where they block.

Re: Tokio 1.0 – async runtime for Rust

#193
post #160

Earlier quoted context omitted.

Async/await matters in Rust specifically because the borrow checker makes writing code without it difficult, inefficient, and unergonomic: http://aturon.github.io/tech/2018/04/24/async-borrowing/ (note that some of the details have changed here, but the thrust of it is very much the same.) That said, if you can get your job done without this stuff, that's fine too, but the reasons it was pursued specifically involve…

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, threads and whatnot. I feel like I must be missing something important here.

Re: Tokio 1.0 – async runtime for Rust

#194

Earlier quoted context omitted.

The Rust language provides the async/await syntax, which can turn imperative code into Future objects, but to run those Future objects, you must repeatedly call their poll method. Tokio is the piece of code that calls that method. It does so in a manner such that futures are only polled if able to continue work, and not e.g. waiting for a timer. Besides that it provides lots of utilities for working with async code.

Would it be fair to compare this to Apple's Grand Central Dispatch a.k.a. libdispatch?

Yes in some regards. Both offer eventloops (in GCD: dispatch queues), which run small chunks of code which belongs to independent tasks on the same thread.

However there are some differences:

- Tokio is focussed on running async/await based code, whereas libdispatch currently mostly targets running callbacks/continuations. This might change once Swift offers async/await support, which for sure could run on top of GCD queues.

- GCD queues provide a lot more fine-grained control. users can exactly specify on which queue to run some code on. And tasks can jump between code. There might also be a dedicated main thread (UI) queue. Tokio just spins up a single queue which runs all code, which might be empowered by a multithreaded executor. This makes it less usable for UI.

Re: Tokio 1.0 – async runtime for Rust

#195
post #156

Earlier quoted context omitted.

This, merely, means that the web application code will hold back what Nginx is capable of serving.

If you know that there will be 10k concurrent users in your application, go ahead and use async right from the start. But for the rest of us, simple, blocking code will do just fine and save us a few headaches.

Alternatively use async everywhere from the start and your headaches go away too. It's mixing blocking code with async code that causes issues.

Re: Tokio 1.0 – async runtime for Rust

#196

Earlier quoted context omitted.

> A non-async function is "regular logic", it must complete without blocking. Maybe one can enforce this convention in the particular project, but there's no ecosystem-wide consensus on this, and in fact I don't want this to be consensus. I write blocking non-async functions every day. Why am I wrong to do so?

There is consensus in some ecosystems. Javascript absolutely maintains that invariant. There are (almost) no blocking functions in the javascript / node standard libraries and we work hard to keep it that way. Go maintains similar discipline at the OS syscall level. I feel like the "what color is your function" thing is incomplete. There are arguably 3 types of functions: - Functions which do all their work synchrono…

We are talking about Rust. There is no consensus in Rust.

Re: Tokio 1.0 – async runtime for Rust

#197

Earlier quoted context omitted.

Shouldn't an HTTP request library make async available? HTTP is the textbook example for async.

It should, but it shouldn't be the default. Right now, reqwest is the default HTTP request library in Rust ecosystem, and async is mandatory for reqwest. This is a bad situation to be in.

There's ureq for sync/blocking HTTP requests.

Re: Tokio 1.0 – async runtime for Rust

#198
post #189

Earlier quoted context omitted.

rouille (my Rust web framework of choice) spawns threads each request, and it handles thousands of requests per second just fine. Computers are fast, and Rust doesn't slow down your computer. If you can't handle thousands of requests per second with thread per request, that's more about your software stack, not about threading. I guess it was different in the past when computers were slow. I can believee that.

And once starting and stopping threads adds to much delay to your request processing, there could be a thread pool that grows as needed and which will reuse threads that haven't been closed yet. This mechanism is implemented by Apache httpd, Tomcat and pretty much every classic application server.

> This mechanism is implemented by Apache httpd, Tomcat and pretty much every classic application server.

True, but there's a reason that Apache usage is declining at the rate that it is.

Re: Tokio 1.0 – async runtime for Rust

#199
post #184

Earlier quoted context omitted.

When learning Rust a few months ago, I built a small client library for a REST API using reqwest (which uses Tokio). I then started writing a web app using Tide ( https://github.com/http-rs/tide ). I eventually realized that it would be difficult to use the library I had built earlier since Tide uses the async-std runtime rather than Tokio. That was very disappointing. Is there any plan to make it easier to write "ru…

You can make libraries runtime agnostic, but it requires a bit of design to get right. We tried our best with Tiberius[0], so you just need to provide an object implementing the AsyncRead and AsyncWrite traits from the futures crate, such as the TcpStream from async-std or tokio (using their compat module). It is not perfect yet, and especially how tokio does not follow the rest of the ecosystem by implementing their…

Yup, building libraries on IO traits which then are implemented by the particular runtimes is a good way to have a runtime agnostic library. Ideally the IO traits are defined in the library itself, to make them not again dependent on another moving target. You can provide implementations of the "glue code" for particular runtimes in separate crates to ease integration for users.

Another way to be runtime agnostic is to start a particular runtime as part of your library, which is used internally. The public interface of your library can provide async functions which are agonstic to a particular runtime, since all actions will be deferred/forwarded to an internal runtime. That approach has a bit more overhead, but can ease usage.

Re: Tokio 1.0 – async runtime for Rust

#200
post #191

Earlier quoted context omitted.

I mean, it is, yes. That post is talking about threads. And the “fearless” name meant that it solves a lot of issues at compile time, which it still does in an async context. Like any static analysis, it’s a give and take between making sure your analysis is sound, while still allowing useful programs.

Whether it's kernel threads or green threads, the same patterns (locks, etc) are possible. Locks are supposed to be the borrow checker's bread and butter, because it can guarantee they are held before accessing shared state. But now you're saying "the borrow checker makes writing code without [async/await] difficult, inefficient, and unergonomic." I'm not saying locks are better than async/await (although they are[1]…

Yes. There are some difficult technical issues. In practice, borrow checker works less well on async code than threaded code.

This is partly why I prefer threading over async in Rust. Look, we went through some enormous effort to make threading good and fun again. Why wouldn't you use threading?

Post reply on HN