Учимся Готовить C++ Корутины На Практике Understanding C++ Coroutines by Example Pavel Novikov @Cpp Ape R&D Align Technology No Decent User Facing Support in C++20

Учимся Готовить C++ Корутины На Практике Understanding C++ Coroutines by Example Pavel Novikov @Cpp Ape R&D Align Technology No Decent User Facing Support in C++20

Учимся готовить 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

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    83 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us