with.hpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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() { return false;}
  17. template<typename Promise>
  18. BOOST_NOINLINE auto await_suspend(std::coroutine_handle<Promise> h) -> std::coroutine_handle<promise_type>;
  19. inline T await_resume();
  20. private:
  21. with_impl(promise_type & promise) : promise(promise) {}
  22. promise_type & promise;
  23. };
  24. template<typename T>
  25. struct with_promise_value
  26. {
  27. std::optional<T> result;
  28. void return_value(std::optional<T> && value)
  29. {
  30. if (value) // so non-move-assign types work
  31. result.emplace(std::move(*value));
  32. }
  33. T get_result()
  34. {
  35. return std::move(result).value();
  36. }
  37. };
  38. template<>
  39. struct with_promise_value<void>
  40. {
  41. void return_void() {}
  42. void get_result() {}
  43. };
  44. template<typename T>
  45. struct with_impl<T>::promise_type
  46. : with_promise_value<T>,
  47. enable_awaitables<promise_type>,
  48. enable_await_allocator<promise_type>
  49. {
  50. using enable_awaitables<promise_type>::await_transform;
  51. using enable_await_allocator<promise_type>::await_transform;
  52. using executor_type = executor;
  53. const executor_type & get_executor() const {return *exec;}
  54. std::optional<executor_type> exec;
  55. with_impl get_return_object()
  56. {
  57. return with_impl{*this};
  58. }
  59. std::exception_ptr e;
  60. void unhandled_exception()
  61. {
  62. e = std::current_exception();
  63. }
  64. std::suspend_always initial_suspend() noexcept {return {};}
  65. struct final_awaitable
  66. {
  67. promise_type *promise;
  68. bool await_ready() const noexcept
  69. {
  70. return false;
  71. }
  72. BOOST_NOINLINE
  73. auto await_suspend(std::coroutine_handle<promise_type> h) noexcept -> std::coroutine_handle<void>
  74. {
  75. return std::coroutine_handle<void>::from_address(h.promise().awaited_from.address());
  76. }
  77. void await_resume() noexcept
  78. {
  79. }
  80. };
  81. auto final_suspend() noexcept
  82. {
  83. return final_awaitable{this};
  84. }
  85. using cancellation_slot_type = asio::cancellation_slot;
  86. cancellation_slot_type get_cancellation_slot() const {return slot_;}
  87. asio::cancellation_slot slot_;
  88. std::coroutine_handle<void> awaited_from{nullptr};
  89. };
  90. template<typename T>
  91. T with_impl<T>::await_resume()
  92. {
  93. auto e = promise.e;
  94. auto res = std::move(promise.get_result());
  95. std::coroutine_handle<promise_type>::from_promise(promise).destroy();
  96. if (e)
  97. std::rethrow_exception(e);
  98. return std::move(res);
  99. }
  100. template<>
  101. inline void with_impl<void>::await_resume()
  102. {
  103. auto e = promise.e;
  104. std::coroutine_handle<promise_type>::from_promise(promise).destroy();
  105. if (e)
  106. std::rethrow_exception(e);
  107. }
  108. template<typename T>
  109. template<typename Promise>
  110. auto with_impl<T>::await_suspend(std::coroutine_handle<Promise> h) -> std::coroutine_handle<promise_type>
  111. {
  112. if constexpr (requires (Promise p) {p.get_executor();})
  113. promise.exec.emplace(h.promise().get_executor());
  114. else
  115. promise.exec.emplace(this_thread::get_executor());
  116. if constexpr (requires (Promise p) {p.get_cancellation_slot();})
  117. promise.slot_ = h.promise().get_cancellation_slot();
  118. promise.awaited_from = h;
  119. return std::coroutine_handle<promise_type>::from_promise(promise);
  120. }
  121. }
  122. #endif //BOOST_COBALT_DETAIL_WITH_HPP