123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- //
- // 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/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(F&& f, const Allocator& a)
- : f_(static_cast<F&&>(f)),
- allocator_(a)
- {
- }
- allocator_binder(const allocator_binder& other)
- : f_(other.f_),
- allocator_(other.allocator_)
- {
- }
- allocator_binder(allocator_binder&& other)
- : f_(static_cast<F&&>(other.f_)),
- allocator_(static_cast<allocator_type&&>(other.allocator_))
- {
- }
- allocator_type get_allocator() const noexcept
- {
- return allocator_;
- }
- void operator()()
- {
- f_();
- }
- private:
- F f_;
- allocator_type allocator_;
- };
- template <typename Executor>
- class strand_executor_service::invoker<Executor,
- enable_if_t<
- execution::is_executor<Executor>::value
- >>
- {
- 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_)
- {
- }
- invoker(invoker&& other)
- : impl_(static_cast<implementation_type&&>(other.impl_)),
- executor_(static_cast<executor_type&&>(other.executor_))
- {
- }
- 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_;
- asio::prefer(
- asio::require(
- static_cast<executor_type&&>(ex),
- execution::blocking.never),
- execution::allocator(allocator)
- ).execute(static_cast<invoker&&>(*this_));
- }
- }
- };
- 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 decay_t<
- prefer_result_t<
- Executor,
- execution::outstanding_work_t::tracked_t
- >
- > executor_type;
- implementation_type impl_;
- executor_type executor_;
- };
- #if !defined(ASIO_NO_TS_EXECUTORS)
- template <typename Executor>
- class strand_executor_service::invoker<Executor,
- enable_if_t<
- !execution::is_executor<Executor>::value
- >>
- {
- public:
- invoker(const implementation_type& impl, Executor& ex)
- : impl_(impl),
- work_(ex)
- {
- }
- invoker(const invoker& other)
- : impl_(other.impl_),
- work_(other.work_)
- {
- }
- invoker(invoker&& other)
- : impl_(static_cast<implementation_type&&>(other.impl_)),
- work_(static_cast<executor_work_guard<Executor>&&>(other.work_))
- {
- }
- 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(static_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, Function&& function,
- enable_if_t<
- can_query<Executor, execution::allocator_t<void>>::value
- >*)
- {
- return strand_executor_service::do_execute(impl, ex,
- static_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, Function&& function,
- enable_if_t<
- !can_query<Executor, execution::allocator_t<void>>::value
- >*)
- {
- return strand_executor_service::do_execute(impl, ex,
- static_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, Function&& function, const Allocator& a)
- {
- typedef decay_t<Function> 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(static_cast<Function&&>(function));
- fenced_block b(fenced_block::full);
- static_cast<function_type&&>(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(static_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)
- {
- ex.execute(invoker<Executor>(impl, ex));
- }
- }
- template <typename Executor, typename Function, typename Allocator>
- void strand_executor_service::dispatch(const implementation_type& impl,
- Executor& ex, Function&& function, const Allocator& a)
- {
- typedef decay_t<Function> 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(static_cast<Function&&>(function));
- fenced_block b(fenced_block::full);
- static_cast<function_type&&>(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(static_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, Function&& function, const Allocator& a)
- {
- typedef decay_t<Function> 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(static_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, Function&& function, const Allocator& a)
- {
- typedef decay_t<Function> 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(static_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
|