]>
Commit | Line | Data |
---|---|---|
b32b8144 FG |
1 | // |
2 | // impl/thread_pool.hpp | |
3 | // ~~~~~~~~~~~~~~~~~~~~ | |
4 | // | |
1e59de90 | 5 | // Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
b32b8144 FG |
6 | // |
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) | |
9 | // | |
10 | ||
11 | #ifndef BOOST_ASIO_IMPL_THREAD_POOL_HPP | |
12 | #define BOOST_ASIO_IMPL_THREAD_POOL_HPP | |
13 | ||
14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |
15 | # pragma once | |
16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |
17 | ||
20effc67 TL |
18 | #include <boost/asio/detail/blocking_executor_op.hpp> |
19 | #include <boost/asio/detail/bulk_executor_op.hpp> | |
b32b8144 FG |
20 | #include <boost/asio/detail/executor_op.hpp> |
21 | #include <boost/asio/detail/fenced_block.hpp> | |
20effc67 | 22 | #include <boost/asio/detail/non_const_lvalue.hpp> |
b32b8144 FG |
23 | #include <boost/asio/detail/type_traits.hpp> |
24 | #include <boost/asio/execution_context.hpp> | |
25 | ||
26 | #include <boost/asio/detail/push_options.hpp> | |
27 | ||
28 | namespace boost { | |
29 | namespace asio { | |
30 | ||
31 | inline thread_pool::executor_type | |
32 | thread_pool::get_executor() BOOST_ASIO_NOEXCEPT | |
33 | { | |
34 | return executor_type(*this); | |
35 | } | |
36 | ||
20effc67 TL |
37 | inline thread_pool::executor_type |
38 | thread_pool::executor() BOOST_ASIO_NOEXCEPT | |
39 | { | |
40 | return executor_type(*this); | |
41 | } | |
42 | ||
43 | inline thread_pool::scheduler_type | |
44 | thread_pool::scheduler() BOOST_ASIO_NOEXCEPT | |
45 | { | |
46 | return scheduler_type(*this); | |
47 | } | |
48 | ||
49 | template <typename Allocator, unsigned int Bits> | |
50 | thread_pool::basic_executor_type<Allocator, Bits>& | |
51 | thread_pool::basic_executor_type<Allocator, Bits>::operator=( | |
52 | const basic_executor_type& other) BOOST_ASIO_NOEXCEPT | |
53 | { | |
54 | if (this != &other) | |
55 | { | |
56 | thread_pool* old_thread_pool = pool_; | |
57 | pool_ = other.pool_; | |
58 | allocator_ = other.allocator_; | |
59 | bits_ = other.bits_; | |
60 | if (Bits & outstanding_work_tracked) | |
61 | { | |
62 | if (pool_) | |
63 | pool_->scheduler_.work_started(); | |
64 | if (old_thread_pool) | |
65 | old_thread_pool->scheduler_.work_finished(); | |
66 | } | |
67 | } | |
68 | return *this; | |
69 | } | |
70 | ||
71 | #if defined(BOOST_ASIO_HAS_MOVE) | |
72 | template <typename Allocator, unsigned int Bits> | |
73 | thread_pool::basic_executor_type<Allocator, Bits>& | |
74 | thread_pool::basic_executor_type<Allocator, Bits>::operator=( | |
75 | basic_executor_type&& other) BOOST_ASIO_NOEXCEPT | |
76 | { | |
77 | if (this != &other) | |
78 | { | |
1e59de90 | 79 | thread_pool* old_thread_pool = pool_; |
20effc67 TL |
80 | pool_ = other.pool_; |
81 | allocator_ = std::move(other.allocator_); | |
82 | bits_ = other.bits_; | |
83 | if (Bits & outstanding_work_tracked) | |
1e59de90 | 84 | { |
20effc67 | 85 | other.pool_ = 0; |
1e59de90 TL |
86 | if (old_thread_pool) |
87 | old_thread_pool->scheduler_.work_finished(); | |
88 | } | |
20effc67 TL |
89 | } |
90 | return *this; | |
91 | } | |
92 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
93 | ||
94 | template <typename Allocator, unsigned int Bits> | |
95 | inline bool thread_pool::basic_executor_type<Allocator, | |
96 | Bits>::running_in_this_thread() const BOOST_ASIO_NOEXCEPT | |
97 | { | |
98 | return pool_->scheduler_.can_dispatch(); | |
99 | } | |
100 | ||
101 | template <typename Allocator, unsigned int Bits> | |
102 | template <typename Function> | |
103 | void thread_pool::basic_executor_type<Allocator, | |
104 | Bits>::do_execute(BOOST_ASIO_MOVE_ARG(Function) f, false_type) const | |
105 | { | |
106 | typedef typename decay<Function>::type function_type; | |
107 | ||
108 | // Invoke immediately if the blocking.possibly property is enabled and we are | |
109 | // already inside the thread pool. | |
110 | if ((bits_ & blocking_never) == 0 && pool_->scheduler_.can_dispatch()) | |
111 | { | |
112 | // Make a local, non-const copy of the function. | |
113 | function_type tmp(BOOST_ASIO_MOVE_CAST(Function)(f)); | |
114 | ||
115 | #if defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR) \ | |
116 | && !defined(BOOST_ASIO_NO_EXCEPTIONS) | |
117 | try | |
118 | { | |
119 | #endif // defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR) | |
120 | // && !defined(BOOST_ASIO_NO_EXCEPTIONS) | |
121 | detail::fenced_block b(detail::fenced_block::full); | |
122 | boost_asio_handler_invoke_helpers::invoke(tmp, tmp); | |
123 | return; | |
124 | #if defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR) \ | |
125 | && !defined(BOOST_ASIO_NO_EXCEPTIONS) | |
126 | } | |
127 | catch (...) | |
128 | { | |
129 | pool_->scheduler_.capture_current_exception(); | |
130 | return; | |
131 | } | |
132 | #endif // defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR) | |
133 | // && !defined(BOOST_ASIO_NO_EXCEPTIONS) | |
134 | } | |
135 | ||
136 | // Allocate and construct an operation to wrap the function. | |
137 | typedef detail::executor_op<function_type, Allocator> op; | |
138 | typename op::ptr p = { detail::addressof(allocator_), | |
139 | op::ptr::allocate(allocator_), 0 }; | |
140 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(f), allocator_); | |
141 | ||
142 | if ((bits_ & relationship_continuation) != 0) | |
143 | { | |
144 | BOOST_ASIO_HANDLER_CREATION((*pool_, *p.p, | |
145 | "thread_pool", pool_, 0, "execute(blk=never,rel=cont)")); | |
146 | } | |
147 | else | |
148 | { | |
149 | BOOST_ASIO_HANDLER_CREATION((*pool_, *p.p, | |
150 | "thread_pool", pool_, 0, "execute(blk=never,rel=fork)")); | |
151 | } | |
152 | ||
153 | pool_->scheduler_.post_immediate_completion(p.p, | |
154 | (bits_ & relationship_continuation) != 0); | |
155 | p.v = p.p = 0; | |
156 | } | |
157 | ||
158 | template <typename Allocator, unsigned int Bits> | |
159 | template <typename Function> | |
160 | void thread_pool::basic_executor_type<Allocator, | |
161 | Bits>::do_execute(BOOST_ASIO_MOVE_ARG(Function) f, true_type) const | |
b32b8144 | 162 | { |
20effc67 TL |
163 | // Obtain a non-const instance of the function. |
164 | detail::non_const_lvalue<Function> f2(f); | |
165 | ||
166 | // Invoke immediately if we are already inside the thread pool. | |
167 | if (pool_->scheduler_.can_dispatch()) | |
168 | { | |
169 | #if !defined(BOOST_ASIO_NO_EXCEPTIONS) | |
170 | try | |
171 | { | |
172 | #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) | |
173 | detail::fenced_block b(detail::fenced_block::full); | |
174 | boost_asio_handler_invoke_helpers::invoke(f2.value, f2.value); | |
175 | return; | |
176 | #if !defined(BOOST_ASIO_NO_EXCEPTIONS) | |
177 | } | |
178 | catch (...) | |
179 | { | |
180 | std::terminate(); | |
181 | } | |
182 | #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) | |
183 | } | |
184 | ||
185 | // Construct an operation to wrap the function. | |
186 | typedef typename decay<Function>::type function_type; | |
187 | detail::blocking_executor_op<function_type> op(f2.value); | |
188 | ||
189 | BOOST_ASIO_HANDLER_CREATION((*pool_, op, | |
190 | "thread_pool", pool_, 0, "execute(blk=always)")); | |
191 | ||
192 | pool_->scheduler_.post_immediate_completion(&op, false); | |
193 | op.wait(); | |
b32b8144 FG |
194 | } |
195 | ||
20effc67 TL |
196 | template <typename Allocator, unsigned int Bits> |
197 | template <typename Function> | |
198 | void thread_pool::basic_executor_type<Allocator, Bits>::do_bulk_execute( | |
199 | BOOST_ASIO_MOVE_ARG(Function) f, std::size_t n, false_type) const | |
b32b8144 | 200 | { |
20effc67 TL |
201 | typedef typename decay<Function>::type function_type; |
202 | typedef detail::bulk_executor_op<function_type, Allocator> op; | |
203 | ||
204 | // Allocate and construct operations to wrap the function. | |
205 | detail::op_queue<detail::scheduler_operation> ops; | |
206 | for (std::size_t i = 0; i < n; ++i) | |
207 | { | |
208 | typename op::ptr p = { detail::addressof(allocator_), | |
209 | op::ptr::allocate(allocator_), 0 }; | |
210 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(f), allocator_, i); | |
211 | ops.push(p.p); | |
212 | ||
213 | if ((bits_ & relationship_continuation) != 0) | |
214 | { | |
215 | BOOST_ASIO_HANDLER_CREATION((*pool_, *p.p, | |
216 | "thread_pool", pool_, 0, "bulk_execute(blk=never,rel=cont)")); | |
217 | } | |
218 | else | |
219 | { | |
220 | BOOST_ASIO_HANDLER_CREATION((*pool_, *p.p, | |
221 | "thread_pool", pool_, 0, "bulk)execute(blk=never,rel=fork)")); | |
222 | } | |
223 | ||
224 | p.v = p.p = 0; | |
225 | } | |
226 | ||
227 | pool_->scheduler_.post_immediate_completions(n, | |
228 | ops, (bits_ & relationship_continuation) != 0); | |
b32b8144 FG |
229 | } |
230 | ||
20effc67 TL |
231 | template <typename Function> |
232 | struct thread_pool_always_blocking_function_adapter | |
b32b8144 | 233 | { |
20effc67 TL |
234 | typename decay<Function>::type* f; |
235 | std::size_t n; | |
236 | ||
237 | void operator()() | |
238 | { | |
239 | for (std::size_t i = 0; i < n; ++i) | |
240 | { | |
241 | (*f)(i); | |
242 | } | |
243 | } | |
244 | }; | |
245 | ||
246 | template <typename Allocator, unsigned int Bits> | |
247 | template <typename Function> | |
248 | void thread_pool::basic_executor_type<Allocator, Bits>::do_bulk_execute( | |
249 | BOOST_ASIO_MOVE_ARG(Function) f, std::size_t n, true_type) const | |
250 | { | |
251 | // Obtain a non-const instance of the function. | |
252 | detail::non_const_lvalue<Function> f2(f); | |
253 | ||
254 | thread_pool_always_blocking_function_adapter<Function> | |
255 | adapter = { detail::addressof(f2.value), n }; | |
256 | ||
257 | this->do_execute(adapter, true_type()); | |
258 | } | |
259 | ||
260 | #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) | |
261 | template <typename Allocator, unsigned int Bits> | |
262 | inline thread_pool& thread_pool::basic_executor_type< | |
263 | Allocator, Bits>::context() const BOOST_ASIO_NOEXCEPT | |
264 | { | |
265 | return *pool_; | |
266 | } | |
267 | ||
268 | template <typename Allocator, unsigned int Bits> | |
269 | inline void thread_pool::basic_executor_type<Allocator, | |
270 | Bits>::on_work_started() const BOOST_ASIO_NOEXCEPT | |
271 | { | |
272 | pool_->scheduler_.work_started(); | |
b32b8144 FG |
273 | } |
274 | ||
20effc67 TL |
275 | template <typename Allocator, unsigned int Bits> |
276 | inline void thread_pool::basic_executor_type<Allocator, | |
277 | Bits>::on_work_finished() const BOOST_ASIO_NOEXCEPT | |
278 | { | |
279 | pool_->scheduler_.work_finished(); | |
280 | } | |
281 | ||
282 | template <typename Allocator, unsigned int Bits> | |
283 | template <typename Function, typename OtherAllocator> | |
284 | void thread_pool::basic_executor_type<Allocator, Bits>::dispatch( | |
285 | BOOST_ASIO_MOVE_ARG(Function) f, const OtherAllocator& a) const | |
b32b8144 FG |
286 | { |
287 | typedef typename decay<Function>::type function_type; | |
288 | ||
289 | // Invoke immediately if we are already inside the thread pool. | |
20effc67 | 290 | if (pool_->scheduler_.can_dispatch()) |
b32b8144 FG |
291 | { |
292 | // Make a local, non-const copy of the function. | |
293 | function_type tmp(BOOST_ASIO_MOVE_CAST(Function)(f)); | |
294 | ||
295 | detail::fenced_block b(detail::fenced_block::full); | |
296 | boost_asio_handler_invoke_helpers::invoke(tmp, tmp); | |
297 | return; | |
298 | } | |
299 | ||
300 | // Allocate and construct an operation to wrap the function. | |
20effc67 | 301 | typedef detail::executor_op<function_type, OtherAllocator> op; |
b32b8144 FG |
302 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
303 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(f), a); | |
304 | ||
20effc67 TL |
305 | BOOST_ASIO_HANDLER_CREATION((*pool_, *p.p, |
306 | "thread_pool", pool_, 0, "dispatch")); | |
b32b8144 | 307 | |
20effc67 | 308 | pool_->scheduler_.post_immediate_completion(p.p, false); |
b32b8144 FG |
309 | p.v = p.p = 0; |
310 | } | |
311 | ||
20effc67 TL |
312 | template <typename Allocator, unsigned int Bits> |
313 | template <typename Function, typename OtherAllocator> | |
314 | void thread_pool::basic_executor_type<Allocator, Bits>::post( | |
315 | BOOST_ASIO_MOVE_ARG(Function) f, const OtherAllocator& a) const | |
b32b8144 FG |
316 | { |
317 | typedef typename decay<Function>::type function_type; | |
318 | ||
319 | // Allocate and construct an operation to wrap the function. | |
20effc67 | 320 | typedef detail::executor_op<function_type, OtherAllocator> op; |
b32b8144 FG |
321 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
322 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(f), a); | |
323 | ||
20effc67 TL |
324 | BOOST_ASIO_HANDLER_CREATION((*pool_, *p.p, |
325 | "thread_pool", pool_, 0, "post")); | |
b32b8144 | 326 | |
20effc67 | 327 | pool_->scheduler_.post_immediate_completion(p.p, false); |
b32b8144 FG |
328 | p.v = p.p = 0; |
329 | } | |
330 | ||
20effc67 TL |
331 | template <typename Allocator, unsigned int Bits> |
332 | template <typename Function, typename OtherAllocator> | |
333 | void thread_pool::basic_executor_type<Allocator, Bits>::defer( | |
334 | BOOST_ASIO_MOVE_ARG(Function) f, const OtherAllocator& a) const | |
b32b8144 FG |
335 | { |
336 | typedef typename decay<Function>::type function_type; | |
337 | ||
338 | // Allocate and construct an operation to wrap the function. | |
20effc67 | 339 | typedef detail::executor_op<function_type, OtherAllocator> op; |
b32b8144 FG |
340 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
341 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(f), a); | |
342 | ||
20effc67 TL |
343 | BOOST_ASIO_HANDLER_CREATION((*pool_, *p.p, |
344 | "thread_pool", pool_, 0, "defer")); | |
b32b8144 | 345 | |
20effc67 | 346 | pool_->scheduler_.post_immediate_completion(p.p, true); |
b32b8144 FG |
347 | p.v = p.p = 0; |
348 | } | |
20effc67 | 349 | #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
b32b8144 FG |
350 | |
351 | } // namespace asio | |
352 | } // namespace boost | |
353 | ||
354 | #include <boost/asio/detail/pop_options.hpp> | |
355 | ||
356 | #endif // BOOST_ASIO_IMPL_THREAD_POOL_HPP |