Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-11-15 09:04:15

0001 // Copyright (c) 2022 Klemens D. Morgenstern
0002 //
0003 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0004 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
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     // only allow it when we're still initializing
0040     if (completed_immediately &&
0041         ((*completed_immediately == completed_immediately_t::initiating)
0042       || (*completed_immediately == completed_immediately_t::maybe)))
0043     {
0044       // only use this indicator if the fn will actually call our completion-handler
0045       // otherwise this was a single op in a composed operation
0046       *completed_immediately = completed_immediately_t::maybe;
0047       fn();
0048       // yes means completion_handler::operator() was called, so we're good.
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 //BOOST_COBALT_HANDLER_HPP