Chris@16
|
1 //
|
Chris@16
|
2 // detail/impl/strand_service.ipp
|
Chris@16
|
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
Chris@16
|
4 //
|
Chris@101
|
5 // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
Chris@16
|
6 //
|
Chris@16
|
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
|
Chris@16
|
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
9 //
|
Chris@16
|
10
|
Chris@16
|
11 #ifndef BOOST_ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP
|
Chris@16
|
12 #define BOOST_ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP
|
Chris@16
|
13
|
Chris@16
|
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
Chris@16
|
15 # pragma once
|
Chris@16
|
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
Chris@16
|
17
|
Chris@16
|
18 #include <boost/asio/detail/config.hpp>
|
Chris@16
|
19 #include <boost/asio/detail/call_stack.hpp>
|
Chris@16
|
20 #include <boost/asio/detail/strand_service.hpp>
|
Chris@16
|
21
|
Chris@16
|
22 #include <boost/asio/detail/push_options.hpp>
|
Chris@16
|
23
|
Chris@16
|
24 namespace boost {
|
Chris@16
|
25 namespace asio {
|
Chris@16
|
26 namespace detail {
|
Chris@16
|
27
|
Chris@16
|
28 struct strand_service::on_do_complete_exit
|
Chris@16
|
29 {
|
Chris@16
|
30 io_service_impl* owner_;
|
Chris@16
|
31 strand_impl* impl_;
|
Chris@16
|
32
|
Chris@16
|
33 ~on_do_complete_exit()
|
Chris@16
|
34 {
|
Chris@16
|
35 impl_->mutex_.lock();
|
Chris@16
|
36 impl_->ready_queue_.push(impl_->waiting_queue_);
|
Chris@16
|
37 bool more_handlers = impl_->locked_ = !impl_->ready_queue_.empty();
|
Chris@16
|
38 impl_->mutex_.unlock();
|
Chris@16
|
39
|
Chris@16
|
40 if (more_handlers)
|
Chris@16
|
41 owner_->post_immediate_completion(impl_, true);
|
Chris@16
|
42 }
|
Chris@16
|
43 };
|
Chris@16
|
44
|
Chris@16
|
45 strand_service::strand_service(boost::asio::io_service& io_service)
|
Chris@16
|
46 : boost::asio::detail::service_base<strand_service>(io_service),
|
Chris@16
|
47 io_service_(boost::asio::use_service<io_service_impl>(io_service)),
|
Chris@16
|
48 mutex_(),
|
Chris@16
|
49 salt_(0)
|
Chris@16
|
50 {
|
Chris@16
|
51 }
|
Chris@16
|
52
|
Chris@16
|
53 void strand_service::shutdown_service()
|
Chris@16
|
54 {
|
Chris@16
|
55 op_queue<operation> ops;
|
Chris@16
|
56
|
Chris@16
|
57 boost::asio::detail::mutex::scoped_lock lock(mutex_);
|
Chris@16
|
58
|
Chris@16
|
59 for (std::size_t i = 0; i < num_implementations; ++i)
|
Chris@16
|
60 {
|
Chris@16
|
61 if (strand_impl* impl = implementations_[i].get())
|
Chris@16
|
62 {
|
Chris@16
|
63 ops.push(impl->waiting_queue_);
|
Chris@16
|
64 ops.push(impl->ready_queue_);
|
Chris@16
|
65 }
|
Chris@16
|
66 }
|
Chris@16
|
67 }
|
Chris@16
|
68
|
Chris@16
|
69 void strand_service::construct(strand_service::implementation_type& impl)
|
Chris@16
|
70 {
|
Chris@16
|
71 boost::asio::detail::mutex::scoped_lock lock(mutex_);
|
Chris@16
|
72
|
Chris@16
|
73 std::size_t salt = salt_++;
|
Chris@16
|
74 #if defined(BOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION)
|
Chris@16
|
75 std::size_t index = salt;
|
Chris@16
|
76 #else // defined(BOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION)
|
Chris@16
|
77 std::size_t index = reinterpret_cast<std::size_t>(&impl);
|
Chris@16
|
78 index += (reinterpret_cast<std::size_t>(&impl) >> 3);
|
Chris@16
|
79 index ^= salt + 0x9e3779b9 + (index << 6) + (index >> 2);
|
Chris@16
|
80 #endif // defined(BOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION)
|
Chris@16
|
81 index = index % num_implementations;
|
Chris@16
|
82
|
Chris@16
|
83 if (!implementations_[index].get())
|
Chris@16
|
84 implementations_[index].reset(new strand_impl);
|
Chris@16
|
85 impl = implementations_[index].get();
|
Chris@16
|
86 }
|
Chris@16
|
87
|
Chris@16
|
88 bool strand_service::running_in_this_thread(
|
Chris@16
|
89 const implementation_type& impl) const
|
Chris@16
|
90 {
|
Chris@16
|
91 return call_stack<strand_impl>::contains(impl) != 0;
|
Chris@16
|
92 }
|
Chris@16
|
93
|
Chris@16
|
94 bool strand_service::do_dispatch(implementation_type& impl, operation* op)
|
Chris@16
|
95 {
|
Chris@16
|
96 // If we are running inside the io_service, and no other handler already
|
Chris@16
|
97 // holds the strand lock, then the handler can run immediately.
|
Chris@16
|
98 bool can_dispatch = io_service_.can_dispatch();
|
Chris@16
|
99 impl->mutex_.lock();
|
Chris@16
|
100 if (can_dispatch && !impl->locked_)
|
Chris@16
|
101 {
|
Chris@16
|
102 // Immediate invocation is allowed.
|
Chris@16
|
103 impl->locked_ = true;
|
Chris@16
|
104 impl->mutex_.unlock();
|
Chris@16
|
105 return true;
|
Chris@16
|
106 }
|
Chris@16
|
107
|
Chris@16
|
108 if (impl->locked_)
|
Chris@16
|
109 {
|
Chris@16
|
110 // Some other handler already holds the strand lock. Enqueue for later.
|
Chris@16
|
111 impl->waiting_queue_.push(op);
|
Chris@16
|
112 impl->mutex_.unlock();
|
Chris@16
|
113 }
|
Chris@16
|
114 else
|
Chris@16
|
115 {
|
Chris@16
|
116 // The handler is acquiring the strand lock and so is responsible for
|
Chris@16
|
117 // scheduling the strand.
|
Chris@16
|
118 impl->locked_ = true;
|
Chris@16
|
119 impl->mutex_.unlock();
|
Chris@16
|
120 impl->ready_queue_.push(op);
|
Chris@16
|
121 io_service_.post_immediate_completion(impl, false);
|
Chris@16
|
122 }
|
Chris@16
|
123
|
Chris@16
|
124 return false;
|
Chris@16
|
125 }
|
Chris@16
|
126
|
Chris@16
|
127 void strand_service::do_post(implementation_type& impl,
|
Chris@16
|
128 operation* op, bool is_continuation)
|
Chris@16
|
129 {
|
Chris@16
|
130 impl->mutex_.lock();
|
Chris@16
|
131 if (impl->locked_)
|
Chris@16
|
132 {
|
Chris@16
|
133 // Some other handler already holds the strand lock. Enqueue for later.
|
Chris@16
|
134 impl->waiting_queue_.push(op);
|
Chris@16
|
135 impl->mutex_.unlock();
|
Chris@16
|
136 }
|
Chris@16
|
137 else
|
Chris@16
|
138 {
|
Chris@16
|
139 // The handler is acquiring the strand lock and so is responsible for
|
Chris@16
|
140 // scheduling the strand.
|
Chris@16
|
141 impl->locked_ = true;
|
Chris@16
|
142 impl->mutex_.unlock();
|
Chris@16
|
143 impl->ready_queue_.push(op);
|
Chris@16
|
144 io_service_.post_immediate_completion(impl, is_continuation);
|
Chris@16
|
145 }
|
Chris@16
|
146 }
|
Chris@16
|
147
|
Chris@16
|
148 void strand_service::do_complete(io_service_impl* owner, operation* base,
|
Chris@16
|
149 const boost::system::error_code& ec, std::size_t /*bytes_transferred*/)
|
Chris@16
|
150 {
|
Chris@16
|
151 if (owner)
|
Chris@16
|
152 {
|
Chris@16
|
153 strand_impl* impl = static_cast<strand_impl*>(base);
|
Chris@16
|
154
|
Chris@16
|
155 // Indicate that this strand is executing on the current thread.
|
Chris@16
|
156 call_stack<strand_impl>::context ctx(impl);
|
Chris@16
|
157
|
Chris@16
|
158 // Ensure the next handler, if any, is scheduled on block exit.
|
Chris@16
|
159 on_do_complete_exit on_exit = { owner, impl };
|
Chris@16
|
160 (void)on_exit;
|
Chris@16
|
161
|
Chris@16
|
162 // Run all ready handlers. No lock is required since the ready queue is
|
Chris@16
|
163 // accessed only within the strand.
|
Chris@16
|
164 while (operation* o = impl->ready_queue_.front())
|
Chris@16
|
165 {
|
Chris@16
|
166 impl->ready_queue_.pop();
|
Chris@16
|
167 o->complete(*owner, ec, 0);
|
Chris@16
|
168 }
|
Chris@16
|
169 }
|
Chris@16
|
170 }
|
Chris@16
|
171
|
Chris@16
|
172 } // namespace detail
|
Chris@16
|
173 } // namespace asio
|
Chris@16
|
174 } // namespace boost
|
Chris@16
|
175
|
Chris@16
|
176 #include <boost/asio/detail/pop_options.hpp>
|
Chris@16
|
177
|
Chris@16
|
178 #endif // BOOST_ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP
|