andreasbuhr / cppcoro

A library of C++ coroutine abstractions for the coroutines TS
MIT License
359 stars 53 forks source link

How was coroutine task rescheduled to the main thread, when resumed in another thread?? #28

Open anlongfei opened 3 years ago

anlongfei commented 3 years ago
class MyAwaiter {
    public:
        bool await_ready() {
            return false;
        }
        void await_suspend(std::experimental::coroutine_handle<> continuation) noexcept {
            std::thread([c = continuation]() mutable { 
                    c.resume();   // resume coroutine in a new thread.
                    std::cout << "new thread: " <<  std::this_thread::get_id() << std::endl;
             }).detach();
        }
        void await_resume() {}
};

TEST_CASE("task doesn't start until awaited")
{
        std::cout << "main thread " << std::this_thread::get_id() << std::endl;
    auto func = [&]() -> cppcoro::task<> {
        std::cout << "before: " << std::this_thread::get_id() << std::endl;
        co_await MyAwaiter();
        std::cout << "after: " << std::this_thread::get_id() << std::endl;
        co_return;
    };
    cppcoro::sync_wait([&]() -> cppcoro::task<>
    {
        auto t = func();
        co_await t;
    }());
}

output like this

main thread 140301651535680
before: 140301651535680
new thread: 140300609353472
after: 140301651535680

As far as I know, the couroutine task func would resumed in a detached thread, so after: 140301651535680 id would be the same as new thread: 140300609353472. but the output is unexpected, so what happened with c.resume(); )-:? Thx :-)

andreasbuhr commented 3 years ago

Interesting. What compiler are you using? Could you please try the following code with a little more output?

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

#include <cppcoro/task.hpp>
#include <cppcoro/sync_wait.hpp>

class MyAwaiter {
public:
    bool await_ready() {
        return false;
    }

    void await_suspend(cppcoro::coroutine_handle<> continuation) noexcept {
        std::thread([c = continuation]() mutable {
            std::cout << "new thread created: " << std::this_thread::get_id() << std::endl;
            c.resume();   // resume coroutine in a new thread.
            std::cout << "new thread after resume: " << std::this_thread::get_id() << std::endl;
        }).detach();
    }

    void await_resume() {}
};

int main() {
    std::cout << "main thread " << std::this_thread::get_id() << std::endl;
    auto func = [&]() -> cppcoro::task<> {
        std::cout << "coroutine before co_await: " << std::this_thread::get_id() << std::endl;
        co_await MyAwaiter();
        std::cout << "coroutine after co_await:" << std::this_thread::get_id() << std::endl;
        co_return;
    };
    std::cout << "main thread before sync_wait:" << std::this_thread::get_id() << std::endl;
    cppcoro::sync_wait([&]() -> cppcoro::task<> {
        std::cout << "top level coroutine start " << std::this_thread::get_id() << std::endl;
        auto t = func();
        std::cout << "top level coroutine before co_await " << std::this_thread::get_id() << std::endl;
        co_await t;
        std::cout << "top level coroutine after co_await " << std::this_thread::get_id() << std::endl;
    }());
    std::cout << "main thread after sync_wait:" << std::this_thread::get_id() << std::endl;
}

On my system I get, as expected:

main thread 139736076810048
main thread before sync_wait:139736076810048
top level coroutine start 139736076810048
top level coroutine before co_await 139736076810048
coroutine before co_await: 139736076810048
new thread created: 139736076805888
coroutine after co_await:139736076805888
top level coroutine after co_await 139736076805888
new thread after resume: 139736076805888
main thread after sync_wait:139736076810048
anlongfei commented 3 years ago

cppcoro lib with this patch:(top commit id is a87e97fe5b6091ca9f6de4637736b8e0d8b109cf)

diff --git a/test/task_tests.cpp b/test/task_tests.cpp
index 96b821a..656f201 100644
--- a/test/task_tests.cpp
+++ b/test/task_tests.cpp
@@ -15,10 +15,51 @@
 #include <string>
 #include <type_traits>

+#include <iostream>
+#include <thread>
 #include "doctest/doctest.h"

 TEST_SUITE_BEGIN("task");

+class MyAwaiter
+{
+public:
+       bool await_ready() { return false; }
+
+       void await_suspend(std::experimental::coroutine_handle<> continuation) noexcept
+       {
+               std::thread([c = continuation]() mutable {
+                       std::cout << "new thread created: " << std::this_thread::get_id() << std::endl;
+                       c.resume();  // resume coroutine in a new thread.
+                       std::cout << "new thread after resume: " << std::this_thread::get_id() << std::endl;
+               }).detach();
+       }
+
+       void await_resume() {}
+};
+
+TEST_CASE("test")
+{
+       std::cout << "main thread " << std::this_thread::get_id() << std::endl;
+       auto func = [&]() -> cppcoro::task<> {
+               std::cout << "coroutine before co_await: " << std::this_thread::get_id() << std::endl;
+               co_await MyAwaiter();
+               std::cout << "coroutine after co_await:" << std::this_thread::get_id() << std::endl;
+               co_return;
+       };
+       std::cout << "main thread before sync_wait:" << std::this_thread::get_id() << std::endl;
+       cppcoro::sync_wait([&]() -> cppcoro::task<> {
+               std::cout << "top level coroutine start " << std::this_thread::get_id() << std::endl;
+               auto t = func();
+               std::cout << "top level coroutine before co_await " << std::this_thread::get_id()
+                                 << std::endl;
+               co_await t;
+               std::cout << "top level coroutine after co_await " << std::this_thread::get_id()
+                                 << std::endl;
+       }());
+       std::cout << "main thread after sync_wait:" << std::this_thread::get_id() << std::endl;
+}
+
 TEST_CASE("task doesn't start until awaited")
 {
        bool started = false;

my compiler isclang/llvm-8.0.1.On centos7, the output is unexpected :(

main thread 140019514656576
main thread before sync_wait:140019514656576
top level coroutine start 140019514656576
top level coroutine before co_await 140019514656576
coroutine before co_await: 140019514656576
new thread created: 140018492016384
coroutine after co_await:140019514656576
top level coroutine after co_await 140019514656576
new thread after resume: 140018492016384
main thread after sync_wait:140019514656576

image

andreasbuhr commented 3 years ago

Trying to reproduce, how do I install clang8 on centos7?

anlongfei commented 3 years ago

Two ways to get clang8:

1.get clang8 binary from https://releases.llvm.org/download.html#8.0.1

2.build from source code reference:https://github.com/llvm/llvm-project

git clone git@github.com:llvm/llvm-project.git -b release/8.x \
&& mkdir build \
&& cmake -G Ninja ../llvm-project/llvm -DLLVM_ENABLE_PROJECTS="clang;llvm" -DCMAKE_INSTALL_PREFIX=<path-to-install> -DCMAKE_BUILD_TYPE=Release \
&& ninja install

replace <path-to-install> with where you want to install clang8 binary.