Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:43:01

0001 //
0002 // detail/impl/strand_executor_service.hpp
0003 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0009 //
0010 
0011 #ifndef BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
0012 #define BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
0013 
0014 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0015 # pragma once
0016 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
0017 
0018 #include <boost/asio/detail/fenced_block.hpp>
0019 #include <boost/asio/detail/recycling_allocator.hpp>
0020 #include <boost/asio/executor_work_guard.hpp>
0021 #include <boost/asio/defer.hpp>
0022 #include <boost/asio/dispatch.hpp>
0023 #include <boost/asio/post.hpp>
0024 
0025 #include <boost/asio/detail/push_options.hpp>
0026 
0027 namespace boost {
0028 namespace asio {
0029 namespace detail {
0030 
0031 template <typename F, typename Allocator>
0032 class strand_executor_service::allocator_binder
0033 {
0034 public:
0035   typedef Allocator allocator_type;
0036 
0037   allocator_binder(F&& f, const Allocator& a)
0038     : f_(static_cast<F&&>(f)),
0039       allocator_(a)
0040   {
0041   }
0042 
0043   allocator_binder(const allocator_binder& other)
0044     : f_(other.f_),
0045       allocator_(other.allocator_)
0046   {
0047   }
0048 
0049   allocator_binder(allocator_binder&& other)
0050     : f_(static_cast<F&&>(other.f_)),
0051       allocator_(static_cast<allocator_type&&>(other.allocator_))
0052   {
0053   }
0054 
0055   allocator_type get_allocator() const noexcept
0056   {
0057     return allocator_;
0058   }
0059 
0060   void operator()()
0061   {
0062     f_();
0063   }
0064 
0065 private:
0066   F f_;
0067   allocator_type allocator_;
0068 };
0069 
0070 template <typename Executor>
0071 class strand_executor_service::invoker<Executor,
0072     enable_if_t<
0073       execution::is_executor<Executor>::value
0074     >>
0075 {
0076 public:
0077   invoker(const implementation_type& impl, Executor& ex)
0078     : impl_(impl),
0079       executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
0080   {
0081   }
0082 
0083   invoker(const invoker& other)
0084     : impl_(other.impl_),
0085       executor_(other.executor_)
0086   {
0087   }
0088 
0089   invoker(invoker&& other)
0090     : impl_(static_cast<implementation_type&&>(other.impl_)),
0091       executor_(static_cast<executor_type&&>(other.executor_))
0092   {
0093   }
0094 
0095   struct on_invoker_exit
0096   {
0097     invoker* this_;
0098 
0099     ~on_invoker_exit()
0100     {
0101       if (push_waiting_to_ready(this_->impl_))
0102       {
0103         recycling_allocator<void> allocator;
0104         executor_type ex = this_->executor_;
0105         boost::asio::prefer(
0106             boost::asio::require(
0107               static_cast<executor_type&&>(ex),
0108               execution::blocking.never),
0109             execution::allocator(allocator)
0110           ).execute(static_cast<invoker&&>(*this_));
0111       }
0112     }
0113   };
0114 
0115   void operator()()
0116   {
0117     // Ensure the next handler, if any, is scheduled on block exit.
0118     on_invoker_exit on_exit = { this };
0119     (void)on_exit;
0120 
0121     run_ready_handlers(impl_);
0122   }
0123 
0124 private:
0125   typedef decay_t<
0126       prefer_result_t<
0127         Executor,
0128         execution::outstanding_work_t::tracked_t
0129       >
0130     > executor_type;
0131 
0132   implementation_type impl_;
0133   executor_type executor_;
0134 };
0135 
0136 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
0137 
0138 template <typename Executor>
0139 class strand_executor_service::invoker<Executor,
0140     enable_if_t<
0141       !execution::is_executor<Executor>::value
0142     >>
0143 {
0144 public:
0145   invoker(const implementation_type& impl, Executor& ex)
0146     : impl_(impl),
0147       work_(ex)
0148   {
0149   }
0150 
0151   invoker(const invoker& other)
0152     : impl_(other.impl_),
0153       work_(other.work_)
0154   {
0155   }
0156 
0157   invoker(invoker&& other)
0158     : impl_(static_cast<implementation_type&&>(other.impl_)),
0159       work_(static_cast<executor_work_guard<Executor>&&>(other.work_))
0160   {
0161   }
0162 
0163   struct on_invoker_exit
0164   {
0165     invoker* this_;
0166 
0167     ~on_invoker_exit()
0168     {
0169       if (push_waiting_to_ready(this_->impl_))
0170       {
0171         Executor ex(this_->work_.get_executor());
0172         recycling_allocator<void> allocator;
0173         ex.post(static_cast<invoker&&>(*this_), allocator);
0174       }
0175     }
0176   };
0177 
0178   void operator()()
0179   {
0180     // Ensure the next handler, if any, is scheduled on block exit.
0181     on_invoker_exit on_exit = { this };
0182     (void)on_exit;
0183 
0184     run_ready_handlers(impl_);
0185   }
0186 
0187 private:
0188   implementation_type impl_;
0189   executor_work_guard<Executor> work_;
0190 };
0191 
0192 #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
0193 
0194 template <typename Executor, typename Function>
0195 inline void strand_executor_service::execute(const implementation_type& impl,
0196     Executor& ex, Function&& function,
0197     enable_if_t<
0198       can_query<Executor, execution::allocator_t<void>>::value
0199     >*)
0200 {
0201   return strand_executor_service::do_execute(impl, ex,
0202       static_cast<Function&&>(function),
0203       boost::asio::query(ex, execution::allocator));
0204 }
0205 
0206 template <typename Executor, typename Function>
0207 inline void strand_executor_service::execute(const implementation_type& impl,
0208     Executor& ex, Function&& function,
0209     enable_if_t<
0210       !can_query<Executor, execution::allocator_t<void>>::value
0211     >*)
0212 {
0213   return strand_executor_service::do_execute(impl, ex,
0214       static_cast<Function&&>(function),
0215       std::allocator<void>());
0216 }
0217 
0218 template <typename Executor, typename Function, typename Allocator>
0219 void strand_executor_service::do_execute(const implementation_type& impl,
0220     Executor& ex, Function&& function, const Allocator& a)
0221 {
0222   typedef decay_t<Function> function_type;
0223 
0224   // If the executor is not never-blocking, and we are already in the strand,
0225   // then the function can run immediately.
0226   if (boost::asio::query(ex, execution::blocking) != execution::blocking.never
0227       && running_in_this_thread(impl))
0228   {
0229     // Make a local, non-const copy of the function.
0230     function_type tmp(static_cast<Function&&>(function));
0231 
0232     fenced_block b(fenced_block::full);
0233     static_cast<function_type&&>(tmp)();
0234     return;
0235   }
0236 
0237   // Allocate and construct an operation to wrap the function.
0238   typedef executor_op<function_type, Allocator> op;
0239   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
0240   p.p = new (p.v) op(static_cast<Function&&>(function), a);
0241 
0242   BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
0243         "strand_executor", impl.get(), 0, "execute"));
0244 
0245   // Add the function to the strand and schedule the strand if required.
0246   bool first = enqueue(impl, p.p);
0247   p.v = p.p = 0;
0248   if (first)
0249   {
0250     ex.execute(invoker<Executor>(impl, ex));
0251   }
0252 }
0253 
0254 template <typename Executor, typename Function, typename Allocator>
0255 void strand_executor_service::dispatch(const implementation_type& impl,
0256     Executor& ex, Function&& function, const Allocator& a)
0257 {
0258   typedef decay_t<Function> function_type;
0259 
0260   // If we are already in the strand then the function can run immediately.
0261   if (running_in_this_thread(impl))
0262   {
0263     // Make a local, non-const copy of the function.
0264     function_type tmp(static_cast<Function&&>(function));
0265 
0266     fenced_block b(fenced_block::full);
0267     static_cast<function_type&&>(tmp)();
0268     return;
0269   }
0270 
0271   // Allocate and construct an operation to wrap the function.
0272   typedef executor_op<function_type, Allocator> op;
0273   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
0274   p.p = new (p.v) op(static_cast<Function&&>(function), a);
0275 
0276   BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
0277         "strand_executor", impl.get(), 0, "dispatch"));
0278 
0279   // Add the function to the strand and schedule the strand if required.
0280   bool first = enqueue(impl, p.p);
0281   p.v = p.p = 0;
0282   if (first)
0283   {
0284     boost::asio::dispatch(ex,
0285         allocator_binder<invoker<Executor>, Allocator>(
0286           invoker<Executor>(impl, ex), a));
0287   }
0288 }
0289 
0290 // Request invocation of the given function and return immediately.
0291 template <typename Executor, typename Function, typename Allocator>
0292 void strand_executor_service::post(const implementation_type& impl,
0293     Executor& ex, Function&& function, const Allocator& a)
0294 {
0295   typedef decay_t<Function> function_type;
0296 
0297   // Allocate and construct an operation to wrap the function.
0298   typedef executor_op<function_type, Allocator> op;
0299   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
0300   p.p = new (p.v) op(static_cast<Function&&>(function), a);
0301 
0302   BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
0303         "strand_executor", impl.get(), 0, "post"));
0304 
0305   // Add the function to the strand and schedule the strand if required.
0306   bool first = enqueue(impl, p.p);
0307   p.v = p.p = 0;
0308   if (first)
0309   {
0310     boost::asio::post(ex,
0311         allocator_binder<invoker<Executor>, Allocator>(
0312           invoker<Executor>(impl, ex), a));
0313   }
0314 }
0315 
0316 // Request invocation of the given function and return immediately.
0317 template <typename Executor, typename Function, typename Allocator>
0318 void strand_executor_service::defer(const implementation_type& impl,
0319     Executor& ex, Function&& function, const Allocator& a)
0320 {
0321   typedef decay_t<Function> function_type;
0322 
0323   // Allocate and construct an operation to wrap the function.
0324   typedef executor_op<function_type, Allocator> op;
0325   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
0326   p.p = new (p.v) op(static_cast<Function&&>(function), a);
0327 
0328   BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
0329         "strand_executor", impl.get(), 0, "defer"));
0330 
0331   // Add the function to the strand and schedule the strand if required.
0332   bool first = enqueue(impl, p.p);
0333   p.v = p.p = 0;
0334   if (first)
0335   {
0336     boost::asio::defer(ex,
0337         allocator_binder<invoker<Executor>, Allocator>(
0338           invoker<Executor>(impl, ex), a));
0339   }
0340 }
0341 
0342 } // namespace detail
0343 } // namespace asio
0344 } // namespace boost
0345 
0346 #include <boost/asio/detail/pop_options.hpp>
0347 
0348 #endif // BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP