Live data from Hacker News

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

mropert.github.io

51–60 of 190 posts

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

#51

You can roll stackful coroutines in C++ (or C) with 50-ish lines of Assembly. It's a matter of saving a few registers and switching the stack pointer, minicoro [1] is a pretty good C library that does it. I like this model a lot more than C++20 coroutines: 1. C++20 coros are stackless, in the general case every async "function call" heap allocates. 2. If you do your own stackful coroutines, every function can suspend…

C++ destructors and exception safety will likely wreak havoc with any "simple" assembly/longjmp-based solution, unless severely constraining what types you can use within the coroutines.

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

#52

Coroutines is just a way to write continuations in an imperative style and with more overhead. I never understood the value. Just use lambdas/callbacks.

Did you read the article? As the author says, it becomes a state machine hell very quickly beyond very simple examples.

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

#53
post #50

Earlier quoted context omitted.

...and then crash when any object it was using gets deleted while it's still running, like when the game changes scenes, but it becomes a manual, error-prone process to track down and stop all the coroutines holding on to references, that costs much more effort than it saves. I've been a serious Unity developer for 16 years, and I avoid coroutines like the plague, just like other architectural mistakes like stringly…

> Who owns it? The MonoBehaviour? The caller? The thing it captured three yields ago? The monobehavior that invoked the routine owns it and is capable of cancelling it at typical lifecycle boundaries. This is not a hill I would die on. There's a lot of other battles to fight when shipping a game.

And then you're bending over backwards and have made so much more busy work for yourself than you would have if you'd just done it the normal way, in which all your state would be explicitly visible and auditable in the editor.

The biggest reason for using Unity is its editor. Don't do things that make the editor useless, and are invisible to it.

The problem with coroutines is that they generate invisible errors you end up shipping and fighting long after you shipped your game, because they're so hard to track down and reproduce and diagnose.

Sure you can push out fixes and updates on Steam, but how about shipping games that don't crash mysteriously and unpredictably in the first place?

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

#54
post #46

Earlier quoted context omitted.

> Just use lambdas/callbacks "Just" is doing a lot of work there. I've use callback-based async frameworks in C++ in the past, and it turns into pure hell very fast. Async programming is, basically, state machines all the way down, and doing it explicitly is not nice. And trying to debug the damn thing is a miserable experience

You can embed the state in your lambda context, it really isn't as difficult as what people claim. The author just chose to write it as a state machine, but you don't have to. Write it in whatever style helps you reach correctness.

You still need the state and the dispatcher, even if the former is a little more hidden in the implicit closure type.

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

#55
post #23
post #5

Always jarring to see how Unity is stuck on an ancient version of C#. The use of IEnumerable as a "generator" mechanic is quite a good hack though.

IIRC generators and co-routines are equivalent in a sense that you can implement one with the other.

Generators are a subset of coroutines that only yield data in one direction. Full coroutines can also receive more input from the caller at every yield point.

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

#56
post #14

Earlier quoted context omitted.

ASIO is also available outside of boost! https://github.com/chriskohlhoff/asio

For anyone wondering; this isn't a hack, that's the same library, just as good, just without boost dependencies.

Thanks for pointing this out! This may not obvious not everybody.

Also, this is not some random GitHub Repo, Chris Kohlhoff is the developer of ASIO :)

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

#57
post #41

Earlier quoted context omitted.

Thankfully they are actively working towards upgrading, Unity 6.8 (they're currently on 6.4) is supposed to move fully towards CoreCLR, and removing Mono. We'll then finally be able to move to C# 14 (from C# 9, which came out in 2020), as well as use newer .NET functionality. https://discussions.unity.com/t/coreclr-scripting-and-ecs-st...

One annoying piece of Unity's CoreCLR plan is there is no plan to upgrade IL2CPP (Unity's AOT compiler) to use a better garbage collector. It will continue to use Boehm GC, which is so much worse for games.

Why wouldn't they use the GC that comes with the dotnet AOT runtime?

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

#58
post #51

You can roll stackful coroutines in C++ (or C) with 50-ish lines of Assembly. It's a matter of saving a few registers and switching the stack pointer, minicoro [1] is a pretty good C library that does it. I like this model a lot more than C++20 coroutines: 1. C++20 coros are stackless, in the general case every async "function call" heap allocates. 2. If you do your own stackful coroutines, every function can suspend…

C++ destructors and exception safety will likely wreak havoc with any "simple" assembly/longjmp-based solution, unless severely constraining what types you can use within the coroutines.

Not really. I've done it years ago. The one restriction for code inside the coroutine is that it mustn't catch (...). You solve destruction by distinguishing whether a couroutine is paused in the middle of execution or if it finished running. When the coroutine is about to be destructed you run it one last time and throw a special exception, triggering destruction of all RAII objects, which you catch at the coroutine entry point.

Passing uncaught exceptions from the coroutine up to the caller is also pretty easy, because it's all synchronous. You just need to wrap it so it can safely travel across the gap. You can restrict the exception types however you want. I chose to support only subclasses of std::exception and handle anything else as an unknown exception.

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

#59
As I mentioned on the Reddit thread,

This is quite understandable when you know the history behind how C++ coroutines came to be.

They were initially proposed by Microsoft, based on a C++/CX extension, that was inspired by .NET async/await implementation, as the WinRT runtime was designed to only support asynchronous code.

Thus if one knows how the .NET compiler and runtime magic works, including custom awaitable types, there will be some common bridges to how C++ co-routines ended up looking like.

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

#60
post #5

Always jarring to see how Unity is stuck on an ancient version of C#. The use of IEnumerable as a "generator" mechanic is quite a good hack though.

Thankfully they are actively working towards upgrading, Unity 6.8 (they're currently on 6.4) is supposed to move fully towards CoreCLR, and removing Mono. We'll then finally be able to move to C# 14 (from C# 9, which came out in 2020), as well as use newer .NET functionality. https://discussions.unity.com/t/coreclr-scripting-and-ecs-st...

For several years now, I wonder if it will ever happen.
Post reply on HN