Hacker News new | ask | show | jobs
by cshokie 1039 days ago
This article hits on a number of interesting points. There is a lot of complexity to be aware of when using C++ coroutines. And a number of “normal” practices become dangerous in them, such as pass by reference.

That said, I think they are still very much worth it. Older asynchronous programming libraries in C++ are so verbose and so much worse than coroutines that it’s an obvious choice to use coroutines.

Also, there is another hazard that the author does not mention in this article: RAII lock wrappers. Holding a lock across suspension points is super dangerous. At best it wastes performance to leave it locked when blocked. At worst it can create deadlocks or corrupt the lock if it is released on a different thread than it was acquired.

4 comments

>RAII lock wrappers

You don't even need coroutines for this to be dangerous. Holding locks over callback invocations is a pet peeve of mine in PR reviews. Callback invocations, like suspension points, can inject arbitrary operations into our code, which can easily break prior invariants, yet look innocuous for the casual reader.

I often just add a task to a runtime queue which gets called once the stack fully unwinds to avoid these sorts of issues. You may not be aware of what locks were acquired prior to your current function being called. Reentrant safe code is considerably more challenging. This might have some overhead as callback parameters have to be placed in the heap, but it's usually worthwhile.
As someone who writes in C++ and uses coroutines everyday for work, I find for our use case this is actually helpful.

We use seastar.io a thread per core framework and locks are "async" friendly in that they yield for access instead of blocking. Also embracing fully async message passing between threads simplifies the programming model a ton.

> it’s an obvious choice to use coroutines.

I agree, but the other choice is to have traditional threads of execution that block. This simple strategy has delivered more successful projects than any other.

I don't understand how it is any more verbose.
Because you have to keep explicitly passing state between each callback, rather than just using the same context (which still has the ability to delete things if needed).
State capture with lambdas is implicit, and only explicit if you want it to be.