Hacker News new | ask | show | jobs
by captainmuon 1040 days ago
A lot of these seem to be downsides of manual memory managment in C++, not stackless coroutines. The same kind of coroutines work fine in Python, Javascript and C#.

Some parts like the one about lazy coroutines seem to only be an issue because coroutines in C++ can theoretically resume on any thread. If they were restricted to the current thread by default (like in Python+Twisted I think?) then you would still be able to use them for many use cases but with less cognitive overhead.

The author seems to prefer stackful coroutines aka green threads, which are essentially just user mode threads. Handoff is implicit deep inside 'blocking' functions. I rarely see the downsides of them discussed, but they have their own problems: They are still threads so you often need locks. You don't know where a function will hand off, and you could accidentially call a really blocking function or run a long computation and ruin responsiveness. And the type of the function no longer reflects if it is blocking or not (the famous colored functions).

2 comments

They don't work just fine in C#, there is a reason why one of ASP.NET architects has written a guide of best practices.

https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/b...

Ironically, C++'s design is heavily related to C#, as the initial proposal was done by Microsoft and shares many of the same ideas, including how to create runtime aware awaitable types.

I've never used ASP.NET, I mean they work fine in GUI apps where you click a button, start downloading a file, and then show a message when the file has been downloaded.

Making code nonblocking without threads and without callbacks = happy case for async. Writing multithreaded servers focussed on throughput is a whole other can of worms, which is basically my point.

Isn't this wrong (the "green threads are just threads" part)? The green thread / stack switching implementations I've seen so far all used cooperative multitasking, eg you know exactly where control is handed back to the scheduler and don't need synchronization between green threads - assuming the scheduler keeps all green threads on the same OS thread of course)

Blocking vs non-blocking can be solved with naming conventions, like Sync vs Async suffix (works well in node.js for instance)

(also getting rid of colored functions is a good thing!)

I don't know, I've always thought green threads refers to the coding style of Go or Java's Project Loom - you write code that looks like multithreading and call blocking methods like `socket.receive()`. And then deep down in each IO call, there is some magic that suspends the green thread, and resumes it when data is available.

I think the colored function thing is often thoroughly misunderstood. There is a real difference between a function that returns `string` vs. `Future<string>`. It's not arbitrary but just a matter of typing. Languages could have more syntactic sugar to bridge both worlds of course. And you can get rid of the distinction as goroutines etc. show.

But actually I wonder if it would be useful to keep some colors. Maybe you could have an effect system and mark functions as "computationally expensive" / "blocking" vs. "computationally trivial". The compiler would prevent you from calling the blocking functions from the GUI thread, but you could `async` or `go` them to another thread and resume when finished.

You have to be careful. Stackless coroutines will only schedule if you call co_await, but a stackful coroutine can be scheduled in the depths of any call.

This can trick 2 parts of your program into thinking they have exclusive access to the same thing at the same time. E.g. they could both grab the same thread local or both enter the critical section of a recursive mutex.