Does C++20 coroutine have any specification on coroutine with std::mutex? Is it problematic to use std::mutex in a coroutine? If it is, is there any solution?
The following are 3 examples (I'm quite new to a coroutine, correct me if I'm wrong).
Example 1: coroutine with std::mutex and blocking operation
In a coroutine, we need to do a blocking operation with a lock, so that no multiple coroutines do the job at the same time.
task<> with_mutex_and_blocking_op() {
Result result;
{
std::lock_guard<std::mutex> lock(some_non_recursive_mutex); // ensure there's only one coroutine calls `call_blocking_io`
result = call_blocking_io(); // block here until IO finishes
}
co_await some_awaiter(result);
}
Example 2: coroutine with std::mutex
We can start an async operation in await_suspend, and get its result in await_resume. The async operation submits a task to a task queue, and the queue is protected by a std::mutex, or gets some lock-protected resources for the async operation.
void await_suspend(std::coroutine_handle<> handle) {
auto task = create_an_async_task();
std::lock_guard<std::mutex> lock(some_non_recursive_mutex);
task_queue.push(std::move(task));
}
Example 3: coroutine with std::mutex protecting a co_await
This is a similar problem with Example 1, except that the critical section calls co_await.
Some ideas
Example 1 seems like an anti-pattern, i.e. calling a blocking function in a coroutine, but I'm wondering what will happen? Say coroutine A acquires the mutex and calls call_blocking_io. Since it's blocking, I think the scheduler will switch to another coroutine, e.g. coroutine B. Unfortunately, B is scheduled on the same thread as coroutine A and tries to lock the mutex. This results in the same thread trying to lock the non-recursive mutex twice, and its undefined behavior.
Example 2 tries to lock the mutex in await_suspend. If it cannot acquire the lock, it will block. In this case, does the scheduler switch to another coroutine? If it will be switched, I think it has the same problem as Example 1.
Example 3 already has an answer and a solution. I'm wondering, 4 years passed if it's still the case? Does C++ 20 release some feature to solve it?