frontpage.
newsnewestaskshowjobs

Open Source @Github

fp.

Open in hackernews

The Tokio/Rayon Trap and Why Async/Await Fails Concurrency

https://pmbanugo.me/blog/why-async-await-complect-concurrency
23•LAC-Tech•2h ago

Comments

EGreg•1h ago
In a cooperative runtime like Rust’s Tokio or Node.js, the thread does not yield until it hits an await point.

This is just because JS is single threaded. Python has the Global Interpreter Lock, which makes it effectively single threaded too. That means you don’t have to deal with true parallelism, and critical sections, semaphores etc. It’s like Ethereum: only one thing happens at a time.

But you don’t have to parse JSON without yielding. You can make anything async by just using setTimeout once in a while. Here is one such implementation: https://www.npmjs.com/package/yieldable-json

The guy may as well have said while(1) locks up Node.

Now they get into multithreaded work-stealing, and isolates. But the solution in Node is to spin up multiple processes and pass messages between them. This is approaching the Erlang actor model, and is also shared-nothing. They even say "the schedule is a single-threaded loop per core" and "all cross-core communication occurs via the messaging subsystem".

This can also be achieved in most other single-threaded languages, too. Python with asyncio, for instance.

Tina may provide a nice opinionated implementation with bounded queues and deterministic scheduling, but those are architectural choices rather than evidence that async/await itself has failed.

Node has isolates, but it's more for sandboxing.

theamk•1h ago
The title of the post is "Tokio/Rayon Trap" - those are two very well known Rust libraries.

(in case you missed it, authors mention them later and explain what they do: "Use Tokio for I/O, and send CPU-bound work to a dedicated thread pool like Rayon.")

Authors have whole section ("The Work-Stealing Myth") on Erlang.

Author's proposed solution ("Project Tina") is a new programming language written in Odin.

How on earth do you read this all and start talking about Javascript problems instead?

what•49m ago
There was a single mention of nodejs (and also python). But I doubt this person read the article.
kev009•1h ago
This is silly or just AI slop post? Because using a Go quote as an example of doing something right in the arena is laughable at best where it has the same problems, more magic, and worse observability.

The punchline seems to be something like the LMAX disruptor style which is genuinely good for some things, but if you have I/O loops like the illustration shows you can easily block that loop with some long running function.. so you have the same cognitive load as managing thread pools or async pools or disruptors..

minraws•48m ago
I can hate Rayon and Tokio as much as the next guy generally I can empathize with problems they cause. But largely either it's a skill issue.

Or you are just trying to squeeze lemonade from stones, ala, they aren't meant to do what you are doing.

Tokio especially is extremely widely used for all kinds of things it doesn't work well for.

Sure I could improve it add or tune some primitives but I am honestly considering writing my own. And so should others.

I feel like we are all too bound in Rust ecosystem suddenly to Tokio and Rayon because we don't want to blame and acknowledge the libraries just don't work for what we want to use it for.

And library authors don't consider these usecases and bug important enough to actually fix it in a ergonomic way.

jmyeet•47m ago
I think it's important to understand how we got here and a lot of it has to do with serving network requests or RPCs.

The first Web servers used CGI (Common Gateway Interface). This spawned an entire program (process) per request and had obvious overheads. This led to some optimizations (eg FastCGI, ISAPI/NSAPI) to reduce the overhead. This was the era of Perl scripts being popular.

Then came the model of having a persistent state across requests. Java servlets were a big example of this. Given the cost of exec'ing a process, this was a big deal. But then you've immediately created an environment where multiple threads were accessing the same resource and you could leak resources. There were other variants like CORBA.

Now this was abotu the time of the birth of PHP. PHP was revolutionary because it had a stateless core that allowed shared hosting environments, which were exceptionally cheap for the time (even though they had security issues). But the idea is that you avoided the threading issues of a persistent environment and didn't really have resource leaks because everything got torn down. Of course PHP had other issues. But this was a big deal because things like the initialization of a JVM class loader, for example, was relatively expensive and you had to tune Java servers around performance and STW GC pauses.

