C++1z coroutine threading context and coroutine scheduling C++1z coroutine threading context and coroutine scheduling multithreading multithreading

C++1z coroutine threading context and coroutine scheduling


The opposite!

C++ coroutine is all about control. the key point here is the
void await_suspend(std::experimental::coroutine_handle<> handle) function.

evey co_await expects awaitable type. in a nutshell, awaitable type is a type which provide these three functions:

  1. bool await_ready() - should the program halt the execution of the coroutine?
  2. void await_suspend(handle) - the program passes you a continuation context for that coroutine frame. if you activate the handle (for example, by calling operator () that the handle provides - the current thread resumes the coroutine immediately).
  3. T await_resume() - tells the thread which resumes the coroutine what to do when resuming the coroutine and what to return from co_await.

so when you call co_await on awaitable type, the program asks the awaitable if the coroutine should be suspended (if await_ready returns false) and if so - you get a coroutine handle in which you can do whatever you like.

for example, you can pass the coroutine handle to a thread-pool. in this case a thread-pool thread will resume the coroutine.

you can pass the coroutine handle to a simple std::thread - your own create thread will resume the coroutine.

you can attach the coroutine handle into a derived class of OVERLAPPED and resume the coroutine when the asynchronous IO finishes.

as you can see - you can control where and when the coroutine is suspended and resumes - by managing the coroutine handle passed in await_suspend. there is no "default scheduler" - how you implement you awaitable type will decide how the coroutine is schedueled.

So, what happens in VC++? unfortunately, std::future still doesn't have then function, so you can't pass the coroutine handle to a std::future. if you await on std::future - the program will just open a new thread. look at the source code given by the future header:

template<class _Ty>    void await_suspend(future<_Ty>& _Fut,        experimental::coroutine_handle<> _ResumeCb)    {   // change to .then when future gets .then    thread _WaitingThread([&_Fut, _ResumeCb]{        _Fut.wait();        _ResumeCb();    });    _WaitingThread.detach();    } 

So why did you see a win32 threadpool-thread if the coroutines are launched in a regular std::thread? that's because it wasn't the coroutine. std::async calls behind the scenes to concurrency::create_task. a concurrency::task is launched under the win32 threadpool by default. after all, the whole purpose of std::async is to launch the callable in another thread.