Live data from Hacker News

C++20: Building a Thread-Pool with Coroutines

blog.eiler.eu

11–20 of 63 posts

Re: C++20: Building a Thread-Pool with Coroutines

#11

Earlier quoted context omitted.

It still boggles the mind that they made it so hard to use this stuff in terms of boilerplate. Hopefully that will all be abstracted into a library that handles io, networking, multiprocessing and synchronization so most of us can just focus on writing the bits that do stuff. But I will never understand how the std maintainers could not manage to do this when every other language is doing async await support in their…

One reason could be that C++ tends to be used more in performance-critical contexts, and frequently the abstractions present in "every other language" can get in the way of performance.

also the memory management model in c++ is much more complex - most other languages just use garbage collection to deal with reference lifetimes

Re: C++20: Building a Thread-Pool with Coroutines

#12
post #8

C++20 coroutines confuse me. Like it's not clear to me what problem they solve. For the last few years I've been doing Hack (Facebook's PHP fork) professionally and async-await as cooperative multitasking is pervasive. IMHO it's a really nice model. Generally speaking, I've come around to believing that if it ever comes down to you spawning your own thread, you're going to have a Bad Time. Go's channels are another v…

They solve exactly the problem you describe; I'm not sure what you're missing. What are you thinking you can't do? I have a project where I've put C++ coroutines on top of libuv and I can do essentially anything I could do in JS/C#/Rust async/await with task/co_await/etc with the imperative style you'd expect.

You may have looked at them at too low a level. Check out something like cppcoro to see what you can do. I don't use it myself, but I've stolen a few things, like task, which is a pretty core thing that the stdlib does not provide.

Goroutines are not cooperative multitasking, by the way, they're non-OS/"green" threads. Until you do something silly like run CPU-bound code that doesn't hit any yield points and you have to put them in yourself (at least the last time I used Go, it's been awhile).

Re: C++20: Building a Thread-Pool with Coroutines

#13
post #4

Earlier quoted context omitted.

You're not the only one. Coroutines are complicated as hell and have too much boiler plate BUT once you handle it for a general enough case you get javascript-esque async await syntax which is very, very nice. Take for instance, this code which relies on libuv for its event loop and co_await to retain its state during its execution: https://gist.github.com/Qix-/09532acd0f6c9a57c09bd9ce31b3023... Lets say that you wan…

It still boggles the mind that they made it so hard to use this stuff in terms of boilerplate. Hopefully that will all be abstracted into a library that handles io, networking, multiprocessing and synchronization so most of us can just focus on writing the bits that do stuff. But I will never understand how the std maintainers could not manage to do this when every other language is doing async await support in their…

I think the reason it's like this is because the C++ maintainers are super focused on noticing whenever there is a choice to be made, and being careful not to make it for you. Given the option between foisting situationally bad performance on you and foisting lots of syntax and interface complexity on you, they always choose the latter. Which I suppose makes sense: C++ niche is that it's the Big Gun you pull out when there's serious work to be done. So they figure you're already going to be dealing with massive complexity anyway, and you're going to be prepared for it. May as well let give you _all_ the access rather than fumbling at the 1 yard line by making some opinionated choice that is a non-starter for somebody, somewhere.

Re: C++20: Building a Thread-Pool with Coroutines

#14
post #8

C++20 coroutines confuse me. Like it's not clear to me what problem they solve. For the last few years I've been doing Hack (Facebook's PHP fork) professionally and async-await as cooperative multitasking is pervasive. IMHO it's a really nice model. Generally speaking, I've come around to believing that if it ever comes down to you spawning your own thread, you're going to have a Bad Time. Go's channels are another v…

Main problems with async await model is that the callee decides whether something should run sync/async. In goroutines model, caller decides

Re: C++20: Building a Thread-Pool with Coroutines

#15
post #11

Earlier quoted context omitted.

One reason could be that C++ tends to be used more in performance-critical contexts, and frequently the abstractions present in "every other language" can get in the way of performance.

also the memory management model in c++ is much more complex - most other languages just use garbage collection to deal with reference lifetimes

garbage collection is one of the abstractions that can interfere with performance. it's out of the question for real time contexts, for example.

Re: C++20: Building a Thread-Pool with Coroutines

#16
post #8

C++20 coroutines confuse me. Like it's not clear to me what problem they solve. For the last few years I've been doing Hack (Facebook's PHP fork) professionally and async-await as cooperative multitasking is pervasive. IMHO it's a really nice model. Generally speaking, I've come around to believing that if it ever comes down to you spawning your own thread, you're going to have a Bad Time. Go's channels are another v…

They solve exactly the problem you describe; I'm not sure what you're missing. What are you thinking you can't do? I have a project where I've put C++ coroutines on top of libuv and I can do essentially anything I could do in JS/C#/Rust async/await with task/co_await/etc with the imperative style you'd expect. You may have looked at them at too low a level. Check out something like cppcoro to see what you can do. I d…

While the starvation behavior of cooperative green-threads isn’t ideal as native threads, the idea is that

1. Properly written code will perform well, whether async/await or Go style.

2. Making async easy makes one use it in more places. In additon having caller decide to run something sync or async also makes it way more useful. In Async/await model that can only work if all methods are declared async - very costly in complexity

Re: C++20: Building a Thread-Pool with Coroutines

#17

I'll admit I find coroutines difficult to grok. It seems to me that 'callback hell' is turning into 'coroutine hell'. The only plausible use-case I can see is enabling functionality similar to that of Python's `yield`. Does threadpool::thread_loop() not have to check if the popped coroutine is suspended before attempting to resume it? Are they really more efficient than normal callbacks when doing async?

For what it's worth Python's yield pretty much is a coroutine, as it has a (very rarely used) feature that allows information from the caller to be passed back to the generator.

The c++ implementation seems closer to lisps 'call with current continuation', though as far as I can tell all implementations achieve more or less the same thing (though thread safety might vary among the options).

Actually continuation passing style (callbacks) are another way of doing the same thing, though they have the disadvantage that they require large structural changes to the code. It wouldn't surprise me if the callback hell can therefore also occur in all versions, though some might make it easier than others (python's implementation in particular makes it somewhat less likely by encouraging information to flow one way)

Re: C++20: Building a Thread-Pool with Coroutines

#18
post #8

C++20 coroutines confuse me. Like it's not clear to me what problem they solve. For the last few years I've been doing Hack (Facebook's PHP fork) professionally and async-await as cooperative multitasking is pervasive. IMHO it's a really nice model. Generally speaking, I've come around to believing that if it ever comes down to you spawning your own thread, you're going to have a Bad Time. Go's channels are another v…

You're missing the big idea a bit. Coroutines in C++ can be used to implement generators or goroutines or async/await, etc. They are intended for library authors as a lower level construct. See for example: https://www.jeremyong.com/cpp/2021/01/04/cpp20-coroutines-a-...

Re: C++20: Building a Thread-Pool with Coroutines

#19

GO style co-routines and native JSON support would pretty much consign GO to history, IMO.

If things could be "consigned to history" by C++ adding more features, there'd hardly be any other languages left! It has all the features. That's its biggest problem.

Re: C++20: Building a Thread-Pool with Coroutines

#20
post #4

Earlier quoted context omitted.

You're not the only one. Coroutines are complicated as hell and have too much boiler plate BUT once you handle it for a general enough case you get javascript-esque async await syntax which is very, very nice. Take for instance, this code which relies on libuv for its event loop and co_await to retain its state during its execution: https://gist.github.com/Qix-/09532acd0f6c9a57c09bd9ce31b3023... Lets say that you wan…

It still boggles the mind that they made it so hard to use this stuff in terms of boilerplate. Hopefully that will all be abstracted into a library that handles io, networking, multiprocessing and synchronization so most of us can just focus on writing the bits that do stuff. But I will never understand how the std maintainers could not manage to do this when every other language is doing async await support in their…

Most of that boilerplate will be hidden in the libraries which use it. Unless you are a library author I don’t know why you would care about the boilerplate-like elements from the standard— you will only use co_yield and co_await.
Post reply on HN