yield_context.hpp 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. //
  2. // Copyright (c) 2024 Klemens Morgenstern (klemens.morgenstern@gmx.net)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_COBALT_EXPERIMENTAL_YIELD_CONTEXT_HPP
  8. #define BOOST_COBALT_EXPERIMENTAL_YIELD_CONTEXT_HPP
  9. #include <boost/cobalt/experimental/frame.hpp>
  10. #include <boost/cobalt/concepts.hpp>
  11. #include <boost/asio/spawn.hpp>
  12. #include <coroutine>
  13. template<typename Executor>
  14. struct std::coroutine_handle<boost::asio::basic_yield_context<Executor>>
  15. {
  16. constexpr operator coroutine_handle<>() const noexcept { return coroutine_handle<>::from_address(address()); }
  17. constexpr explicit operator bool() const noexcept { return true; }
  18. constexpr bool done() const noexcept { return false; }
  19. void operator()() const noexcept {}
  20. void resume() const noexcept {frame_->promiseresume();}
  21. void destroy() const noexcept {frame_->destroy();}
  22. boost::asio::basic_yield_context<Executor> & promise() const noexcept { return frame_->promise; }
  23. constexpr void* address() const noexcept { return frame_; }
  24. struct yield_context_frame :
  25. boost::cobalt::experimental::frame<yield_context_frame, boost::asio::basic_yield_context<Executor>>
  26. {
  27. using boost::cobalt::experimental::frame<yield_context_frame, boost::asio::basic_yield_context<Executor>>::frame;
  28. void resume()
  29. {
  30. lifetime.resume();
  31. }
  32. void destroy()
  33. {
  34. // destroy the lifetime.
  35. auto lf = std::move(lifetime);
  36. }
  37. boost::asio::detail::spawn_handler_base<Executor> lifetime{this->promise};
  38. };
  39. coroutine_handle(yield_context_frame & frame) : frame_(&frame) {}
  40. private:
  41. yield_context_frame * frame_;
  42. };
  43. namespace boost::cobalt::experimental
  44. {
  45. template<awaitable_type Aw, typename Executor>
  46. auto await(Aw && aw, boost::asio::basic_yield_context<Executor> ctx)
  47. {
  48. if (!std::forward<Aw>(aw).await_ready())
  49. {
  50. using ch = std::coroutine_handle<boost::asio::basic_yield_context<Executor>>;
  51. typename ch::yield_context_frame fr{std::move(ctx)};
  52. ch h{fr};
  53. ctx.spawned_thread_->suspend_with(
  54. [&]
  55. {
  56. using rt = decltype(std::forward<Aw>(aw).await_suspend(h));
  57. if constexpr (std::is_void_v<rt>)
  58. std::forward<Aw>(aw).await_suspend(h);
  59. else if constexpr (std::is_same_v<rt, bool>)
  60. {
  61. if (!std::forward<Aw>(aw).await_suspend(h))
  62. ctx.spawned_thread_->resume();
  63. }
  64. else
  65. std::forward<Aw>(aw).await_suspend(h).resume();
  66. }
  67. );
  68. }
  69. return std::forward<Aw>(aw).await_resume();
  70. }
  71. template<typename Aw, typename Executor>
  72. requires requires (Aw && aw) {{std::forward<Aw>(aw).operator co_await()} -> awaitable_type; }
  73. auto await(Aw && aw, boost::asio::basic_yield_context<Executor> ctx)
  74. {
  75. return await(std::forward<Aw>(aw).operator co_await(), std::move(ctx));
  76. }
  77. template<typename Aw, typename Executor>
  78. requires requires (Aw && aw) {{operator co_await(std::forward<Aw>(aw))} -> awaitable_type; }
  79. auto await(Aw && aw, boost::asio::basic_yield_context<Executor> ctx)
  80. {
  81. return await(operator co_await(std::forward<Aw>(aw)), std::move(ctx));
  82. }
  83. }
  84. #endif //BOOST_COBALT_EXPERIMENTAL_YIELD_CONTEXT_HPP