
Учимся готовить C++ корутины на практике Understanding C++ coroutines by example Pavel Novikov @cpp_ape R&D Align Technology No decent user facing support in C++20 Use cppcoro by Lewis Baker https://github.com/lewissbaker/cppcoro 2 Thanks for coming! Gameplan • Iteration 0: my first coroutine • What is a C++ coroutine? • Demystifying compiler magic • Iteration 1: awaiting tasks • Making tasks awaitable • Writing awaitable types • Iteration 2: • Getting tasks result • Thread safety • Analysis of the approach 5 Iteration 0: my first coroutine Task<int> foo() { co_return 42; } A function is a coroutine if it contains one of these: co_return (coroutine return statement) co_await (await expression) co_yield (yielD expression) 6 What is a C++ coroutine? Coroutine taxonomy routine программа subroutine coroutine подпрограмма сопрограмма stackfull stackless (fiber) 7 What is a C++ coroutine? 8 What is a C++ coroutine? Task<int> foo() { A coroutine behaves as if its function-body were replaced by: co_return 42; { } promise-type promise promise-constructor-arguments ; try { foo() co_await promise.initial_suspend() ; function-body } catch ( ... ) { initial suspend if (!initial-await-resume-called) throw ; foo() body promise.unhandled_exception() ; } final-suspend : final suspend co_await promise.final_suspend() ; } 9 What is a C++ coroutine? Task<int> foo() { A coroutine behaves as if its function-body were replaced by: co_return 42; { } promise-type promise promise-constructor-arguments ; try { foo() co_await promise.initial_suspend() ; function-body } catch ( ... ) { initial suspend if (!initial-await-resume-called) throw ; foo() body promise.unhandled_exception() ; } final-suspend : final suspend co_await promise.final_suspend() ; } 10 Task<int> foo() { Transformation by struct CoroFrame { Task<int>::promise_type promise; the compiler bool initial_await_resume_called = false; int state = 0; Task<int> foo() { void operator()() { co_return 42; } co_return 42; originaltransformed code code } }; auto coroFrame = new CoroFrame; auto returnObject{ coroFrame->promise.get_return_object() }; (*coroFrame)(); return returnObject; } Task<int> foo() { Transformation by struct CoroFrame { Task<int>::promise_type promise; the compiler bool initial_await_resume_called = false; int state = 0; Task<int> foo() { void operator()() { co_return 42; try { } Task<int> foo() { co_await promise.initial_suspend(); struct CoroFrame { co_return 42; } coroutine Task<int>::promise_type promise;catch (...) { frame bool initial_await_resume_calledif (!initial_await_resume_called= false; ) int state = 0; throw; void operator()() { /*...*/ } promise.unhandled_exception(); } }; final_suspend: auto coroFrame = new CoroFrame;co_await promise.final_suspend(); auto returnObject{ coroFrame->} promise.get_return_object() }; }; (*coroFrame)(); auto coroFrame = new CoroFrame; return returnObject; auto returnObject{ coroFrame->promise.get_return_object() }; } (*coroFrame)(); return returnObject; } Task<int> foo() { Transformation by struct CoroFrame { Task<int>::promise_type promise; the compiler bool initial_await_resume_called = false; int state = 0; Task<int> foo() { void operator()() { co_return 42; try { } void operator()() { co_await promise.initial_suspend(); try { co_return 42; co_await promise.initial_suspend} (); co_return 42; catch (...) { } if (!initial_await_resume_called) throw; catch (...) { promise.unhandled_exception(); if (!initial_await_resume_called} ) throw; final_suspend: co_await promise.final_suspend(); promise.unhandled_exception} (); } }; final_suspend: auto coroFrame = new CoroFrame; co_await promise.autofinal_suspendreturnObject{ coroFrame(); ->promise.get_return_object() }; (*coroFrame)(); } return returnObject; } Task<int> foo() { Transformation by struct CoroFrame { Task<int>::promise_type promise; the compiler bool initial_await_resume_called = false; int state = 0; Task<int> foo() { void operator()() { co_return 42; try { } void operator()() { co_await promise.initial_suspend(); try { promise.return_value(42); goto final_suspend; co_await promise.initial_suspend} (); promise.return_valuecatch(42);(...) { goto final_suspend; } if (!initial_await_resume_called) throw; catch (...) { promise.unhandled_exception(); if (!initial_await_resume_called} ) throw; final_suspend: co_await promise.final_suspend(); promise.unhandled_exception} (); } }; final_suspend: auto coroFrame = new CoroFrame; co_await promise.autofinal_suspendreturnObject{ coroFrame(); ->promise.get_return_object() }; (*coroFrame)(); } return returnObject; } Task<int> foo() { Transformation by struct CoroFrame { Task<int>::promise_type promise; the compiler bool initial_await_resume_called = false; int state = 0; Task<int> foo() { void operator()() { co_return 42; try { } co_await promise.initial_suspend(); promise.return_value(42); goto final_suspend; Sequence of operations: } Task<int>::promise_type promise; catch (...) { promise.get_return_object(); if (!initial_await_resume_called) promise.initial_suspend(); throw; promise.unhandled_exception(); promise.return_value(42); } promise.unhandled_exception(); final_suspend: promise.final_suspend(); co_await promise.final_suspend(); } }; auto coroFrame = new CoroFrame; auto returnObject{ coroFrame->promise.get_return_object() }; (*coroFrame)(); return returnObject; } Task type template<typename T> struct Promise; struct CoroDeleter { templatetemplate<typename<typenameT>Promise> structvoid operator()[[nodiscard(Promise]] Task *{promise) const noexcept { usingusingpromise_typeCoroHandle = Promisestd::coroutine_handle<T>; <Promise>; TaskCoroHandle() = default::from_promise; (*promise).destroy(); } private}; : templateTask(Promise<typename<T> *Tpromise> ) : promise{ promise } {} using PromisePtr = std::unique_ptr<Promise<T>, CoroDeleter>; PromisePtr<T> promise = nullptr; template<typename> friend struct Promise; }; 16 Task type template<typename T> struct Promise; template<typename T> struct [[nodiscard]] Task { using promise_type = Promise<T>; Task() = default; private: Task(Promise<T> *promise) : promise{ promise } {} PromisePtr<T> promise = nullptr; template<typename> friend struct Promise; }; 17 Promise type voidtemplatereturn_value<typename T(>U &&value) struct Promise { noexcept(std::is_nothrow_assignable_v<decltype(result), decltype(std::forward<U>(value))>) voidTaskunhandled_exception<T> get_return_object()() noexcept { return { this }; } { std::suspend_never initial_suspend() noexcept { return {}; } Tasknoexcept<int>( stdfoo::is_nothrow_assignable_v() { <decltype(result), std::exception_ptr>) result.std::suspend_alwaystemplate emplace<1>(final_suspendstd()::forward< noexcept { Ureturn>(value{};)); } }{ struct CoroFrame { void operator()() { T &&templategetResult<typename() { U> try { foo() result.voidTaskreturn_valuetemplate<int>::promise_type(emplace<2>(U &&value) stdpromise;::current_exception()); if (result.index()void == operator()2) //...() { } noexcept//... (std::is_nothrow_constructible_v<T, decltype(std::forward<U>(value))>); };std::rethrow_exceptiontry { (std} ::get<2>(result)); void unhandled_exception() initial suspend returnautonoexceptcoroFramestd(::move(std::is_nothrow_constructible_v=std new::get<1>(result));co_awaitCoroFramecatchpromise.;<(...)std::exception_ptrinitial_suspend { , std::exception_ptr(); >); } boolautoisReadyreturnObject() const noexcept= promise.coroFrame{ returnreturn_value//...->promise.result.indexget_return_object(42);() !=goto 0; }final_suspend(); ; T(*&&coroFramegetResult();)(); } promise.unhandled_exceptionfoo() ();body return returnObjectcatch; (...)} { /*...*/ } } std::variant<std::monostatefinal_suspend, final_suspendT, std:::exception_ptr: > result;final suspend }; co_awaitco_awaitpromise.final_suspendpromise.final_suspend(); (); } } Iteration 0: my first coroutine Task<int> foo() { std::cout << "foo(): about to return\n"; co_return 42; } auto task = foo(); output: foo(): about to return 19 Iteration 0: my first coroutine Task<void> foo() {void operator()() { try { co_return; co_await promise.initial_suspend(); } promise.return_void(); goto final_suspend; } catch (...) { /*...*/ } template<typenamefinal_suspendT> : struct Promise { co_await promise.final_suspend(); } //... void return_void() noexcept; //... }; 20 Iteration 1: awaiting tasks Task<int> bar() { const auto result = foo(); const int i = co_await result; co_return i + 23; } 21 Awaiting: rough idea co_await result; auto awaitable{ getAwaitable(result) }; if (!awaitable.await_ready()) { awaitable.await_suspend(thisCoroHandle); // suspend coroutine } resume: awaitable.await_resume(); 22 Transformation by the compiler Task<int> bar() { Task<int> bar() { const auto result = foo(); struct CoroFrame { const int i = co_await result; Task<int>::promise_type promise; co_return i + 23; bool initial_await_resume_called = false; } int state = 0; //... void operator()(); transformed code original code }; auto coroFrame = new CoroFrame; auto returnObject{ coroFrame->promise.get_return_object() }; (*coroFrame)(); return returnObject; } 23 Transformation by the compiler void operator()() { struct CoroFrame { try { Task<int>::promise_type promise; switch (state) bool initial_await_resume_called = false; { int state = 0; //... case 0: void operator()(); break; }; case 1: goto initialResume; case 2: goto resume2; default: break; //bad } //... 24 Transformation by the compiler struct CoroFrame { void operator()() { co_await promise.initial_suspend(); //... Task<int>::promise_type promise; state = 1; bool initial_await_resume_called = false; awaitable0
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages83 Page
-
File Size-