with.hpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright (c) 2022 Klemens D. Morgenstern
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_COBALT_DETAIL_WITH_HPP
  6. #define BOOST_COBALT_DETAIL_WITH_HPP
  7. #include <boost/cobalt/concepts.hpp>
  8. #include <boost/cobalt/this_coro.hpp>
  9. #include <boost/asio/cancellation_signal.hpp>
  10. namespace boost::cobalt::detail
  11. {
  12. template<typename T>
  13. struct [[nodiscard]] with_impl
  14. {
  15. struct promise_type;
  16. bool await_ready() const { return false;}
  17. template<typename Promise>
  18. BOOST_COBALT_MSVC_NOINLINE
  19. auto await_suspend(std::coroutine_handle<Promise> h) -> std::coroutine_handle<promise_type>;
  20. inline T await_resume();
  21. private:
  22. with_impl(promise_type & promise) : promise(promise) {}
  23. promise_type & promise;
  24. };
  25. template<typename T>
  26. struct with_promise_value
  27. {
  28. std::optional<T> result;
  29. void return_value(std::optional<T> && value)
  30. {
  31. if (value) // so non-move-assign types work
  32. result.emplace(std::move(*value));
  33. }
  34. std::optional<T> get_result()
  35. {
  36. return std::move(result);
  37. }
  38. };
  39. template<>
  40. struct with_promise_value<void>
  41. {
  42. void return_void() {}
  43. void get_result() {}
  44. };
  45. template<typename T>
  46. struct with_impl<T>::promise_type
  47. : with_promise_value<T>,
  48. enable_awaitables<promise_type>,
  49. enable_await_allocator<promise_type>
  50. {
  51. using enable_awaitables<promise_type>::await_transform;
  52. using enable_await_allocator<promise_type>::await_transform;
  53. using executor_type = executor;
  54. const executor_type & get_executor() const {return *exec;}
  55. std::optional<executor_type> exec;
  56. with_impl get_return_object()
  57. {
  58. return with_impl{*this};
  59. }
  60. std::exception_ptr e;
  61. void unhandled_exception()
  62. {
  63. e = std::current_exception();
  64. }
  65. std::suspend_always initial_suspend() noexcept {return {};}
  66. struct final_awaitable
  67. {
  68. promise_type *promise;
  69. bool await_ready() const noexcept
  70. {
  71. return false;
  72. }
  73. BOOST_COBALT_MSVC_NOINLINE
  74. auto await_suspend(std::coroutine_handle<promise_type> h) noexcept -> std::coroutine_handle<void>
  75. {
  76. return std::coroutine_handle<void>::from_address(h.promise().awaited_from.address());
  77. }
  78. void await_resume() noexcept
  79. {
  80. }
  81. };
  82. auto final_suspend() noexcept
  83. {
  84. return final_awaitable{this};
  85. }
  86. using cancellation_slot_type = asio::cancellation_slot;
  87. cancellation_slot_type get_cancellation_slot() const {return slot_;}
  88. asio::cancellation_slot slot_;
  89. std::coroutine_handle<void> awaited_from{nullptr};
  90. };
  91. template<typename T>
  92. T with_impl<T>::await_resume()
  93. {
  94. auto e = promise.e;
  95. auto res = promise.get_result();
  96. std::coroutine_handle<promise_type>::from_promise(promise).destroy();
  97. if (e)
  98. std::rethrow_exception(e);
  99. return *std::move(res);
  100. }
  101. template<>
  102. inline void with_impl<void>::await_resume()
  103. {
  104. auto e = promise.e;
  105. std::coroutine_handle<promise_type>::from_promise(promise).destroy();
  106. if (e)
  107. std::rethrow_exception(e);
  108. }
  109. template<typename T>
  110. template<typename Promise>
  111. auto with_impl<T>::await_suspend(std::coroutine_handle<Promise> h) -> std::coroutine_handle<promise_type>
  112. {
  113. if constexpr (requires {h.promise().get_executor();})
  114. promise.exec.emplace(h.promise().get_executor());
  115. else
  116. promise.exec.emplace(this_thread::get_executor());
  117. if constexpr (requires {h.promise().get_cancellation_slot();})
  118. promise.slot_ = h.promise().get_cancellation_slot();
  119. promise.awaited_from = h;
  120. return std::coroutine_handle<promise_type>::from_promise(promise);
  121. }
  122. }
  123. #endif //BOOST_COBALT_DETAIL_WITH_HPP