Rainer Deyke wrote:
Second off, my texture-loading coroutine does three things: - Read an image file. - Decode the image file. - Convert upload the image as a texture to OpenGL.
None of these operation, taken individually, require a coroutine.
I'll grant that the first step, the i/o layer, does use coroutines internally, but the hard work in done by SDL, wrapped in a custom awaitable. Wrapping the inner awaitable in a coroutine was a choice I made. I could have chosen to wrap the outer functionality in a custom awaitable directly. A coroutine was used, but not required.
The second step is a CPU-bound synchronous function that needs to run in a thread pool.
The third step is a GPU-upload-speed-bound synchronous function that needs to run in the main thread.
Right now I have one short, readable coroutine that co_awaits one awaitable, calls two functions. Between the two function is a very visible and very explicit tmc::resume_on to jump between executors. It's four lines if you don't count error handling. It's already as simple as possible, and its trivial to reason about.
Breaking this up into multiple coroutines makes it neither simpler nor easier to reason about. It just adds additional noise.
That's a separate question, independent of whether coroutines always resume on their original executor. You're talking about this: // on I/O executor co_await read_image_file(); // still on I/O executor co_await resume_on( cpu_ex ); // on CPU executor co_await decode_image_file(); // still on CPU executor co_await resume_on( main_thread_ex ); // on main thread co_await upload_image_to_opengl(); // still on main thread This is perfectly compatible with the Capy requirement that co_await should not switch executors; the explicit resume_on operation is obviously exempt. It's a legitimate design, just one that the Capy authors have decided against, in favor of the explicit (and equally legitimate) run_on( cpu_ex )( decode_image_file() );