Live data from Hacker News

Looking at Unity made me understand the point of C++ coroutines

mropert.github.io

151–160 of 190 posts

Re: Looking at Unity made me understand the point of C++ coroutines

#151

Earlier quoted context omitted.

async is simply a difficult problem, and I think we'll find irreducible complexity there. Sometimes you are just doing 2 or 3 things at once and you need a hand-written state machine with good unit tests around it. Sometimes you can't just glue 3 happy paths together into CSP and call it a day.

Using structured concurrency [1] as introduced in Python Trio [2] genuinely does help write much simpler concurrent code. Also, as noted in that Simon Tatham article, Python makes choices at the language level that you have to fuss over yourself in C++. Given how different Trio is from asyncio (the async library in Python's standard library), it seems to me that making some of those basic choices wasn't actually that…

After so wrote the comment below I realized that it really is just ‘um, actually…’ about discussing using concurrency vs implementing it. It’s probably not needed, but I do like my wording so I’m posting it for personal posterity.

In the context of an article about C++’s coroutines for building concurrency I think structured concurrency is out of scope. Structured concurrency is an effective and, reasonably, efficient idiom for handling a substantial percentage of concurrent workloads (which in light of your parent’s comment is probably why you brought up structured concurrency as a solution); however, C++ coroutines are pitched several levels of abstraction below where structured concurrency is implemented.

Additionally, there is the implementation requirements to have Trio style structured concurrency function. I’m almost certain a garbage collector is not required so that probably isn’t an issue, but, the implementation of the nurseries and the associated memory management required are independent implementations that C++ will almost certainly never impose as a base requirement to have concurrency. There are also some pretty effective cancelation strategies presumed in Trio which would also have to be positioned as requirements.

Not really a critique on the idiom, but I think it’s worth mentioning that a higher level solution is not always applicable given a lower level language feature’s expected usage. Particularly where implementing concurrency, as in the C++ coroutines, versus using concurrency, as in Trio.

Re: Looking at Unity made me understand the point of C++ coroutines

#152
post #95

I do not find so called "green threads" useful at all. In my opinion except some very esoteric cases they serve no purpose in "native" languages that have full access to all OS threading and IO facilities. Useful only in "deficient" environments like inherently single threaded request handlers like NodeJS.

Yeah I agree that user threads are overused by programmers. For most situations, using an OS thread is going to be far easier to work with. People like to cite the drawback that context switching overhead becomes a problem when you have thousands of threads, but the reality is that most people are not writing software that needs to handle many thousands of users all at once. Using green threads to handle such large scale instead of OS threads is a prime example of YAGNI.

Re: Looking at Unity made me understand the point of C++ coroutines

#153

Earlier quoted context omitted.

I'll take the bait. Here's a coroutine waitFrames(5); // wait 5 frames fireProjectile(); waitFrames(15); turnLeft(-30/*deg*/, 120); // turn left over 120 frames waitFrames(10); fireProjectile(); // spin and shoot for (i of range(0, 360, 60)) { turnRight(60, 90); // turn 60 degrees over 90 frames fireProjectile(); } 10 lines and I get behavior over time. What would your non-coroutine solution look like?

Given a coroutine body ``` int f() { a; co_yield r; b; co_return r2; } ``` this transforms into ``` auto f(auto then) { a; return then(r, [&]() { b; return then(r2); }); }; ``` You can easily extend this to arbitrarily complex statements. The main thing is that obviously, you have to worry about the capture lifetime yourself (coroutines allocate a frame separate from the stack), and the syntax causes nesting for ever…

Isn't this basically what javascript went through with Promise chaining "callback hell" that was cleaned up with async/await (and esbuild can still desugar the latter down to the former)

Re: Looking at Unity made me understand the point of C++ coroutines

#154

Earlier quoted context omitted.

async is simply a difficult problem, and I think we'll find irreducible complexity there. Sometimes you are just doing 2 or 3 things at once and you need a hand-written state machine with good unit tests around it. Sometimes you can't just glue 3 happy paths together into CSP and call it a day.

It's quite simple in Golang.

Golang has a GC and that makes a lot of things easier.

Re: Looking at Unity made me understand the point of C++ coroutines

#155

Earlier quoted context omitted.

Unfortunately swap context requires saving and restoring the signal mask, which, at least on Linux, requires a syscall so it is going to be at least a hundred times slower than an hand rolled implementation. Also, although not likely to be removed anytime soon from existing systems, POSIX has declared the context API obsolescent a while ago (it might actually no longer be part of the standard).

Stackful coroutines also can't be used to "send" a coroutine to a worker thread, because the compiler might save the address of a thread local variable across the thread switch (happened in QEMU).

Yes I know, GCC has a long standing bug open on the issue :(.

Re: Looking at Unity made me understand the point of C++ coroutines

#156

> turns it into some sort of ugly state machine Why are people afraid of state machines? There's been sooo much effort spent on hiding them from the programmer...

They're essentially callable, stateful, structured gotos. Difficult to understand for the uninitiated. For example, generators. Also known as semicoroutines. https://langdev.stackexchange.com/a/834 This: generator fib() { a, b = 1, 2 while (a Becomes this: struct fibState { a, b, position } int fib(fibState state) { switch (fibState.postion) { case 0: fibState.a, fibState.b = 1,2 while (a The ugly state machine examp…

What you've given is an example of how to implement a coroutine though.

Not of how to write a state machine based application without hiding the state machine behind abstractions.

Re: Looking at Unity made me understand the point of C++ coroutines

#157
post #122

Earlier quoted context omitted.

C++ standards follow a tick-tock schedule for complex features. For the `tick`, the core language gets an un-opinionated iteration of the feature that is meant for compiler developers and library writers to play with. (This is why we sometimes see production compilers lagging behind in features). For the `tock`, we try to get the standard library improved with these features to a realistic extent, and also fix wrinkl…

Regarding your mention of compiler magic and Swift, I don’t know much about the language, but I have read a handful of discussions/blogs about the compiler and the techniques used for its implementation. One of the purported benefits/points of pride for Swift that stood out to me and I still remember was something to the effect of Swift being fundamentally against features/abstractions/‘things’ being built in. In par…

> literal types (ints, sized ints, bools, etc) “built in” to the compiler but were defined in the language.

This is actually a good example by itself.

Int is defined in swift with Builtin.int64 IIRC. That is not part of the swift language.

Re: Looking at Unity made me understand the point of C++ coroutines

#158
post #9

Simon Tatham, author of Putty, has quite a detailed blog post [0] on using the C++20's coroutine system. And yep, it's a lot to do on your own, C++26 really ought to give us some pre-built templates/patterns/scaffolds. [0] https://web.archive.org/web/20260105235513/https://www.chiar...

People love to complain about Rust async-await being too complicated, but somehow C++ manages to be even worse. C++ never disappoints!

Not really, because due to C++'s unsafe first approach, means that workarounds like Pin aren't required.

Additionally, for those with .NET background, C++ co-routines are pretty much inspired by how they work in .NET/C#, naturally with the added hurdle there isn't a GC, and there is some memory management to take into account.

Also so even if it takes some time across ISO working processes, there is still a goal to have some capabilities on the standard library, that in Rust's case means "use tokio" instead.

Re: Looking at Unity made me understand the point of C++ coroutines

#159

Earlier quoted context omitted.

If you need to implement an async state machine, couldn't that just as easily be done with std::future? How do coroutines make this cleaner/better?

std::future doesn't give you a state machine. You get the building blocks you have to assemble into one manually. Coroutines give you the same building blocks but let the compiler do the assembly, making the suspension points visible in the source while hiding the mechanical boilerplate. This is why coroutine-based frameworks (e.g., C++20 coroutines with cppcoro) have largely superseded future-chaining for async stat…

[dead]

Re: Looking at Unity made me understand the point of C++ coroutines

#160
post #142

Earlier quoted context omitted.

Enjoy shipping console titles that run at a constant 60 fps with no GC. Again, fine for pet projects on PC :)

I'll continue to bite... What AAA 60+fps mobile game written Unity without coroutines are you referring to?

There's exactly 0 (zero) AAA games made with unity so it's going to be tough. They're all a terrible lag fest no matter how they're implemented
Post reply on HN