Remix.run Logo
pclmulqdq 2 days ago

Without the "async" keyword, you can still write async code. It looks totally different because you have to control the state machine of task scheduling. Green threads are a step further than the async keyword because they have none of the function coloring stuff.

You may think of use of an async keyword as explicit async code but that is very much not the case.

If you want to see async code without the keyword, most of the code of Linux is asynchronous.

Dylan16807 2 days ago | parent | next [-]

Having to put "await" everywhere is very explicit. I'd even say it's equally explicit to a bunch of awkward closures. Why do you say it's less?

pclmulqdq 2 days ago | parent | next [-]

It's explicit that the code is async, but how the asynchrony happens is completely implicit with async/await, and is managed by a runtime of some kind.

Kernel-style async code, where everything is explicit:

* You write a poller that opens up queues and reads structs representing work

* Your functions are not tagged as "async" but they do not block

* When those functions finish, you explicitly put that struct in another queue based on the result

Async-await code, where the runtime is implicit:

* All async functions are marked and you await them if they might block

* A runtime of some sort handles queueing and runnability

Green threads, where all asynchrony is implicit:

* Functions are functions and can block

* A runtime wraps everything that can block to switch to other local work before yielding back to the kernel

lstodd 2 days ago | parent [-]

> Green threads, where all asynchrony is implicit:

which are no different from app POV from kernel threads, or any threads for that matter.

the whole async stuff came up because context switch per event is way more expensive than just shoveling down a page of file descriptor state.

thus poll, kqueue, epoll, io_uring, whatever.

think of it as batch processing

throw-qqqqq 2 days ago | parent | prev | next [-]

> Why do you say it's less

Let me try to clarify my point of view:

I don’t mean that async/await is more or less explicit than goroutines. I mean regular threaded code is more explicit than async/await code, and I prefer that.

I see colleagues struggle to correctly analyze resource usage for instance. Someone tries to parallelize some code (perhaps naiively) by converting it to async/await and then run out of memory.

Again, I don’t mean to judge anyone. I just observe that the async/await-flavor has more bugs in the code bases I work on.

curt15 2 days ago | parent [-]

>I don’t mean that async/await is more or less explicit than goroutines. I mean regular threaded code is more explicit than async/await code, and I prefer that.

More explicit in what sense? I've written both regular threaded Python and async/await Python. Only the latter shows me precisely where the context switches occur.

throwawayffffas 2 days ago | parent | prev [-]

Because it hides away the underlying machinery.

Everything is in a run loop that does not exist in my codebase.

The context switching points are obvious but the execution environment is opaque.

At least that's how it looks to me.

toast0 2 days ago | parent | next [-]

The problem isn't that it hides away the machinery. The problem is that it hides some things, but not everything. Certainly a lot of stuff hides behind await/async. But as a naive developer who is used to real threads and green threads, I expected there would be some wait to await on a real thread and all the async stuff would just happen... but instead, if you await, actually you've got to be async too. If you had to write your async code where you gave an eventloop a FD and a callback to run when it was ready, that would be more explicit, IMHO... but it would be so wordy that it would only get used under extreme duress... I've worked on those code bases and they can do amazing things, but if there's any complexity it quickly becomes not worth it.

Green threads are better (IMHO), because they actually do hide all the machinery. As a developer in a language with mature green threads (Erlang), I don't have to know about the machinery[1], I just write code that blocks from my perspective and BEAM makes magic happen. As I understand it, that's the model for Java's Project Loom aka Java Green Threads 2: 2 Green 2 Threads. The first release had some issues with the machinery, but I think I read the second release was much better, and I haven't seen much since... I'm not a Cafe Babe, so I don't follow Java that closely.

[1] It's always nice to know about the machinery, but I don't have to know about it, and I was able to get started pretty quick and figure out the machinery later.

worthless-trash 2 days ago | parent [-]

I don't know who you are, but thanks.. My beam code goes brrrr.. so fast, much async, so reliable, no worries.

ForHackernews 2 days ago | parent | prev [-]

I don't understand this criticism. The JVM is opaque, App Engine is opaque, Docker is opaque. All execution environments are opaque unless you've attached a debugger and are manually poking at the thing while it runs.

pclmulqdq 2 days ago | parent [-]

Some are more opaque than others.

lstodd 11 hours ago | parent [-]

If those persist in opaqueness, say "confess, or I fetch Ghidra".

If even this does not help, rm -rf is your friend.

vova_hn 2 days ago | parent | prev | next [-]

> Green threads are a step further than the async keyword because they have none of the function coloring stuff.

I would say that green threads still have "function coloring stuff", we just decided that every function will be async-colored.

Now, what happens if you try to cross an FFI-border and try to call a function that knows nothing about your green-thread runtime is an entirely different story...

throw-qqqqq 2 days ago | parent | prev [-]

This is exactly what I mean.

Thank you for explaining much more clearly than I could.

> none of the function coloring stuff

And it’s this part that I don’t like (and see colleagues struggling to implement correctly at work).