• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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