1 //
2 // thread_pool.cpp
3 // ~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 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 #if defined(BOOST_ASIO_NO_TYPEID)
94 static boost::asio::execution_context::id id;
95 #endif // defined(BOOST_ASIO_NO_TYPEID)
96
97 typedef test_service key_type;
98
test_service(boost::asio::execution_context & ctx)99 test_service(boost::asio::execution_context& ctx)
100 : boost::asio::execution_context::service(ctx)
101 {
102 }
103
104 private:
shutdown()105 virtual void shutdown() {}
106 };
107
108 #if defined(BOOST_ASIO_NO_TYPEID)
109 boost::asio::execution_context::id test_service::id;
110 #endif // defined(BOOST_ASIO_NO_TYPEID)
111
thread_pool_service_test()112 void thread_pool_service_test()
113 {
114 boost::asio::thread_pool pool1(1);
115 boost::asio::thread_pool pool2(1);
116 boost::asio::thread_pool pool3(1);
117
118 // Implicit service registration.
119
120 boost::asio::use_service<test_service>(pool1);
121
122 BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(pool1));
123
124 test_service* svc1 = new test_service(pool1);
125 try
126 {
127 boost::asio::add_service(pool1, svc1);
128 BOOST_ASIO_ERROR("add_service did not throw");
129 }
130 catch (boost::asio::service_already_exists&)
131 {
132 }
133 delete svc1;
134
135 // Explicit service registration.
136
137 test_service& svc2 = boost::asio::make_service<test_service>(pool2);
138
139 BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(pool2));
140 BOOST_ASIO_CHECK(&boost::asio::use_service<test_service>(pool2) == &svc2);
141
142 test_service* svc3 = new test_service(pool2);
143 try
144 {
145 boost::asio::add_service(pool2, svc3);
146 BOOST_ASIO_ERROR("add_service did not throw");
147 }
148 catch (boost::asio::service_already_exists&)
149 {
150 }
151 delete svc3;
152
153 // Explicit registration with invalid owner.
154
155 test_service* svc4 = new test_service(pool2);
156 try
157 {
158 boost::asio::add_service(pool3, svc4);
159 BOOST_ASIO_ERROR("add_service did not throw");
160 }
161 catch (boost::asio::invalid_service_owner&)
162 {
163 }
164 delete svc4;
165
166 BOOST_ASIO_CHECK(!boost::asio::has_service<test_service>(pool3));
167 }
168
thread_pool_executor_query_test()169 void thread_pool_executor_query_test()
170 {
171 thread_pool pool(1);
172
173 BOOST_ASIO_CHECK(
174 &boost::asio::query(pool.executor(),
175 boost::asio::execution::context)
176 == &pool);
177
178 BOOST_ASIO_CHECK(
179 boost::asio::query(pool.executor(),
180 boost::asio::execution::blocking)
181 == boost::asio::execution::blocking.possibly);
182
183 BOOST_ASIO_CHECK(
184 boost::asio::query(pool.executor(),
185 boost::asio::execution::blocking.possibly)
186 == boost::asio::execution::blocking.possibly);
187
188 BOOST_ASIO_CHECK(
189 boost::asio::query(pool.executor(),
190 boost::asio::execution::outstanding_work)
191 == boost::asio::execution::outstanding_work.untracked);
192
193 BOOST_ASIO_CHECK(
194 boost::asio::query(pool.executor(),
195 boost::asio::execution::outstanding_work.untracked)
196 == boost::asio::execution::outstanding_work.untracked);
197
198 BOOST_ASIO_CHECK(
199 boost::asio::query(pool.executor(),
200 boost::asio::execution::relationship)
201 == boost::asio::execution::relationship.fork);
202
203 BOOST_ASIO_CHECK(
204 boost::asio::query(pool.executor(),
205 boost::asio::execution::relationship.fork)
206 == boost::asio::execution::relationship.fork);
207
208 BOOST_ASIO_CHECK(
209 boost::asio::query(pool.executor(),
210 boost::asio::execution::bulk_guarantee)
211 == boost::asio::execution::bulk_guarantee.parallel);
212
213 BOOST_ASIO_CHECK(
214 boost::asio::query(pool.executor(),
215 boost::asio::execution::mapping)
216 == boost::asio::execution::mapping.thread);
217
218 BOOST_ASIO_CHECK(
219 boost::asio::query(pool.executor(),
220 boost::asio::execution::allocator)
221 == std::allocator<void>());
222
223 BOOST_ASIO_CHECK(
224 boost::asio::query(pool.executor(),
225 boost::asio::execution::occupancy)
226 == 1);
227 }
228
thread_pool_executor_execute_test()229 void thread_pool_executor_execute_test()
230 {
231 int count = 0;
232 thread_pool pool(1);
233
234 boost::asio::execution::execute(pool.executor(),
235 bindns::bind(increment, &count));
236
237 boost::asio::execution::execute(
238 boost::asio::require(pool.executor(),
239 boost::asio::execution::blocking.possibly),
240 bindns::bind(increment, &count));
241
242 boost::asio::execution::execute(
243 boost::asio::require(pool.executor(),
244 boost::asio::execution::blocking.always),
245 bindns::bind(increment, &count));
246
247 boost::asio::execution::execute(
248 boost::asio::require(pool.executor(),
249 boost::asio::execution::blocking.never),
250 bindns::bind(increment, &count));
251
252 boost::asio::execution::execute(
253 boost::asio::require(pool.executor(),
254 boost::asio::execution::blocking.never,
255 boost::asio::execution::outstanding_work.tracked),
256 bindns::bind(increment, &count));
257
258 boost::asio::execution::execute(
259 boost::asio::require(pool.executor(),
260 boost::asio::execution::blocking.never,
261 boost::asio::execution::outstanding_work.untracked),
262 bindns::bind(increment, &count));
263
264 boost::asio::execution::execute(
265 boost::asio::require(pool.executor(),
266 boost::asio::execution::blocking.never,
267 boost::asio::execution::outstanding_work.untracked,
268 boost::asio::execution::relationship.fork),
269 bindns::bind(increment, &count));
270
271 boost::asio::execution::execute(
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 bindns::bind(increment, &count));
277
278 boost::asio::execution::execute(
279 boost::asio::prefer(
280 boost::asio::require(pool.executor(),
281 boost::asio::execution::blocking.never,
282 boost::asio::execution::outstanding_work.untracked,
283 boost::asio::execution::relationship.continuation),
284 boost::asio::execution::allocator(std::allocator<void>())),
285 bindns::bind(increment, &count));
286
287 boost::asio::execution::execute(
288 boost::asio::prefer(
289 boost::asio::require(pool.executor(),
290 boost::asio::execution::blocking.never,
291 boost::asio::execution::outstanding_work.untracked,
292 boost::asio::execution::relationship.continuation),
293 boost::asio::execution::allocator),
294 bindns::bind(increment, &count));
295
296 pool.wait();
297
298 BOOST_ASIO_CHECK(count == 10);
299 }
300
301 struct receiver
302 {
303 int* count_;
304
receiverreceiver305 receiver(int* count)
306 : count_(count)
307 {
308 }
309
receiverreceiver310 receiver(const receiver& other) BOOST_ASIO_NOEXCEPT
311 : count_(other.count_)
312 {
313 }
314
315 #if defined(BOOST_ASIO_HAS_MOVE)
receiverreceiver316 receiver(receiver&& other) BOOST_ASIO_NOEXCEPT
317 : count_(other.count_)
318 {
319 other.count_ = 0;
320 }
321 #endif // defined(BOOST_ASIO_HAS_MOVE)
322
set_valuereceiver323 void set_value() BOOST_ASIO_NOEXCEPT
324 {
325 ++(*count_);
326 }
327
328 template <typename E>
set_errorreceiver329 void set_error(BOOST_ASIO_MOVE_ARG(E) e) BOOST_ASIO_NOEXCEPT
330 {
331 (void)e;
332 }
333
set_donereceiver334 void set_done() BOOST_ASIO_NOEXCEPT
335 {
336 }
337 };
338
339 namespace boost {
340 namespace asio {
341 namespace traits {
342
343 #if !defined(BOOST_ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT)
344
345 template <>
346 struct set_value_member<receiver, void()>
347 {
348 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
349 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
350 typedef void result_type;
351 };
352
353 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT)
354
355 #if !defined(BOOST_ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT)
356
357 template <typename E>
358 struct set_error_member<receiver, E>
359 {
360 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
361 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
362 typedef void result_type;
363 };
364
365 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT)
366
367 #if !defined(BOOST_ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT)
368
369 template <>
370 struct set_done_member<receiver>
371 {
372 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
373 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
374 typedef void result_type;
375 };
376
377 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT)
378
379 } // namespace traits
380 } // namespace asio
381 } // namespace boost
382
thread_pool_scheduler_test()383 void thread_pool_scheduler_test()
384 {
385 int count = 0;
386 receiver r(&count);
387 thread_pool pool(1);
388
389 boost::asio::execution::submit(
390 boost::asio::execution::schedule(pool.scheduler()), r);
391
392 boost::asio::execution::submit(
393 boost::asio::require(
394 boost::asio::execution::schedule(pool.executor()),
395 boost::asio::execution::blocking.possibly), r);
396
397 boost::asio::execution::submit(
398 boost::asio::require(
399 boost::asio::execution::schedule(pool.executor()),
400 boost::asio::execution::blocking.always), r);
401
402 boost::asio::execution::submit(
403 boost::asio::require(
404 boost::asio::execution::schedule(pool.executor()),
405 boost::asio::execution::blocking.never), r);
406
407 boost::asio::execution::submit(
408 boost::asio::require(
409 boost::asio::execution::schedule(pool.executor()),
410 boost::asio::execution::blocking.never,
411 boost::asio::execution::outstanding_work.tracked), r);
412
413 boost::asio::execution::submit(
414 boost::asio::require(
415 boost::asio::execution::schedule(pool.executor()),
416 boost::asio::execution::blocking.never,
417 boost::asio::execution::outstanding_work.untracked), r);
418
419 boost::asio::execution::submit(
420 boost::asio::require(
421 boost::asio::execution::schedule(pool.executor()),
422 boost::asio::execution::blocking.never,
423 boost::asio::execution::outstanding_work.untracked,
424 boost::asio::execution::relationship.fork), r);
425
426 boost::asio::execution::submit(
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), r);
432
433 boost::asio::execution::submit(
434 boost::asio::prefer(
435 boost::asio::require(
436 boost::asio::execution::schedule(pool.executor()),
437 boost::asio::execution::blocking.never,
438 boost::asio::execution::outstanding_work.untracked,
439 boost::asio::execution::relationship.continuation),
440 boost::asio::execution::allocator(std::allocator<void>())), r);
441
442 boost::asio::execution::submit(
443 boost::asio::prefer(
444 boost::asio::require(
445 boost::asio::execution::schedule(pool.executor()),
446 boost::asio::execution::blocking.never,
447 boost::asio::execution::outstanding_work.untracked,
448 boost::asio::execution::relationship.continuation),
449 boost::asio::execution::allocator), r);
450
451 pool.wait();
452
453 BOOST_ASIO_CHECK(count == 10);
454 }
455
thread_pool_executor_bulk_execute_test()456 void thread_pool_executor_bulk_execute_test()
457 {
458 int count = 0;
459 thread_pool pool(1);
460
461 pool.executor().bulk_execute(
462 bindns::bind(increment, &count), 2);
463
464 boost::asio::require(pool.executor(),
465 boost::asio::execution::blocking.possibly).bulk_execute(
466 bindns::bind(increment, &count), 2);
467
468 boost::asio::require(pool.executor(),
469 boost::asio::execution::blocking.always).bulk_execute(
470 bindns::bind(increment, &count), 2);
471
472 boost::asio::require(pool.executor(),
473 boost::asio::execution::blocking.never).bulk_execute(
474 bindns::bind(increment, &count), 2);
475
476 boost::asio::require(pool.executor(),
477 boost::asio::execution::blocking.never,
478 boost::asio::execution::outstanding_work.tracked).bulk_execute(
479 bindns::bind(increment, &count), 2);
480
481 boost::asio::require(pool.executor(),
482 boost::asio::execution::blocking.never,
483 boost::asio::execution::outstanding_work.untracked).bulk_execute(
484 bindns::bind(increment, &count), 2);
485
486 boost::asio::require(pool.executor(),
487 boost::asio::execution::blocking.never,
488 boost::asio::execution::outstanding_work.untracked,
489 boost::asio::execution::relationship.fork).bulk_execute(
490 bindns::bind(increment, &count), 2);
491
492 boost::asio::require(pool.executor(),
493 boost::asio::execution::blocking.never,
494 boost::asio::execution::outstanding_work.untracked,
495 boost::asio::execution::relationship.continuation).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(std::allocator<void>())).bulk_execute(
504 bindns::bind(increment, &count), 2);
505
506 boost::asio::prefer(
507 boost::asio::require(pool.executor(),
508 boost::asio::execution::blocking.never,
509 boost::asio::execution::outstanding_work.untracked,
510 boost::asio::execution::relationship.continuation),
511 boost::asio::execution::allocator).bulk_execute(
512 bindns::bind(increment, &count), 2);
513
514 pool.wait();
515
516 BOOST_ASIO_CHECK(count == 20);
517 }
518
519 BOOST_ASIO_TEST_SUITE
520 (
521 "thread_pool",
522 BOOST_ASIO_TEST_CASE(thread_pool_test)
523 BOOST_ASIO_TEST_CASE(thread_pool_service_test)
524 BOOST_ASIO_TEST_CASE(thread_pool_executor_query_test)
525 BOOST_ASIO_TEST_CASE(thread_pool_executor_execute_test)
526 BOOST_ASIO_TEST_CASE(thread_pool_executor_bulk_execute_test)
527 BOOST_ASIO_TEST_CASE(thread_pool_scheduler_test)
528 )
529