1 //
2 // thread_pool.cpp
3 // ~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
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 // Disable autolinking for unit tests.
12 #if !defined(BOOST_ALL_NO_LIB)
13 #define BOOST_ALL_NO_LIB 1
14 #endif // !defined(BOOST_ALL_NO_LIB)
15
16 // Test that header file is self-contained.
17 #include <boost/asio/thread_pool.hpp>
18
19 #include <boost/asio/dispatch.hpp>
20 #include <boost/asio/post.hpp>
21 #include "unit_test.hpp"
22
23 #if defined(BOOST_ASIO_HAS_BOOST_BIND)
24 # include <boost/bind/bind.hpp>
25 #else // defined(BOOST_ASIO_HAS_BOOST_BIND)
26 # include <functional>
27 #endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
28
29 using namespace boost::asio;
30
31 #if defined(BOOST_ASIO_HAS_BOOST_BIND)
32 namespace bindns = boost;
33 #else // defined(BOOST_ASIO_HAS_BOOST_BIND)
34 namespace bindns = std;
35 #endif
36
increment(int * count)37 void increment(int* count)
38 {
39 ++(*count);
40 }
41
decrement_to_zero(thread_pool * pool,int * count)42 void decrement_to_zero(thread_pool* pool, int* count)
43 {
44 if (*count > 0)
45 {
46 --(*count);
47
48 int before_value = *count;
49 boost::asio::post(*pool, bindns::bind(decrement_to_zero, pool, count));
50
51 // Handler execution cannot nest, so count value should remain unchanged.
52 BOOST_ASIO_CHECK(*count == before_value);
53 }
54 }
55
nested_decrement_to_zero(thread_pool * pool,int * count)56 void nested_decrement_to_zero(thread_pool* pool, int* count)
57 {
58 if (*count > 0)
59 {
60 --(*count);
61
62 boost::asio::dispatch(*pool,
63 bindns::bind(nested_decrement_to_zero, pool, count));
64
65 // Handler execution is nested, so count value should now be zero.
66 BOOST_ASIO_CHECK(*count == 0);
67 }
68 }
69
thread_pool_test()70 void thread_pool_test()
71 {
72 thread_pool pool(1);
73
74 int count1 = 0;
75 boost::asio::post(pool, bindns::bind(increment, &count1));
76
77 int count2 = 10;
78 boost::asio::post(pool, bindns::bind(decrement_to_zero, &pool, &count2));
79
80 int count3 = 10;
81 boost::asio::post(pool, bindns::bind(nested_decrement_to_zero, &pool, &count3));
82
83 pool.wait();
84
85 BOOST_ASIO_CHECK(count1 == 1);
86 BOOST_ASIO_CHECK(count2 == 0);
87 BOOST_ASIO_CHECK(count3 == 0);
88 }
89
90 class test_service : public boost::asio::execution_context::service
91 {
92 public:
93 typedef test_service key_type;
94
test_service(boost::asio::execution_context & ctx)95 test_service(boost::asio::execution_context& ctx)
96 : boost::asio::execution_context::service(ctx)
97 {
98 }
99
100 private:
shutdown()101 virtual void shutdown() {}
102 };
103
thread_pool_service_test()104 void thread_pool_service_test()
105 {
106 boost::asio::thread_pool pool1(1);
107 boost::asio::thread_pool pool2(1);
108 boost::asio::thread_pool pool3(1);
109
110 // Implicit service registration.
111
112 boost::asio::use_service<test_service>(pool1);
113
114 BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(pool1));
115
116 test_service* svc1 = new test_service(pool1);
117 try
118 {
119 boost::asio::add_service(pool1, svc1);
120 BOOST_ASIO_ERROR("add_service did not throw");
121 }
122 catch (boost::asio::service_already_exists&)
123 {
124 }
125 delete svc1;
126
127 // Explicit service registration.
128
129 test_service& svc2 = boost::asio::make_service<test_service>(pool2);
130
131 BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(pool2));
132 BOOST_ASIO_CHECK(&boost::asio::use_service<test_service>(pool2) == &svc2);
133
134 test_service* svc3 = new test_service(pool2);
135 try
136 {
137 boost::asio::add_service(pool2, svc3);
138 BOOST_ASIO_ERROR("add_service did not throw");
139 }
140 catch (boost::asio::service_already_exists&)
141 {
142 }
143 delete svc3;
144
145 // Explicit registration with invalid owner.
146
147 test_service* svc4 = new test_service(pool2);
148 try
149 {
150 boost::asio::add_service(pool3, svc4);
151 BOOST_ASIO_ERROR("add_service did not throw");
152 }
153 catch (boost::asio::invalid_service_owner&)
154 {
155 }
156 delete svc4;
157
158 BOOST_ASIO_CHECK(!boost::asio::has_service<test_service>(pool3));
159 }
160
thread_pool_executor_query_test()161 void thread_pool_executor_query_test()
162 {
163 thread_pool pool(1);
164
165 BOOST_ASIO_CHECK(
166 &boost::asio::query(pool.executor(),
167 boost::asio::execution::context)
168 == &pool);
169
170 BOOST_ASIO_CHECK(
171 boost::asio::query(pool.executor(),
172 boost::asio::execution::blocking)
173 == boost::asio::execution::blocking.possibly);
174
175 BOOST_ASIO_CHECK(
176 boost::asio::query(pool.executor(),
177 boost::asio::execution::blocking.possibly)
178 == boost::asio::execution::blocking.possibly);
179
180 BOOST_ASIO_CHECK(
181 boost::asio::query(pool.executor(),
182 boost::asio::execution::outstanding_work)
183 == boost::asio::execution::outstanding_work.untracked);
184
185 BOOST_ASIO_CHECK(
186 boost::asio::query(pool.executor(),
187 boost::asio::execution::outstanding_work.untracked)
188 == boost::asio::execution::outstanding_work.untracked);
189
190 BOOST_ASIO_CHECK(
191 boost::asio::query(pool.executor(),
192 boost::asio::execution::relationship)
193 == boost::asio::execution::relationship.fork);
194
195 BOOST_ASIO_CHECK(
196 boost::asio::query(pool.executor(),
197 boost::asio::execution::relationship.fork)
198 == boost::asio::execution::relationship.fork);
199
200 BOOST_ASIO_CHECK(
201 boost::asio::query(pool.executor(),
202 boost::asio::execution::bulk_guarantee)
203 == boost::asio::execution::bulk_guarantee.parallel);
204
205 BOOST_ASIO_CHECK(
206 boost::asio::query(pool.executor(),
207 boost::asio::execution::mapping)
208 == boost::asio::execution::mapping.thread);
209
210 BOOST_ASIO_CHECK(
211 boost::asio::query(pool.executor(),
212 boost::asio::execution::allocator)
213 == std::allocator<void>());
214
215 BOOST_ASIO_CHECK(
216 boost::asio::query(pool.executor(),
217 boost::asio::execution::occupancy)
218 == 1);
219 }
220
thread_pool_executor_execute_test()221 void thread_pool_executor_execute_test()
222 {
223 int count = 0;
224 thread_pool pool(1);
225
226 boost::asio::execution::execute(pool.executor(),
227 bindns::bind(increment, &count));
228
229 boost::asio::execution::execute(
230 boost::asio::require(pool.executor(),
231 boost::asio::execution::blocking.possibly),
232 bindns::bind(increment, &count));
233
234 boost::asio::execution::execute(
235 boost::asio::require(pool.executor(),
236 boost::asio::execution::blocking.always),
237 bindns::bind(increment, &count));
238
239 boost::asio::execution::execute(
240 boost::asio::require(pool.executor(),
241 boost::asio::execution::blocking.never),
242 bindns::bind(increment, &count));
243
244 boost::asio::execution::execute(
245 boost::asio::require(pool.executor(),
246 boost::asio::execution::blocking.never,
247 boost::asio::execution::outstanding_work.tracked),
248 bindns::bind(increment, &count));
249
250 boost::asio::execution::execute(
251 boost::asio::require(pool.executor(),
252 boost::asio::execution::blocking.never,
253 boost::asio::execution::outstanding_work.untracked),
254 bindns::bind(increment, &count));
255
256 boost::asio::execution::execute(
257 boost::asio::require(pool.executor(),
258 boost::asio::execution::blocking.never,
259 boost::asio::execution::outstanding_work.untracked,
260 boost::asio::execution::relationship.fork),
261 bindns::bind(increment, &count));
262
263 boost::asio::execution::execute(
264 boost::asio::require(pool.executor(),
265 boost::asio::execution::blocking.never,
266 boost::asio::execution::outstanding_work.untracked,
267 boost::asio::execution::relationship.continuation),
268 bindns::bind(increment, &count));
269
270 boost::asio::execution::execute(
271 boost::asio::prefer(
272 boost::asio::require(pool.executor(),
273 boost::asio::execution::blocking.never,
274 boost::asio::execution::outstanding_work.untracked,
275 boost::asio::execution::relationship.continuation),
276 boost::asio::execution::allocator(std::allocator<void>())),
277 bindns::bind(increment, &count));
278
279 boost::asio::execution::execute(
280 boost::asio::prefer(
281 boost::asio::require(pool.executor(),
282 boost::asio::execution::blocking.never,
283 boost::asio::execution::outstanding_work.untracked,
284 boost::asio::execution::relationship.continuation),
285 boost::asio::execution::allocator),
286 bindns::bind(increment, &count));
287
288 pool.wait();
289
290 BOOST_ASIO_CHECK(count == 10);
291 }
292
293 struct receiver
294 {
295 int* count_;
296
receiverreceiver297 receiver(int* count)
298 : count_(count)
299 {
300 }
301
receiverreceiver302 receiver(const receiver& other) BOOST_ASIO_NOEXCEPT
303 : count_(other.count_)
304 {
305 }
306
307 #if defined(BOOST_ASIO_HAS_MOVE)
receiverreceiver308 receiver(receiver&& other) BOOST_ASIO_NOEXCEPT
309 : count_(other.count_)
310 {
311 other.count_ = 0;
312 }
313 #endif // defined(BOOST_ASIO_HAS_MOVE)
314
set_valuereceiver315 void set_value() BOOST_ASIO_NOEXCEPT
316 {
317 ++(*count_);
318 }
319
320 template <typename E>
set_errorreceiver321 void set_error(BOOST_ASIO_MOVE_ARG(E) e) BOOST_ASIO_NOEXCEPT
322 {
323 (void)e;
324 }
325
set_donereceiver326 void set_done() BOOST_ASIO_NOEXCEPT
327 {
328 }
329 };
330
331 namespace boost {
332 namespace asio {
333 namespace traits {
334
335 #if !defined(BOOST_ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT)
336
337 template <>
338 struct set_value_member<receiver, void()>
339 {
340 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
341 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
342 typedef void result_type;
343 };
344
345 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT)
346
347 #if !defined(BOOST_ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT)
348
349 template <typename E>
350 struct set_error_member<receiver, E>
351 {
352 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
353 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
354 typedef void result_type;
355 };
356
357 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT)
358
359 #if !defined(BOOST_ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT)
360
361 template <>
362 struct set_done_member<receiver>
363 {
364 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
365 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
366 typedef void result_type;
367 };
368
369 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT)
370
371 } // namespace traits
372 } // namespace asio
373 } // namespace boost
374
thread_pool_scheduler_test()375 void thread_pool_scheduler_test()
376 {
377 int count = 0;
378 receiver r(&count);
379 thread_pool pool(1);
380
381 boost::asio::execution::submit(
382 boost::asio::execution::schedule(pool.scheduler()), r);
383
384 boost::asio::execution::submit(
385 boost::asio::require(
386 boost::asio::execution::schedule(pool.executor()),
387 boost::asio::execution::blocking.possibly), r);
388
389 boost::asio::execution::submit(
390 boost::asio::require(
391 boost::asio::execution::schedule(pool.executor()),
392 boost::asio::execution::blocking.always), r);
393
394 boost::asio::execution::submit(
395 boost::asio::require(
396 boost::asio::execution::schedule(pool.executor()),
397 boost::asio::execution::blocking.never), r);
398
399 boost::asio::execution::submit(
400 boost::asio::require(
401 boost::asio::execution::schedule(pool.executor()),
402 boost::asio::execution::blocking.never,
403 boost::asio::execution::outstanding_work.tracked), r);
404
405 boost::asio::execution::submit(
406 boost::asio::require(
407 boost::asio::execution::schedule(pool.executor()),
408 boost::asio::execution::blocking.never,
409 boost::asio::execution::outstanding_work.untracked), r);
410
411 boost::asio::execution::submit(
412 boost::asio::require(
413 boost::asio::execution::schedule(pool.executor()),
414 boost::asio::execution::blocking.never,
415 boost::asio::execution::outstanding_work.untracked,
416 boost::asio::execution::relationship.fork), r);
417
418 boost::asio::execution::submit(
419 boost::asio::require(
420 boost::asio::execution::schedule(pool.executor()),
421 boost::asio::execution::blocking.never,
422 boost::asio::execution::outstanding_work.untracked,
423 boost::asio::execution::relationship.continuation), r);
424
425 boost::asio::execution::submit(
426 boost::asio::prefer(
427 boost::asio::require(
428 boost::asio::execution::schedule(pool.executor()),
429 boost::asio::execution::blocking.never,
430 boost::asio::execution::outstanding_work.untracked,
431 boost::asio::execution::relationship.continuation),
432 boost::asio::execution::allocator(std::allocator<void>())), r);
433
434 boost::asio::execution::submit(
435 boost::asio::prefer(
436 boost::asio::require(
437 boost::asio::execution::schedule(pool.executor()),
438 boost::asio::execution::blocking.never,
439 boost::asio::execution::outstanding_work.untracked,
440 boost::asio::execution::relationship.continuation),
441 boost::asio::execution::allocator), r);
442
443 pool.wait();
444
445 BOOST_ASIO_CHECK(count == 10);
446 }
447
thread_pool_executor_bulk_execute_test()448 void thread_pool_executor_bulk_execute_test()
449 {
450 int count = 0;
451 thread_pool pool(1);
452
453 pool.executor().bulk_execute(
454 bindns::bind(increment, &count), 2);
455
456 boost::asio::require(pool.executor(),
457 boost::asio::execution::blocking.possibly).bulk_execute(
458 bindns::bind(increment, &count), 2);
459
460 boost::asio::require(pool.executor(),
461 boost::asio::execution::blocking.always).bulk_execute(
462 bindns::bind(increment, &count), 2);
463
464 boost::asio::require(pool.executor(),
465 boost::asio::execution::blocking.never).bulk_execute(
466 bindns::bind(increment, &count), 2);
467
468 boost::asio::require(pool.executor(),
469 boost::asio::execution::blocking.never,
470 boost::asio::execution::outstanding_work.tracked).bulk_execute(
471 bindns::bind(increment, &count), 2);
472
473 boost::asio::require(pool.executor(),
474 boost::asio::execution::blocking.never,
475 boost::asio::execution::outstanding_work.untracked).bulk_execute(
476 bindns::bind(increment, &count), 2);
477
478 boost::asio::require(pool.executor(),
479 boost::asio::execution::blocking.never,
480 boost::asio::execution::outstanding_work.untracked,
481 boost::asio::execution::relationship.fork).bulk_execute(
482 bindns::bind(increment, &count), 2);
483
484 boost::asio::require(pool.executor(),
485 boost::asio::execution::blocking.never,
486 boost::asio::execution::outstanding_work.untracked,
487 boost::asio::execution::relationship.continuation).bulk_execute(
488 bindns::bind(increment, &count), 2);
489
490 boost::asio::prefer(
491 boost::asio::require(pool.executor(),
492 boost::asio::execution::blocking.never,
493 boost::asio::execution::outstanding_work.untracked,
494 boost::asio::execution::relationship.continuation),
495 boost::asio::execution::allocator(std::allocator<void>())).bulk_execute(
496 bindns::bind(increment, &count), 2);
497
498 boost::asio::prefer(
499 boost::asio::require(pool.executor(),
500 boost::asio::execution::blocking.never,
501 boost::asio::execution::outstanding_work.untracked,
502 boost::asio::execution::relationship.continuation),
503 boost::asio::execution::allocator).bulk_execute(
504 bindns::bind(increment, &count), 2);
505
506 pool.wait();
507
508 BOOST_ASIO_CHECK(count == 20);
509 }
510
511 BOOST_ASIO_TEST_SUITE
512 (
513 "thread_pool",
514 BOOST_ASIO_TEST_CASE(thread_pool_test)
515 BOOST_ASIO_TEST_CASE(thread_pool_service_test)
516 BOOST_ASIO_TEST_CASE(thread_pool_executor_query_test)
517 BOOST_ASIO_TEST_CASE(thread_pool_executor_execute_test)
518 BOOST_ASIO_TEST_CASE(thread_pool_executor_bulk_execute_test)
519 BOOST_ASIO_TEST_CASE(thread_pool_scheduler_test)
520 )
521