File indexing completed on 2024-11-15 09:04:15
0001
0002
0003
0004
0005 #ifndef BOOST_COBALT_HANDLER_HPP
0006 #define BOOST_COBALT_HANDLER_HPP
0007
0008 #include <boost/cobalt/this_coro.hpp>
0009 #include <boost/cobalt/unique_handle.hpp>
0010 #include <boost/cobalt/detail/util.hpp>
0011
0012 #include <boost/cobalt/detail/sbo_resource.hpp>
0013 #include <boost/asio/bind_allocator.hpp>
0014 #include <boost/asio/post.hpp>
0015 #include <boost/system/result.hpp>
0016
0017 #include <memory>
0018 #include <optional>
0019
0020 namespace boost::cobalt
0021 {
0022
0023 namespace detail
0024 {
0025
0026 enum class completed_immediately_t
0027 {
0028 no, maybe, yes, initiating
0029 };
0030
0031 struct completion_handler_noop_executor
0032 {
0033 executor exec;
0034 completed_immediately_t * completed_immediately = nullptr;
0035
0036 template<typename Fn>
0037 void execute(Fn && fn) const
0038 {
0039
0040 if (completed_immediately &&
0041 ((*completed_immediately == completed_immediately_t::initiating)
0042 || (*completed_immediately == completed_immediately_t::maybe)))
0043 {
0044
0045
0046 *completed_immediately = completed_immediately_t::maybe;
0047 fn();
0048
0049 if (*completed_immediately != completed_immediately_t::yes)
0050 *completed_immediately = completed_immediately_t::initiating;
0051 }
0052 else
0053 {
0054 asio::post(exec, std::forward<Fn>(fn));
0055 }
0056 }
0057
0058 friend bool operator==(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
0059 {
0060 return true;
0061 }
0062
0063 friend bool operator!=(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
0064 {
0065 return false;
0066 }
0067
0068 completion_handler_noop_executor(const completion_handler_noop_executor & rhs) noexcept = default;
0069 completion_handler_noop_executor(cobalt::executor inner, completed_immediately_t * completed_immediately)
0070 : exec(std::move(inner)), completed_immediately(completed_immediately)
0071 {
0072 }
0073
0074 };
0075
0076
0077 struct completion_handler_base
0078 {
0079 using cancellation_slot_type = asio::cancellation_slot;
0080 cancellation_slot_type cancellation_slot ;
0081 cancellation_slot_type get_cancellation_slot() const noexcept
0082 {
0083 return cancellation_slot ;
0084 }
0085
0086 using executor_type = executor;
0087 const executor_type & executor_ ;
0088 const executor_type & get_executor() const noexcept
0089 {
0090 return executor_ ;
0091 }
0092
0093 #if !defined(BOOST_COBALT_NO_PMR)
0094 using allocator_type = pmr::polymorphic_allocator<void>;
0095 pmr::polymorphic_allocator<void> allocator ;
0096 allocator_type get_allocator() const noexcept
0097 {
0098 return allocator ;
0099 }
0100 #else
0101 using allocator_type = detail::sbo_allocator<void>;
0102 detail::sbo_allocator<void> allocator ;
0103 allocator_type get_allocator() const noexcept
0104 {
0105 return allocator ;
0106 }
0107 #endif
0108 using immediate_executor_type = completion_handler_noop_executor;
0109 completed_immediately_t * completed_immediately = nullptr;
0110 immediate_executor_type get_immediate_executor() const noexcept
0111 {
0112 return {get_executor(), completed_immediately};
0113 }
0114
0115 template<typename Promise>
0116 requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
0117 completion_handler_base(std::coroutine_handle<Promise> h,
0118 completed_immediately_t * completed_immediately = nullptr)
0119 : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
0120 executor_(h.promise().get_executor()),
0121 #if !defined(BOOST_COBALT_NO_PMR)
0122 allocator(asio::get_associated_allocator(h.promise(), this_thread::get_allocator())),
0123 #else
0124 allocator(detail::get_null_sbo_resource()),
0125 #endif
0126 completed_immediately(completed_immediately)
0127 {
0128 }
0129 #if !defined(BOOST_COBALT_NO_PMR)
0130 template<typename Promise>
0131 requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
0132 completion_handler_base(std::coroutine_handle<Promise> h,
0133 pmr::memory_resource * resource,
0134 completed_immediately_t * completed_immediately = nullptr)
0135 : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
0136 executor_(h.promise().get_executor()),
0137 allocator(resource),
0138 completed_immediately(completed_immediately)
0139 {
0140 }
0141 #else
0142 template<typename Promise>
0143 requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
0144 completion_handler_base(std::coroutine_handle<Promise> h,
0145 detail::sbo_resource * resource,
0146 completed_immediately_t * completed_immediately = nullptr)
0147 : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
0148 executor_(h.promise().get_executor()),
0149 allocator(resource),
0150 completed_immediately(completed_immediately)
0151 {
0152 }
0153
0154 #endif
0155 };
0156
0157
0158 template<typename Handler>
0159 void assign_cancellation(std::coroutine_handle<void>, Handler &&) {}
0160
0161 template<typename Promise, typename Handler>
0162 void assign_cancellation(std::coroutine_handle<Promise> h, Handler && func)
0163 {
0164 if constexpr (requires {h.promise().get_cancellation_slot();})
0165 if (h.promise().get_cancellation_slot().is_connected())
0166 h.promise().get_cancellation_slot().assign(std::forward<Handler>(func));
0167 }
0168
0169 template<typename Promise>
0170 const executor &
0171 get_executor(std::coroutine_handle<Promise> h)
0172 {
0173 if constexpr (requires {h.promise().get_executor();})
0174 {
0175 static_assert(std::same_as<decltype(h.promise().get_executor()),
0176 const executor &>,
0177 "for performance reasons, the get_executor function on a promise must return a const reference");
0178 return h.promise().get_executor();
0179 }
0180 else
0181 return this_thread::get_executor();
0182 }
0183
0184 inline const executor &
0185 get_executor(std::coroutine_handle<>)
0186 {
0187 return this_thread::get_executor();
0188 }
0189
0190 }
0191
0192 template<typename ... Args>
0193 struct handler
0194 {
0195 void operator()(Args ... args)
0196 {
0197 result.emplace(static_cast<Args>(args)...);
0198 }
0199 handler(std::optional<std::tuple<Args...>> &result) : result(result) {}
0200 private:
0201 std::optional<std::tuple<Args...>> &result;
0202 };
0203
0204 template<typename ... Args>
0205 handler(std::optional<std::tuple<Args...>> &result) -> handler<Args...>;
0206
0207 template<typename ... Args>
0208 struct completion_handler : detail::completion_handler_base
0209 {
0210 completion_handler(completion_handler && ) = default;
0211
0212 template<typename Promise>
0213 completion_handler(std::coroutine_handle<Promise> h,
0214 std::optional<std::tuple<Args...>> &result,
0215 detail::completed_immediately_t * completed_immediately = nullptr
0216 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0217 , const boost::source_location & loc = BOOST_CURRENT_LOCATION
0218 #endif
0219 ) : completion_handler_base(h, completed_immediately),
0220 self(h.address()), result(result)
0221 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0222 , loc_(loc)
0223 #endif
0224 {
0225 }
0226
0227 #if !defined(BOOST_COBALT_NO_PMR)
0228 template<typename Promise>
0229 completion_handler(std::coroutine_handle<Promise> h,
0230 std::optional<std::tuple<Args...>> &result,
0231 pmr::memory_resource * resource,
0232 detail::completed_immediately_t * completed_immediately = nullptr
0233 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0234 , const boost::source_location & loc = BOOST_CURRENT_LOCATION
0235 #endif
0236 ) : completion_handler_base(h, resource, completed_immediately),
0237 self(h.address()), result(result)
0238 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0239 , loc_(loc)
0240 #endif
0241 {
0242 }
0243 #else
0244 template<typename Promise>
0245 completion_handler(std::coroutine_handle<Promise> h,
0246 std::optional<std::tuple<Args...>> &result,
0247 detail::sbo_resource * resource,
0248 detail::completed_immediately_t * completed_immediately = nullptr)
0249 : completion_handler_base(h, resource, completed_immediately),
0250 self(h.address()), result(result)
0251 {
0252 }
0253 #endif
0254
0255
0256 void operator()(Args ... args)
0257 {
0258 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0259 BOOST_ASIO_HANDLER_LOCATION((loc_.file_name(), loc_.line(), loc_.function_name()));
0260 #endif
0261 result.emplace(std::move(args)...);
0262 BOOST_ASSERT(this->self != nullptr);
0263 auto p = this->self.release();
0264 if (completed_immediately != nullptr
0265 && *completed_immediately == detail::completed_immediately_t::maybe)
0266 {
0267 *completed_immediately = detail::completed_immediately_t::yes;
0268 return;
0269 }
0270
0271 std::move(p)();
0272 }
0273 using result_type = std::optional<std::tuple<Args...>>;
0274
0275 ~completion_handler()
0276 {
0277 if (self && completed_immediately
0278 && *completed_immediately == detail::completed_immediately_t::initiating
0279 && std::uncaught_exceptions() > 0)
0280 self.release();
0281 }
0282 private:
0283 unique_handle<void> self;
0284 std::optional<std::tuple<Args...>> &result;
0285 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0286 boost::source_location loc_;
0287 #endif
0288 };
0289
0290 };
0291
0292 #endif