None of the above really has anything to do with programming languages other than people learned (or didn't learn) just how hard writing multithreaded code is, something that is true to this day and you absoultely want to avoid it if possible. It is incredibly difficult to get right in an era of cores, threads, different L1/L2 caches, out-of-order proccessing, branch prediction, etc etc etc. And your code may have to run on multiple architectures.

Now Go chose to get around this issue with goroutines and channels. I personally think these are a bad abstraction, particularly because buffered channels are used without understanding the impact (leading to deadlocks), you can have exploding goroutine sttacks and using unbuffered channels is a strictly inferior (IMHO) async/await abstraction.

I actually think that Facebook's Hack has basically the almost perfect async/await. The whole idea of async/await is that you get the benefits of the PHP model of being single-threaded in your own application code and you can tear down your environment when you're done. Any IO goes through async API functions.

Now how does the scheduler manage threads, exhaustion, etc in this environment? Honestly? I have no idea. It just basically works. So maybe the Tokio issue is that the scheduler itself is blocked, which seems to be the case from this article. That does seem like a flaw but a fixable one.

I get the whole colored function criticism but the reality of using Hack to serve HTTP requests is that everything is async anyway so it seems to be a non-issue in practice. You can if you really need to call call an async function from a non-async function with a blocking function but that's not best practice.

I do know that thread pools, particularly multiple thread pools, is not the answer.

Animats•45m ago
There are some fundamental assumptions in the Rust async system:

- The program is mostly I/O bound.

- All tasks have equal priority.

If your program isn't like that, the Tokio model is a bad match to the problem.

Real time control is not like that. MMO and metaverse game programs are not like that. Most web stuff is, but that's a special case. A big special case, but a special case.

RealityVoid•41m ago
Real time control can be like that as long as the computing parts are clearly bound within your performance requirement.
LAC-Tech•37m ago
There's a few runtimes for IO bound work loads; monoio from bytedance comes to mind.
oersted•33m ago
To be fair, the Rust async model itself was intentionally designed not to be prescriptive in the way you describe. You can build, and there exists, different task executors that can handle things like priority and many other execution models.

Async is just a way to describe a tree of concurrent tasks that may depend on (wait on) each other at certain points. It is mostly declarative.

Tokio has taken over as the default choice, but there's a reason why it's not part of the standard library, it is not meant to be the only choice.

VorpalWay•7m ago
Outside of Embassy in embedded, tokio is the only realistic choice though, because it is likely that any third party async crate has a dependency ok it already. Yes, smol, monio, glommio etc exist, but they are marginalised (and as far as I can tell they don't really help that much with mixed IO / compute workloads).

In fact, async/await in Rust falls apart with a mixed IO / compute workload since scheduling is cooperative. As soon as you want preemption (most of the time for what I do), it is not the right choice.

Seeing how embassy (embedded async rust) handles preemption reinforces this: it uses a separate scheduler per preemption level. This works fine, but is a bit clunky. Basically you are at this point just using async to help write a state machine per preemptive thread, which can be useful for some code patterns (in particular those common in embedded, where you are often waiting for IO). But to talk between threads you are back to channels, mutexes etc.

LAC-Tech•42m ago
I've been thinking similar thoughts recently, in that I am not sure that a function call is the best way to model asynchronicity. Io-uring in particular feels much more suited to a req/res type model, which has the benefit that you can make a single threaded state machine the core of your app - very pleasant to test and reason about.

I'd draw the analogy to RPC; it's a leaky abstraction because HTTP is fundamentally a different thing than a function call. I'd argue that event loops are a different thing as well.

oersted•23m ago
I agree that a request-response model can be quite ergonomic for concurrency. I've used this pattern in quite a few Rust projects in a somewhat ad-hoc manner.

It's a bit like having a system composed of microservices (nanoservices?) that communicate via function calls.

It sounds a lot like the actor model, but I always found the classic architecture too limiting: requiring every actor to be a single-threaded message processor, instead of being able to handle requests concurrently. It's not too different from classic object-oriented design either, with singleton services.

In some project I've called my concurrent services Gods just to have a bit of fun with it :)

dkh•40m ago
“human in the loop scheduler” is very funny
jolux•25m ago
I am not really sure how much yet another post complaining about async/await that ends with “thread-per-core is the way to go” adds to this discussion. Granted I’m both an Erlang programmer and a big fan of Tokio and Rust’s async/await implementation in general and I think this post and many others like it betray a fundamental misunderstanding of these technologies so I am probably biased.
haberman•8m ago
I see two solid points here:

1. It's not reasonable to expect the application layer to carefully partition its work into "I/O heavy" and "CPU heavy" parts.

2. It's not reasonable to queue up an arbitrary amount of work without back-pressure.

I haven't used Tokio much, but if it falls prey to these pitfalls, it would make me pause before adopting it.

I think there are probably ways of using Rust async that don't fall prey to these. Maybe not so much with network servers (I haven't written that many of those), but models where you are evaluating a graph and have more control over how new work is added to the system.

didibus•4m ago
The suggested alternative is interesting, I was expecting a Rust library though, but it's an Odin one. What's missing are any sort of benchmarks or proof of the claims that it will scale beyond the issues the article discuss.
WatchDog•7m ago
Your history is all valid, but I don't think it really hits on the main motivations for how we got here.

Thread per request works perfectly fine if your application is CPU constrained.

However the observation was made, that most web applications are IO constrained, the majority of the time spent serving a web request is spent waiting for a database or downstream API.

Since most of the threads are idle waiting, your application needs many threads to optimally utilize the servers resources.

There was a perception(valid or not) that OS threads have too much memory and scheduling overhead.

Nginx came out using async io, and it could handle much more concurrent requests than apache, which used a threaded model, it sparked a lot of interest in different kinds of application managed scheduling.

It inspired initiatives like the reactive manifesto[0], which spawned tools like RXJava.

[0]: https://reactivemanifesto.org/

lioeters•3m ago
That's an informative overview. Somewhere in the story was Node.js and libuv, the callback style, promises, and the popularization of the async/await paradigm. Not sure if there was a direct influence on Rust's async libraries, but I imagine it influenced how people generally think about what an intuitive async syntax might look like.

Guerrilla London Bus Ads Mock Kylie Jenner's Meta Glasses Campaign

https://hyperallergic.com/guerrilla-london-bus-ads-mock-kylie-jenners-meta-glasses-campaign/
73•decimalenough•1h ago•37 comments

If you want to create a button from scratch, you must first create the universe

https://madcampos.dev/blog/2026/07/accessibility-from-scratch/
21•treve•1h ago•0 comments

Inkling: Our Open-Weights Model

https://thinkingmachines.ai/news/introducing-inkling/
815•vimarsh6739•10h ago•211 comments

SQLite should have (Rust-style) editions

https://mort.coffee/home/sqlite-editions/
168•gnyeki•6h ago•69 comments

Making 768 servers look like 1

https://planetscale.com/blog/making-768-servers-look-like-1
13•hisamafahri•1h ago•2 comments

Grok Build is open source

https://github.com/xai-org/grok-build
338•skp1995•8h ago•370 comments

G# – A modern .NET language with Go, Kotlin, and Swift ergonomics

https://davidobando.github.io/gsharp/
62•serial_dev•4d ago•23 comments

High-Bandwidth Flash offers efficient storage for model weights

https://spectrum.ieee.org/high-bandwidth-flash
19•Gaishan•1d ago•6 comments

Bluesky Trademarks ATProto

https://atproto.com/blog/at-protocol-trademark
43•chaosharmonic•3h ago•16 comments

Governments, companies, nonprofits should invest in free, open source AI [pdf]

https://www.siegelendowment.org/wp-content/uploads/2026/07/fortune-david-siegel-open-source-ai.pdf
126•bilsbie•7h ago•50 comments

Stripe and Advent have made a joint offer to acquire PayPal – sources

https://www.reuters.com/business/finance/stripe-advent-offer-buy-paypal-more-than-53-billion-sour...
400•rvz•1d ago•220 comments

The Tokio/Rayon Trap and Why Async/Await Fails Concurrency

https://pmbanugo.me/blog/why-async-await-complect-concurrency
23•LAC-Tech•2h ago•21 comments

CatchCat – Pokémon Go for Cats, IRL

https://www.catchcat.lol/
29•marojejian•5d ago•6 comments

My Throw Decides My Aim

https://thegustafson.com/blog/my-throw-decides-my-aim
7•usernotfoundrn•58m ago•0 comments

LLM Networking with MikroTik

https://blog.greg.technology/2026/07/14/llm-networking-with-mikrotik.html
67•gregsadetsky•6h ago•27 comments

Metal-Organic Frameworks, Chemistry's New Miracle Materials (2018)

https://chemistry.berkeley.edu/news/meet-metal-organic-frameworks-chemistry%E2%80%99s-new-miracle...
47•andsoitis•5h ago•10 comments

1,300 Beautiful Wildlife Illustrations from the 19th Century Now Restored

https://www.openculture.com/2026/07/explore-1300-beautiful-wildlife-illustrations-from-the-19th-c...
6•gslin•1h ago•0 comments

Running Gemma 4 26B at 5 tokens/sec on a 13-year-old Xeon with no GPU

https://www.neomindlabs.com/2026/06/08/running-gemma-4-26b-at-5-tokens-sec-on-a-13-year-old-xeon-...
257•neomindryan•13h ago•169 comments

Job queues are deceptively tricky

https://typesanitizer.com/blog/job-queues.html
52•ingve•1d ago•11 comments

Command Line Interface Guidelines

https://clig.dev/
95•subset•3d ago•19 comments

The Last Picture Show: A Conversation with George Lucas

https://a-rabbitsfoot.com/editorial/confessions/the-last-picture-show-a-conversation-with-george-...
4•Michelangelo11•1d ago•2 comments

Open Source, Free Tier Capable Whispr Using Cloudflare AI

https://github.com/PrestigePvP/Voicebox
6•TreDub•1h ago•2 comments

Show HN: One More Letter

https://playonemoreletter.com/
58•hmate9•5h ago•36 comments

Duskers, the scary command line game, is getting a sequel

https://elbowgreasegames.substack.com/p/misfits-attic-announces-duskers-20
107•spacemarine1•9h ago•31 comments

Nul Characters in Strings in SQLite

https://sqlite.org/nulinstr.html
41•basilikum•5h ago•14 comments

Brainless: Shadcn components that look like Claude Code, Codex and Grok

https://brainless.swerdlow.dev
115•benswerd•9h ago•23 comments

Voxatron

https://www.lexaloffle.com/voxatron.php
78•lsferreira42•9h ago•19 comments

Artie (YC S23) Is Hiring Software Engineers

https://jobs.ashbyhq.com/artie
1•tang8330•11h ago

Collection of Digital Clock Designs

https://clocks.dev
208•levmiseri•12h ago•37 comments

Mysteries of Telegram Data Centers (2022)

https://dev.moe/en/3025
249•theanonymousone•15h ago•136 comments