• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // io_context.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/io_context.hpp>
18 
19 #include <sstream>
20 #include <boost/asio/bind_executor.hpp>
21 #include <boost/asio/dispatch.hpp>
22 #include <boost/asio/post.hpp>
23 #include <boost/asio/detail/thread.hpp>
24 #include "unit_test.hpp"
25 
26 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
27 # include <boost/asio/deadline_timer.hpp>
28 #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
29 # include <boost/asio/steady_timer.hpp>
30 #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
31 
32 #if defined(BOOST_ASIO_HAS_BOOST_BIND)
33 # include <boost/bind/bind.hpp>
34 #else // defined(BOOST_ASIO_HAS_BOOST_BIND)
35 # include <functional>
36 #endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
37 
38 using namespace boost::asio;
39 
40 #if defined(BOOST_ASIO_HAS_BOOST_BIND)
41 namespace bindns = boost;
42 #else // defined(BOOST_ASIO_HAS_BOOST_BIND)
43 namespace bindns = std;
44 #endif
45 
46 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
47 typedef deadline_timer timer;
48 namespace chronons = boost::posix_time;
49 #elif defined(BOOST_ASIO_HAS_CHRONO)
50 typedef steady_timer timer;
51 namespace chronons = boost::asio::chrono;
52 #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
53 
increment(int * count)54 void increment(int* count)
55 {
56   ++(*count);
57 }
58 
decrement_to_zero(io_context * ioc,int * count)59 void decrement_to_zero(io_context* ioc, int* count)
60 {
61   if (*count > 0)
62   {
63     --(*count);
64 
65     int before_value = *count;
66     boost::asio::post(*ioc, bindns::bind(decrement_to_zero, ioc, count));
67 
68     // Handler execution cannot nest, so count value should remain unchanged.
69     BOOST_ASIO_CHECK(*count == before_value);
70   }
71 }
72 
nested_decrement_to_zero(io_context * ioc,int * count)73 void nested_decrement_to_zero(io_context* ioc, int* count)
74 {
75   if (*count > 0)
76   {
77     --(*count);
78 
79     boost::asio::dispatch(*ioc,
80         bindns::bind(nested_decrement_to_zero, ioc, count));
81 
82     // Handler execution is nested, so count value should now be zero.
83     BOOST_ASIO_CHECK(*count == 0);
84   }
85 }
86 
sleep_increment(io_context * ioc,int * count)87 void sleep_increment(io_context* ioc, int* count)
88 {
89   timer t(*ioc, chronons::seconds(2));
90   t.wait();
91 
92   if (++(*count) < 3)
93     boost::asio::post(*ioc, bindns::bind(sleep_increment, ioc, count));
94 }
95 
start_sleep_increments(io_context * ioc,int * count)96 void start_sleep_increments(io_context* ioc, int* count)
97 {
98   // Give all threads a chance to start.
99   timer t(*ioc, chronons::seconds(2));
100   t.wait();
101 
102   // Start the first of three increments.
103   boost::asio::post(*ioc, bindns::bind(sleep_increment, ioc, count));
104 }
105 
throw_exception()106 void throw_exception()
107 {
108   throw 1;
109 }
110 
io_context_run(io_context * ioc)111 void io_context_run(io_context* ioc)
112 {
113   ioc->run();
114 }
115 
io_context_test()116 void io_context_test()
117 {
118   io_context ioc;
119   int count = 0;
120 
121   boost::asio::post(ioc, bindns::bind(increment, &count));
122 
123   // No handlers can be called until run() is called.
124   BOOST_ASIO_CHECK(!ioc.stopped());
125   BOOST_ASIO_CHECK(count == 0);
126 
127   ioc.run();
128 
129   // The run() call will not return until all work has finished.
130   BOOST_ASIO_CHECK(ioc.stopped());
131   BOOST_ASIO_CHECK(count == 1);
132 
133   count = 0;
134   ioc.restart();
135   boost::asio::post(ioc, bindns::bind(increment, &count));
136   boost::asio::post(ioc, bindns::bind(increment, &count));
137   boost::asio::post(ioc, bindns::bind(increment, &count));
138   boost::asio::post(ioc, bindns::bind(increment, &count));
139   boost::asio::post(ioc, bindns::bind(increment, &count));
140 
141   // No handlers can be called until run() is called.
142   BOOST_ASIO_CHECK(!ioc.stopped());
143   BOOST_ASIO_CHECK(count == 0);
144 
145   ioc.run();
146 
147   // The run() call will not return until all work has finished.
148   BOOST_ASIO_CHECK(ioc.stopped());
149   BOOST_ASIO_CHECK(count == 5);
150 
151   count = 0;
152   ioc.restart();
153   executor_work_guard<io_context::executor_type> w = make_work_guard(ioc);
154   boost::asio::post(ioc, bindns::bind(&io_context::stop, &ioc));
155   BOOST_ASIO_CHECK(!ioc.stopped());
156   ioc.run();
157 
158   // The only operation executed should have been to stop run().
159   BOOST_ASIO_CHECK(ioc.stopped());
160   BOOST_ASIO_CHECK(count == 0);
161 
162   ioc.restart();
163   boost::asio::post(ioc, bindns::bind(increment, &count));
164   w.reset();
165 
166   // No handlers can be called until run() is called.
167   BOOST_ASIO_CHECK(!ioc.stopped());
168   BOOST_ASIO_CHECK(count == 0);
169 
170   ioc.run();
171 
172   // The run() call will not return until all work has finished.
173   BOOST_ASIO_CHECK(ioc.stopped());
174   BOOST_ASIO_CHECK(count == 1);
175 
176   count = 10;
177   ioc.restart();
178   boost::asio::post(ioc, bindns::bind(decrement_to_zero, &ioc, &count));
179 
180   // No handlers can be called until run() is called.
181   BOOST_ASIO_CHECK(!ioc.stopped());
182   BOOST_ASIO_CHECK(count == 10);
183 
184   ioc.run();
185 
186   // The run() call will not return until all work has finished.
187   BOOST_ASIO_CHECK(ioc.stopped());
188   BOOST_ASIO_CHECK(count == 0);
189 
190   count = 10;
191   ioc.restart();
192   boost::asio::post(ioc, bindns::bind(nested_decrement_to_zero, &ioc, &count));
193 
194   // No handlers can be called until run() is called.
195   BOOST_ASIO_CHECK(!ioc.stopped());
196   BOOST_ASIO_CHECK(count == 10);
197 
198   ioc.run();
199 
200   // The run() call will not return until all work has finished.
201   BOOST_ASIO_CHECK(ioc.stopped());
202   BOOST_ASIO_CHECK(count == 0);
203 
204   count = 10;
205   ioc.restart();
206   boost::asio::dispatch(ioc,
207       bindns::bind(nested_decrement_to_zero, &ioc, &count));
208 
209   // No handlers can be called until run() is called, even though nested
210   // delivery was specifically allowed in the previous call.
211   BOOST_ASIO_CHECK(!ioc.stopped());
212   BOOST_ASIO_CHECK(count == 10);
213 
214   ioc.run();
215 
216   // The run() call will not return until all work has finished.
217   BOOST_ASIO_CHECK(ioc.stopped());
218   BOOST_ASIO_CHECK(count == 0);
219 
220   count = 0;
221   int count2 = 0;
222   ioc.restart();
223   BOOST_ASIO_CHECK(!ioc.stopped());
224   boost::asio::post(ioc, bindns::bind(start_sleep_increments, &ioc, &count));
225   boost::asio::post(ioc, bindns::bind(start_sleep_increments, &ioc, &count2));
226   boost::asio::detail::thread thread1(bindns::bind(io_context_run, &ioc));
227   boost::asio::detail::thread thread2(bindns::bind(io_context_run, &ioc));
228   thread1.join();
229   thread2.join();
230 
231   // The run() calls will not return until all work has finished.
232   BOOST_ASIO_CHECK(ioc.stopped());
233   BOOST_ASIO_CHECK(count == 3);
234   BOOST_ASIO_CHECK(count2 == 3);
235 
236   count = 10;
237   io_context ioc2;
238   boost::asio::dispatch(ioc, boost::asio::bind_executor(ioc2,
239         bindns::bind(decrement_to_zero, &ioc2, &count)));
240   ioc.restart();
241   BOOST_ASIO_CHECK(!ioc.stopped());
242   ioc.run();
243 
244   // No decrement_to_zero handlers can be called until run() is called on the
245   // second io_context object.
246   BOOST_ASIO_CHECK(ioc.stopped());
247   BOOST_ASIO_CHECK(count == 10);
248 
249   ioc2.run();
250 
251   // The run() call will not return until all work has finished.
252   BOOST_ASIO_CHECK(count == 0);
253 
254   count = 0;
255   int exception_count = 0;
256   ioc.restart();
257   boost::asio::post(ioc, &throw_exception);
258   boost::asio::post(ioc, bindns::bind(increment, &count));
259   boost::asio::post(ioc, bindns::bind(increment, &count));
260   boost::asio::post(ioc, &throw_exception);
261   boost::asio::post(ioc, bindns::bind(increment, &count));
262 
263   // No handlers can be called until run() is called.
264   BOOST_ASIO_CHECK(!ioc.stopped());
265   BOOST_ASIO_CHECK(count == 0);
266   BOOST_ASIO_CHECK(exception_count == 0);
267 
268   for (;;)
269   {
270     try
271     {
272       ioc.run();
273       break;
274     }
275     catch (int)
276     {
277       ++exception_count;
278     }
279   }
280 
281   // The run() calls will not return until all work has finished.
282   BOOST_ASIO_CHECK(ioc.stopped());
283   BOOST_ASIO_CHECK(count == 3);
284   BOOST_ASIO_CHECK(exception_count == 2);
285 }
286 
287 class test_service : public boost::asio::io_context::service
288 {
289 public:
290   static boost::asio::io_context::id id;
test_service(boost::asio::io_context & s)291   test_service(boost::asio::io_context& s)
292     : boost::asio::io_context::service(s) {}
293 private:
shutdown_service()294   virtual void shutdown_service() {}
295 };
296 
297 boost::asio::io_context::id test_service::id;
298 
io_context_service_test()299 void io_context_service_test()
300 {
301   boost::asio::io_context ioc1;
302   boost::asio::io_context ioc2;
303   boost::asio::io_context ioc3;
304 
305   // Implicit service registration.
306 
307   boost::asio::use_service<test_service>(ioc1);
308 
309   BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(ioc1));
310 
311   test_service* svc1 = new test_service(ioc1);
312   try
313   {
314     boost::asio::add_service(ioc1, svc1);
315     BOOST_ASIO_ERROR("add_service did not throw");
316   }
317   catch (boost::asio::service_already_exists&)
318   {
319   }
320   delete svc1;
321 
322   // Explicit service registration.
323 
324   test_service* svc2 = new test_service(ioc2);
325   boost::asio::add_service(ioc2, svc2);
326 
327   BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(ioc2));
328   BOOST_ASIO_CHECK(&boost::asio::use_service<test_service>(ioc2) == svc2);
329 
330   test_service* svc3 = new test_service(ioc2);
331   try
332   {
333     boost::asio::add_service(ioc2, svc3);
334     BOOST_ASIO_ERROR("add_service did not throw");
335   }
336   catch (boost::asio::service_already_exists&)
337   {
338   }
339   delete svc3;
340 
341   // Explicit registration with invalid owner.
342 
343   test_service* svc4 = new test_service(ioc2);
344   try
345   {
346     boost::asio::add_service(ioc3, svc4);
347     BOOST_ASIO_ERROR("add_service did not throw");
348   }
349   catch (boost::asio::invalid_service_owner&)
350   {
351   }
352   delete svc4;
353 
354   BOOST_ASIO_CHECK(!boost::asio::has_service<test_service>(ioc3));
355 }
356 
io_context_executor_query_test()357 void io_context_executor_query_test()
358 {
359   io_context ioc;
360 
361   BOOST_ASIO_CHECK(
362       &boost::asio::query(ioc.get_executor(),
363         boost::asio::execution::context)
364       == &ioc);
365 
366   BOOST_ASIO_CHECK(
367       boost::asio::query(ioc.get_executor(),
368         boost::asio::execution::blocking)
369       == boost::asio::execution::blocking.possibly);
370 
371   BOOST_ASIO_CHECK(
372       boost::asio::query(ioc.get_executor(),
373         boost::asio::execution::blocking.possibly)
374       == boost::asio::execution::blocking.possibly);
375 
376   BOOST_ASIO_CHECK(
377       boost::asio::query(ioc.get_executor(),
378         boost::asio::execution::outstanding_work)
379       == boost::asio::execution::outstanding_work.untracked);
380 
381   BOOST_ASIO_CHECK(
382       boost::asio::query(ioc.get_executor(),
383         boost::asio::execution::outstanding_work.untracked)
384       == boost::asio::execution::outstanding_work.untracked);
385 
386   BOOST_ASIO_CHECK(
387       boost::asio::query(ioc.get_executor(),
388         boost::asio::execution::relationship)
389       == boost::asio::execution::relationship.fork);
390 
391   BOOST_ASIO_CHECK(
392       boost::asio::query(ioc.get_executor(),
393         boost::asio::execution::relationship.fork)
394       == boost::asio::execution::relationship.fork);
395 
396   BOOST_ASIO_CHECK(
397       boost::asio::query(ioc.get_executor(),
398         boost::asio::execution::mapping)
399       == boost::asio::execution::mapping.thread);
400 
401   BOOST_ASIO_CHECK(
402       boost::asio::query(ioc.get_executor(),
403         boost::asio::execution::allocator)
404       == std::allocator<void>());
405 }
406 
io_context_executor_execute_test()407 void io_context_executor_execute_test()
408 {
409   io_context ioc;
410   int count = 0;
411 
412   boost::asio::execution::execute(ioc.get_executor(),
413       bindns::bind(increment, &count));
414 
415   // No handlers can be called until run() is called.
416   BOOST_ASIO_CHECK(!ioc.stopped());
417   BOOST_ASIO_CHECK(count == 0);
418 
419   ioc.run();
420 
421   // The run() call will not return until all work has finished.
422   BOOST_ASIO_CHECK(ioc.stopped());
423   BOOST_ASIO_CHECK(count == 1);
424 
425   count = 0;
426   ioc.restart();
427   boost::asio::execution::execute(
428       boost::asio::require(ioc.get_executor(),
429         boost::asio::execution::blocking.possibly),
430       bindns::bind(increment, &count));
431 
432   // No handlers can be called until run() is called.
433   BOOST_ASIO_CHECK(!ioc.stopped());
434   BOOST_ASIO_CHECK(count == 0);
435 
436   ioc.run();
437 
438   // The run() call will not return until all work has finished.
439   BOOST_ASIO_CHECK(ioc.stopped());
440   BOOST_ASIO_CHECK(count == 1);
441 
442   count = 0;
443   ioc.restart();
444   boost::asio::execution::execute(
445       boost::asio::require(ioc.get_executor(),
446         boost::asio::execution::blocking.never),
447       bindns::bind(increment, &count));
448 
449   // No handlers can be called until run() is called.
450   BOOST_ASIO_CHECK(!ioc.stopped());
451   BOOST_ASIO_CHECK(count == 0);
452 
453   ioc.run();
454 
455   // The run() call will not return until all work has finished.
456   BOOST_ASIO_CHECK(ioc.stopped());
457   BOOST_ASIO_CHECK(count == 1);
458 
459   count = 0;
460   ioc.restart();
461   BOOST_ASIO_CHECK(!ioc.stopped());
462 
463   boost::asio::execution::execute(
464       boost::asio::require(ioc.get_executor(),
465         boost::asio::execution::blocking.never,
466         boost::asio::execution::outstanding_work.tracked),
467       bindns::bind(increment, &count));
468 
469   // No handlers can be called until run() is called.
470   BOOST_ASIO_CHECK(!ioc.stopped());
471   BOOST_ASIO_CHECK(count == 0);
472 
473   ioc.run();
474 
475   // The run() call will not return until all work has finished.
476   BOOST_ASIO_CHECK(ioc.stopped());
477   BOOST_ASIO_CHECK(count == 1);
478 
479   count = 0;
480   ioc.restart();
481   boost::asio::execution::execute(
482       boost::asio::require(ioc.get_executor(),
483         boost::asio::execution::blocking.never,
484         boost::asio::execution::outstanding_work.untracked),
485       bindns::bind(increment, &count));
486 
487   // No handlers can be called until run() is called.
488   BOOST_ASIO_CHECK(!ioc.stopped());
489   BOOST_ASIO_CHECK(count == 0);
490 
491   ioc.run();
492 
493   // The run() call will not return until all work has finished.
494   BOOST_ASIO_CHECK(ioc.stopped());
495   BOOST_ASIO_CHECK(count == 1);
496 
497   count = 0;
498   ioc.restart();
499   boost::asio::execution::execute(
500       boost::asio::require(ioc.get_executor(),
501         boost::asio::execution::blocking.never,
502         boost::asio::execution::outstanding_work.untracked,
503         boost::asio::execution::relationship.fork),
504       bindns::bind(increment, &count));
505 
506   // No handlers can be called until run() is called.
507   BOOST_ASIO_CHECK(!ioc.stopped());
508   BOOST_ASIO_CHECK(count == 0);
509 
510   ioc.run();
511 
512   // The run() call will not return until all work has finished.
513   BOOST_ASIO_CHECK(ioc.stopped());
514   BOOST_ASIO_CHECK(count == 1);
515 
516   count = 0;
517   ioc.restart();
518   boost::asio::execution::execute(
519       boost::asio::require(ioc.get_executor(),
520         boost::asio::execution::blocking.never,
521         boost::asio::execution::outstanding_work.untracked,
522         boost::asio::execution::relationship.continuation),
523       bindns::bind(increment, &count));
524 
525   // No handlers can be called until run() is called.
526   BOOST_ASIO_CHECK(!ioc.stopped());
527   BOOST_ASIO_CHECK(count == 0);
528 
529   ioc.run();
530 
531   // The run() call will not return until all work has finished.
532   BOOST_ASIO_CHECK(ioc.stopped());
533   BOOST_ASIO_CHECK(count == 1);
534 
535   count = 0;
536   ioc.restart();
537   boost::asio::execution::execute(
538       boost::asio::prefer(
539         boost::asio::require(ioc.get_executor(),
540           boost::asio::execution::blocking.never,
541           boost::asio::execution::outstanding_work.untracked,
542           boost::asio::execution::relationship.continuation),
543         boost::asio::execution::allocator(std::allocator<void>())),
544       bindns::bind(increment, &count));
545 
546   // No handlers can be called until run() is called.
547   BOOST_ASIO_CHECK(!ioc.stopped());
548   BOOST_ASIO_CHECK(count == 0);
549 
550   ioc.run();
551 
552   // The run() call will not return until all work has finished.
553   BOOST_ASIO_CHECK(ioc.stopped());
554   BOOST_ASIO_CHECK(count == 1);
555 
556   count = 0;
557   ioc.restart();
558   boost::asio::execution::execute(
559       boost::asio::prefer(
560         boost::asio::require(ioc.get_executor(),
561           boost::asio::execution::blocking.never,
562           boost::asio::execution::outstanding_work.untracked,
563           boost::asio::execution::relationship.continuation),
564         boost::asio::execution::allocator),
565       bindns::bind(increment, &count));
566 
567   // No handlers can be called until run() is called.
568   BOOST_ASIO_CHECK(!ioc.stopped());
569   BOOST_ASIO_CHECK(count == 0);
570 
571   ioc.run();
572 
573   // The run() call will not return until all work has finished.
574   BOOST_ASIO_CHECK(ioc.stopped());
575   BOOST_ASIO_CHECK(count == 1);
576 }
577 
578 BOOST_ASIO_TEST_SUITE
579 (
580   "io_context",
581   BOOST_ASIO_TEST_CASE(io_context_test)
582   BOOST_ASIO_TEST_CASE(io_context_service_test)
583   BOOST_ASIO_TEST_CASE(io_context_executor_query_test)
584   BOOST_ASIO_TEST_CASE(io_context_executor_execute_test)
585 )
586