Live data from Hacker News

How to think about async/await in Rust

cliffle.com

211–220 of 268 posts

Re: How to think about async/await in Rust

#211

Not specific to rust, but I think asynchronous programming in general is a hype. It didn't start because it is so awesome, it started because JS can't do parallel any other way. That's the long and short of it. People wanted to use JS in the backend for some reason. The backend requires concurrency. JS cannot do concurrency. Enter the event loop. Then enter some syntactic sugar for the event loop. And since JS is pop…

The tradition of async programming goes back much further back than JS. Doing async I/O — usually referred to as event-driven programming — has been a popular technique in C and C++ for decades, with epoll(), kqueue, libevent/libev/libuv, Boost Asio, ASE, and so on. A lot of modern C software is built on async I/O, notably projects like Nginx, Memcached, Tor, Chrome, ntp, Redis, etc.

> has been a popular technique in C and C++ for decades, with epoll(), kqueue, libevent/libev/libuv, Boost Asio, ASE, and so on.

JavaScript is older than all of those. (Libuv in particular was harvested from Node, which was itself built on top of JavaScript.)

Obviously JS didn't invent callback-based async IO, but I think you're forgetting how old JS is and how relatively new that style of IO is.

Re: How to think about async/await in Rust

#212
post #14

Earlier quoted context omitted.

Asynchronous programming is a great fit for IO-driven programs, because modern IO is inherently asynchronous. This is clearly true for networking, but even for disk IO, generally commands are sent to the disks and results come back later. Another thing that’s asynchronous is user input, and that’s why JS has it. As for threading vs. explicit yielding (e.g. coroutines), I’d say it’s a matter of taste. I generally pref…

> Asynchronous programming is a great fit for IO-driven programs Yeah, but this could already be solved without "async/await compiler magic" in native code just with OS primitives, for instance with Windows-style event objects, it might look like this in pseudo-code: const event1 = read_async(...); const event2 = read_async(...); const event3 = read_async(...); wait_all(event1, event2, event3); This would run three I…

[deleted]

Re: How to think about async/await in Rust

#213
post #6

Earlier quoted context omitted.

There's a role for async, and it's when you're very I/O bound, you have one thread, and async means you don't need locking. This is simple to think about. That's the classic JavaScript model. If you have compute-bound components, things get more complicated. If you have threads, locks, and async, all in one program, things get much more complicated. I'm not sure that's a win. At some point, it's easier to use somethi…

> when you're very I/O bound, you have one thread Yes, but this is very unusual. In a web server, you have pool of threads that can respond to incoming connections. When one is blocking, another is ready to go. All the transition are handled transparently by the kernel.

In node.js, each process used to be single thread. There's now a hokey threading model with limited shared memory areas.[1] Annoyingly, this is also Android's threading model.

[1] https://nodejs.org/api/worker_threads.html

Re: How to think about async/await in Rust

#214
post #83
post #12

Earlier quoted context omitted.

To me, async is just "cooperative multitasking" with a quick paintjob It is, and not only to you. It is a way to save a call stack until a runloop calls it back. But what I can’t agree with is parallels with OS. Coop MT is only problematic in OS MT. When it’s your code there’s no unknown bad actor, and having multiple cooperative (mostly waiting) processes without scheduling them on a thread pool is a useful concept…

Async is great for dealing with I/O but it forgets that the CPU is a resource too.

Which is why you queue up CPU intensive tasks to threads where you can time slice, and IO intensive tasks to async/fibers whatever you wanna call them. It's hard to make this completely seamless. Power to Java for getting the closest.

Re: How to think about async/await in Rust

#216

Earlier quoted context omitted.

> Threads are a resource hog. Not really on any decent operating system, but if they are too heavy, there's still fibers aka green-threads aka stack-switching (which at least on Windows are an operating system primitive - but can be implemented in user code on any system that gives you direct access to the CPU stack and registers). I doubt that the async-await state machine code transformation which 'slices' sequenti…

The async/await model gives you exactly one guarantee: because the yield continuation is second class, at most one stack frame can be suspended, so the the amount of space that needs to be reserved for a task is bounded and potentially can be computed statically. This can be important for very high performance/very high concurrency programs, so I think the upsides can be more than the downsides in something like rust…

> I still do not understand why async was deemed appropriate, for example, in python.

Because queues backed by thread/process pools for serving web requests has sharp edges.

Re: How to think about async/await in Rust

#217

Earlier quoted context omitted.

The tradition of async programming goes back much further back than JS. Doing async I/O — usually referred to as event-driven programming — has been a popular technique in C and C++ for decades, with epoll(), kqueue, libevent/libev/libuv, Boost Asio, ASE, and so on. A lot of modern C software is built on async I/O, notably projects like Nginx, Memcached, Tor, Chrome, ntp, Redis, etc.

> has been a popular technique in C and C++ for decades, with epoll(), kqueue, libevent/libev/libuv, Boost Asio, ASE, and so on. JavaScript is older than all of those. (Libuv in particular was harvested from Node, which was itself built on top of JavaScript.) Obviously JS didn't invent callback-based async IO, but I think you're forgetting how old JS is and how relatively new that style of IO is.

This thread is about async/await, which was added to JavaScript in 2017. Before then, async programming was only done with Node.js (unless you consider windows.setTimeout() to be "event-driven programming"), which came out in 2009. Event-driven programming was an established paradigm years before then.

Re: How to think about async/await in Rust

#218
post #11

Earlier quoted context omitted.

One advantage of async/await is that its easier to cancel things. For example, this leads to the design pattern where you have multiple futures and you want to select the one that finishes first and cancel the rest. In regular threaded programming, cancellation is a bit more painful as you need to have some type of cancellation token used each time the thread waits for something. This a) is more verbose and b) can le…

>In regular threaded programming, cancellation is a bit more painful No, it isn't. Nothing is stopping your threading library from implementing the same thing. It just turns out it is a bad idea to kill threads at random points in time because they may own things like locks. Or in the case of async await doing something that is thought to be atomic.

You're describing exactly why it's painful for threads. If you only cancel co-routines at yield points (which unless you do dark magic is the only time you can cancel them) is always safe.

A co-routine that yields in the middle of an atomic operation is an oxymoron. Anything can happen before you're scheduled again.

Re: How to think about async/await in Rust

#219
post #165

Earlier quoted context omitted.

Yeah it blocks the thread, any other "user work" needs to happen on a different thread. But if you just need multiple non-blocking IO operations run in parallel it's as simple as it gets. (the operating system's thread scheduler is basically the equivalent to the JS "event loop").

Desktop operating systems all have application event loops that run within a single thread because the OS thread scheduler is not the same thing. If you just want an event loop, trying to use threads instead for everything will often end up in tears due to concurrent data access issues.

Use one or more service threads to do most work off the UI thread.

Re: How to think about async/await in Rust

#220

Earlier quoted context omitted.

The biggest benefit of async/await is imo. in GUI programming where you simply can't have blocking code a lot of the time, and moving everything between the GUI thread and the backend thread can be rather costly and annoying as well as buggy if people are not 100% aware which thread accesses what. I find it rather odd that you say "easier to reason about.". I find it much harder to keep a mental model in my head whic…

> in GUI programming where you simply can't have blocking code a lot of the time, This can be transparently solved by the GUI library. The main thread does a loop and polls events from a queue. Those events are generated by a gui on another thread. It can be designed so gui itself can be manipulated on the mainpulated on the main thread, and the event handling and rendering is double buffered, or synchronized for you…

What GUI system are you referring to? As far as I know, pretty much all major UI frameworks use a single thread, or at best a gui thread and a render thread.
Post reply on HN