Skip to content

[dcl.fct.def.coroutine] p9 Whether a coroutine is suspended before a resumption should be in terms of happens-before #870

@xmh0511

Description

@xmh0511

Full name of submitter (unless configured in github; will be published with the issue): Jim X

[dcl.fct.def.coroutine] p9 says:

Invoking a resumption member function for a coroutine that is not suspended results in undefined behavior.

However, in a concurrent context, whether a coroutine is suspended before a resumption is hard to determine according to the current wording. Consider this example:

#include <coroutine>
#include <exception>
#include <iostream>
#include <thread>

struct Task {
    struct promise_type {
        Task get_return_object() {
            return {std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
    };

    std::coroutine_handle<promise_type> handle;

    Task(std::coroutine_handle<promise_type> h) : handle(h) {}
};

struct SuspendOnce {
    bool await_ready() { return false; }
    void await_suspend(std::coroutine_handle<> h) {}
    void await_resume() {}
};

Task my_coroutine() {
    co_await SuspendOnce{};
    co_await SuspendOnce{};
}

int main() {
    auto task = my_coroutine();
    auto& h = task.handle;
    auto t1 = std::thread([&]() {
        h.resume();  // #1
    });
    auto t2 = std::thread([&]() {
        h.resume();  // #2
    });
    t1.join();
    t2.join();
}

The signature of the member function resume implies the action is read-only, as per [coroutine.handle.resumption]

void resume() const;

So, the concurrent invocation of the member function does not constitute a data race because there are no conflicting actions. Similarly, there are also no conflicting actions in the body of the coroutine. This example doesn't itself have a data race.

However, it's hard to say whether the coroutine is suspended relative to the invocation of the resumption member function when there is another such concurrent invocation.

Suggested Resolution:

We may need to use happens-before to define whether a coroutine is suspended relative to a resumption(i.e., including resumption by returning false from await-suspend, see #865).

The suggested wording might be:

Resuming a coroutine shall happen after the coroutine is suspended and before any other resumption.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions