368 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			368 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| // detail/impl/strand_executor_service.hpp
 | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| //
 | |
| // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
 | |
| //
 | |
| // Distributed under the Boost Software License, Version 1.0. (See accompanying
 | |
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | |
| //
 | |
| 
 | |
| #ifndef ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
 | |
| #define ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
 | |
| 
 | |
| #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| # pragma once
 | |
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| 
 | |
| #include "asio/detail/fenced_block.hpp"
 | |
| #include "asio/detail/handler_invoke_helpers.hpp"
 | |
| #include "asio/detail/recycling_allocator.hpp"
 | |
| #include "asio/executor_work_guard.hpp"
 | |
| #include "asio/defer.hpp"
 | |
| #include "asio/dispatch.hpp"
 | |
| #include "asio/post.hpp"
 | |
| 
 | |
| #include "asio/detail/push_options.hpp"
 | |
| 
 | |
| namespace asio {
 | |
| namespace detail {
 | |
| 
 | |
| template <typename F, typename Allocator>
 | |
| class strand_executor_service::allocator_binder
 | |
| {
 | |
| public:
 | |
|   typedef Allocator allocator_type;
 | |
| 
 | |
|   allocator_binder(ASIO_MOVE_ARG(F) f, const Allocator& a)
 | |
|     : f_(ASIO_MOVE_CAST(F)(f)),
 | |
|       allocator_(a)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   allocator_binder(const allocator_binder& other)
 | |
|     : f_(other.f_),
 | |
|       allocator_(other.allocator_)
 | |
|   {
 | |
|   }
 | |
| 
 | |
| #if defined(ASIO_HAS_MOVE)
 | |
|   allocator_binder(allocator_binder&& other)
 | |
|     : f_(ASIO_MOVE_CAST(F)(other.f_)),
 | |
|       allocator_(ASIO_MOVE_CAST(allocator_type)(other.allocator_))
 | |
|   {
 | |
|   }
 | |
| #endif // defined(ASIO_HAS_MOVE)
 | |
| 
 | |
|   allocator_type get_allocator() const ASIO_NOEXCEPT
 | |
|   {
 | |
|     return allocator_;
 | |
|   }
 | |
| 
 | |
|   void operator()()
 | |
|   {
 | |
|     f_();
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   F f_;
 | |
|   allocator_type allocator_;
 | |
| };
 | |
| 
 | |
| template <typename Executor>
 | |
| class strand_executor_service::invoker<Executor,
 | |
|     typename enable_if<
 | |
|       execution::is_executor<Executor>::value
 | |
|     >::type>
 | |
| {
 | |
| public:
 | |
|   invoker(const implementation_type& impl, Executor& ex)
 | |
|     : impl_(impl),
 | |
|       executor_(asio::prefer(ex, execution::outstanding_work.tracked))
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   invoker(const invoker& other)
 | |
|     : impl_(other.impl_),
 | |
|       executor_(other.executor_)
 | |
|   {
 | |
|   }
 | |
| 
 | |
| #if defined(ASIO_HAS_MOVE)
 | |
|   invoker(invoker&& other)
 | |
|     : impl_(ASIO_MOVE_CAST(implementation_type)(other.impl_)),
 | |
|       executor_(ASIO_MOVE_CAST(executor_type)(other.executor_))
 | |
|   {
 | |
|   }
 | |
| #endif // defined(ASIO_HAS_MOVE)
 | |
| 
 | |
|   struct on_invoker_exit
 | |
|   {
 | |
|     invoker* this_;
 | |
| 
 | |
|     ~on_invoker_exit()
 | |
|     {
 | |
|       if (push_waiting_to_ready(this_->impl_))
 | |
|       {
 | |
|         recycling_allocator<void> allocator;
 | |
|         executor_type ex = this_->executor_;
 | |
| #if defined(ASIO_NO_DEPRECATED)
 | |
|         asio::prefer(
 | |
|             asio::require(
 | |
|               ASIO_MOVE_CAST(executor_type)(ex),
 | |
|               execution::blocking.never),
 | |
|             execution::allocator(allocator)
 | |
|           ).execute(ASIO_MOVE_CAST(invoker)(*this_));
 | |
| #else // defined(ASIO_NO_DEPRECATED)
 | |
|         execution::execute(
 | |
|             asio::prefer(
 | |
|               asio::require(
 | |
|                 ASIO_MOVE_CAST(executor_type)(ex),
 | |
|                 execution::blocking.never),
 | |
|               execution::allocator(allocator)),
 | |
|             ASIO_MOVE_CAST(invoker)(*this_));
 | |
| #endif // defined(ASIO_NO_DEPRECATED)
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   void operator()()
 | |
|   {
 | |
|     // Ensure the next handler, if any, is scheduled on block exit.
 | |
|     on_invoker_exit on_exit = { this };
 | |
|     (void)on_exit;
 | |
| 
 | |
|     run_ready_handlers(impl_);
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   typedef typename decay<
 | |
|       typename prefer_result<
 | |
|         Executor,
 | |
|         execution::outstanding_work_t::tracked_t
 | |
|       >::type
 | |
|     >::type executor_type;
 | |
| 
 | |
|   implementation_type impl_;
 | |
|   executor_type executor_;
 | |
| };
 | |
| 
 | |
| #if !defined(ASIO_NO_TS_EXECUTORS)
 | |
| 
 | |
| template <typename Executor>
 | |
| class strand_executor_service::invoker<Executor,
 | |
|     typename enable_if<
 | |
|       !execution::is_executor<Executor>::value
 | |
|     >::type>
 | |
| {
 | |
| public:
 | |
|   invoker(const implementation_type& impl, Executor& ex)
 | |
|     : impl_(impl),
 | |
|       work_(ex)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   invoker(const invoker& other)
 | |
|     : impl_(other.impl_),
 | |
|       work_(other.work_)
 | |
|   {
 | |
|   }
 | |
| 
 | |
| #if defined(ASIO_HAS_MOVE)
 | |
|   invoker(invoker&& other)
 | |
|     : impl_(ASIO_MOVE_CAST(implementation_type)(other.impl_)),
 | |
|       work_(ASIO_MOVE_CAST(executor_work_guard<Executor>)(other.work_))
 | |
|   {
 | |
|   }
 | |
| #endif // defined(ASIO_HAS_MOVE)
 | |
| 
 | |
|   struct on_invoker_exit
 | |
|   {
 | |
|     invoker* this_;
 | |
| 
 | |
|     ~on_invoker_exit()
 | |
|     {
 | |
|       if (push_waiting_to_ready(this_->impl_))
 | |
|       {
 | |
|         Executor ex(this_->work_.get_executor());
 | |
|         recycling_allocator<void> allocator;
 | |
|         ex.post(ASIO_MOVE_CAST(invoker)(*this_), allocator);
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   void operator()()
 | |
|   {
 | |
|     // Ensure the next handler, if any, is scheduled on block exit.
 | |
|     on_invoker_exit on_exit = { this };
 | |
|     (void)on_exit;
 | |
| 
 | |
|     run_ready_handlers(impl_);
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   implementation_type impl_;
 | |
|   executor_work_guard<Executor> work_;
 | |
| };
 | |
| 
 | |
| #endif // !defined(ASIO_NO_TS_EXECUTORS)
 | |
| 
 | |
| template <typename Executor, typename Function>
 | |
| inline void strand_executor_service::execute(const implementation_type& impl,
 | |
|     Executor& ex, ASIO_MOVE_ARG(Function) function,
 | |
|     typename enable_if<
 | |
|       can_query<Executor, execution::allocator_t<void> >::value
 | |
|     >::type*)
 | |
| {
 | |
|   return strand_executor_service::do_execute(impl, ex,
 | |
|       ASIO_MOVE_CAST(Function)(function),
 | |
|       asio::query(ex, execution::allocator));
 | |
| }
 | |
| 
 | |
| template <typename Executor, typename Function>
 | |
| inline void strand_executor_service::execute(const implementation_type& impl,
 | |
|     Executor& ex, ASIO_MOVE_ARG(Function) function,
 | |
|     typename enable_if<
 | |
|       !can_query<Executor, execution::allocator_t<void> >::value
 | |
|     >::type*)
 | |
| {
 | |
|   return strand_executor_service::do_execute(impl, ex,
 | |
|       ASIO_MOVE_CAST(Function)(function),
 | |
|       std::allocator<void>());
 | |
| }
 | |
| 
 | |
| template <typename Executor, typename Function, typename Allocator>
 | |
| void strand_executor_service::do_execute(const implementation_type& impl,
 | |
|     Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a)
 | |
| {
 | |
|   typedef typename decay<Function>::type function_type;
 | |
| 
 | |
|   // If the executor is not never-blocking, and we are already in the strand,
 | |
|   // then the function can run immediately.
 | |
|   if (asio::query(ex, execution::blocking) != execution::blocking.never
 | |
|       && running_in_this_thread(impl))
 | |
|   {
 | |
|     // Make a local, non-const copy of the function.
 | |
|     function_type tmp(ASIO_MOVE_CAST(Function)(function));
 | |
| 
 | |
|     fenced_block b(fenced_block::full);
 | |
|     asio_handler_invoke_helpers::invoke(tmp, tmp);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Allocate and construct an operation to wrap the function.
 | |
|   typedef executor_op<function_type, Allocator> op;
 | |
|   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
 | |
|   p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a);
 | |
| 
 | |
|   ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
 | |
|         "strand_executor", impl.get(), 0, "execute"));
 | |
| 
 | |
|   // Add the function to the strand and schedule the strand if required.
 | |
|   bool first = enqueue(impl, p.p);
 | |
|   p.v = p.p = 0;
 | |
|   if (first)
 | |
|   {
 | |
| #if defined(ASIO_NO_DEPRECATED)
 | |
|     ex.execute(invoker<Executor>(impl, ex));
 | |
| #else // defined(ASIO_NO_DEPRECATED)
 | |
|     execution::execute(ex, invoker<Executor>(impl, ex));
 | |
| #endif // defined(ASIO_NO_DEPRECATED)
 | |
|   }
 | |
| }
 | |
| 
 | |
| template <typename Executor, typename Function, typename Allocator>
 | |
| void strand_executor_service::dispatch(const implementation_type& impl,
 | |
|     Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a)
 | |
| {
 | |
|   typedef typename decay<Function>::type function_type;
 | |
| 
 | |
|   // If we are already in the strand then the function can run immediately.
 | |
|   if (running_in_this_thread(impl))
 | |
|   {
 | |
|     // Make a local, non-const copy of the function.
 | |
|     function_type tmp(ASIO_MOVE_CAST(Function)(function));
 | |
| 
 | |
|     fenced_block b(fenced_block::full);
 | |
|     asio_handler_invoke_helpers::invoke(tmp, tmp);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Allocate and construct an operation to wrap the function.
 | |
|   typedef executor_op<function_type, Allocator> op;
 | |
|   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
 | |
|   p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a);
 | |
| 
 | |
|   ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
 | |
|         "strand_executor", impl.get(), 0, "dispatch"));
 | |
| 
 | |
|   // Add the function to the strand and schedule the strand if required.
 | |
|   bool first = enqueue(impl, p.p);
 | |
|   p.v = p.p = 0;
 | |
|   if (first)
 | |
|   {
 | |
|     asio::dispatch(ex,
 | |
|         allocator_binder<invoker<Executor>, Allocator>(
 | |
|           invoker<Executor>(impl, ex), a));
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Request invocation of the given function and return immediately.
 | |
| template <typename Executor, typename Function, typename Allocator>
 | |
| void strand_executor_service::post(const implementation_type& impl,
 | |
|     Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a)
 | |
| {
 | |
|   typedef typename decay<Function>::type function_type;
 | |
| 
 | |
|   // Allocate and construct an operation to wrap the function.
 | |
|   typedef executor_op<function_type, Allocator> op;
 | |
|   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
 | |
|   p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a);
 | |
| 
 | |
|   ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
 | |
|         "strand_executor", impl.get(), 0, "post"));
 | |
| 
 | |
|   // Add the function to the strand and schedule the strand if required.
 | |
|   bool first = enqueue(impl, p.p);
 | |
|   p.v = p.p = 0;
 | |
|   if (first)
 | |
|   {
 | |
|     asio::post(ex,
 | |
|         allocator_binder<invoker<Executor>, Allocator>(
 | |
|           invoker<Executor>(impl, ex), a));
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Request invocation of the given function and return immediately.
 | |
| template <typename Executor, typename Function, typename Allocator>
 | |
| void strand_executor_service::defer(const implementation_type& impl,
 | |
|     Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a)
 | |
| {
 | |
|   typedef typename decay<Function>::type function_type;
 | |
| 
 | |
|   // Allocate and construct an operation to wrap the function.
 | |
|   typedef executor_op<function_type, Allocator> op;
 | |
|   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
 | |
|   p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a);
 | |
| 
 | |
|   ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
 | |
|         "strand_executor", impl.get(), 0, "defer"));
 | |
| 
 | |
|   // Add the function to the strand and schedule the strand if required.
 | |
|   bool first = enqueue(impl, p.p);
 | |
|   p.v = p.p = 0;
 | |
|   if (first)
 | |
|   {
 | |
|     asio::defer(ex,
 | |
|         allocator_binder<invoker<Executor>, Allocator>(
 | |
|           invoker<Executor>(impl, ex), a));
 | |
|   }
 | |
| }
 | |
| 
 | |
| } // namespace detail
 | |
| } // namespace asio
 | |
| 
 | |
| #include "asio/detail/pop_options.hpp"
 | |
| 
 | |
| #endif // ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
 |