2 // detail/impl/strand_executor_service.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 // Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11 #ifndef BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
12 #define BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18 #include <boost/asio/detail/call_stack.hpp>
19 #include <boost/asio/detail/fenced_block.hpp>
20 #include <boost/asio/detail/handler_invoke_helpers.hpp>
21 #include <boost/asio/detail/recycling_allocator.hpp>
22 #include <boost/asio/executor_work_guard.hpp>
24 #include <boost/asio/detail/push_options.hpp>
30 template <typename Executor>
31 class strand_executor_service::invoker
34 invoker(const implementation_type& impl, Executor& ex)
40 invoker(const invoker& other)
46 #if defined(BOOST_ASIO_HAS_MOVE)
47 invoker(invoker&& other)
48 : impl_(BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_)),
49 work_(BOOST_ASIO_MOVE_CAST(executor_work_guard<Executor>)(other.work_))
52 #endif // defined(BOOST_ASIO_HAS_MOVE)
54 struct on_invoker_exit
60 this_->impl_->mutex_->lock();
61 this_->impl_->ready_queue_.push(this_->impl_->waiting_queue_);
62 bool more_handlers = this_->impl_->locked_ =
63 !this_->impl_->ready_queue_.empty();
64 this_->impl_->mutex_->unlock();
68 Executor ex(this_->work_.get_executor());
69 recycling_allocator<void> allocator;
70 ex.post(BOOST_ASIO_MOVE_CAST(invoker)(*this_), allocator);
77 // Indicate that this strand is executing on the current thread.
78 call_stack<strand_impl>::context ctx(impl_.get());
80 // Ensure the next handler, if any, is scheduled on block exit.
81 on_invoker_exit on_exit = { this };
84 // Run all ready handlers. No lock is required since the ready queue is
85 // accessed only within the strand.
86 boost::system::error_code ec;
87 while (scheduler_operation* o = impl_->ready_queue_.front())
89 impl_->ready_queue_.pop();
90 o->complete(impl_.get(), ec, 0);
95 implementation_type impl_;
96 executor_work_guard<Executor> work_;
99 template <typename Executor, typename Function, typename Allocator>
100 void strand_executor_service::dispatch(const implementation_type& impl,
101 Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a)
103 typedef typename decay<Function>::type function_type;
105 // If we are already in the strand then the function can run immediately.
106 if (call_stack<strand_impl>::contains(impl.get()))
108 // Make a local, non-const copy of the function.
109 function_type tmp(BOOST_ASIO_MOVE_CAST(Function)(function));
111 fenced_block b(fenced_block::full);
112 boost_asio_handler_invoke_helpers::invoke(tmp, tmp);
116 // Allocate and construct an operation to wrap the function.
117 typedef executor_op<function_type, Allocator> op;
118 typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
119 p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a);
121 BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
122 "strand_executor", impl.get(), 0, "dispatch"));
124 // Add the function to the strand and schedule the strand if required.
125 bool first = enqueue(impl, p.p);
128 ex.dispatch(invoker<Executor>(impl, ex), a);
131 // Request invocation of the given function and return immediately.
132 template <typename Executor, typename Function, typename Allocator>
133 void strand_executor_service::post(const implementation_type& impl,
134 Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a)
136 typedef typename decay<Function>::type function_type;
138 // Allocate and construct an operation to wrap the function.
139 typedef executor_op<function_type, Allocator> op;
140 typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
141 p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a);
143 BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
144 "strand_executor", impl.get(), 0, "post"));
146 // Add the function to the strand and schedule the strand if required.
147 bool first = enqueue(impl, p.p);
150 ex.post(invoker<Executor>(impl, ex), a);
153 // Request invocation of the given function and return immediately.
154 template <typename Executor, typename Function, typename Allocator>
155 void strand_executor_service::defer(const implementation_type& impl,
156 Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a)
158 typedef typename decay<Function>::type function_type;
160 // Allocate and construct an operation to wrap the function.
161 typedef executor_op<function_type, Allocator> op;
162 typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
163 p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a);
165 BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
166 "strand_executor", impl.get(), 0, "defer"));
168 // Add the function to the strand and schedule the strand if required.
169 bool first = enqueue(impl, p.p);
172 ex.defer(invoker<Executor>(impl, ex), a);
175 } // namespace detail
179 #include <boost/asio/detail/pop_options.hpp>
181 #endif // BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP