1//
2// io_service.cpp
3// ~~~~~~~~~~~~~~
4//
5// Copyright (c) 2003-2015 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_service.hpp>
18
19#include <sstream>
20#include <boost/asio/detail/thread.hpp>
21#include "unit_test.hpp"
22
23#if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
24# include <boost/asio/deadline_timer.hpp>
25#else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
26# include <boost/asio/steady_timer.hpp>
27#endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
28
29#if defined(BOOST_ASIO_HAS_BOOST_BIND)
30# include <boost/bind.hpp>
31#else // defined(BOOST_ASIO_HAS_BOOST_BIND)
32# include <functional>
33#endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
34
35using namespace boost::asio;
36
37#if defined(BOOST_ASIO_HAS_BOOST_BIND)
38namespace bindns = boost;
39#else // defined(BOOST_ASIO_HAS_BOOST_BIND)
40namespace bindns = std;
41#endif
42
43#if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
44typedef deadline_timer timer;
45namespace chronons = boost::posix_time;
46#elif defined(BOOST_ASIO_HAS_STD_CHRONO)
47typedef steady_timer timer;
48namespace chronons = std::chrono;
49#elif defined(BOOST_ASIO_HAS_BOOST_CHRONO)
50typedef steady_timer timer;
51namespace chronons = boost::chrono;
52#endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
53
54void increment(int* count)
55{
56 ++(*count);
57}
58
59void decrement_to_zero(io_service* ios, int* count)
60{
61 if (*count > 0)
62 {
63 --(*count);
64
65 int before_value = *count;
66 ios->post(handler: bindns::bind(f: decrement_to_zero, a1: ios, a2: count));
67
68 // Handler execution cannot nest, so count value should remain unchanged.
69 BOOST_ASIO_CHECK(*count == before_value);
70 }
71}
72
73void nested_decrement_to_zero(io_service* ios, int* count)
74{
75 if (*count > 0)
76 {
77 --(*count);
78
79 ios->dispatch(handler: bindns::bind(f: nested_decrement_to_zero, a1: ios, a2: count));
80
81 // Handler execution is nested, so count value should now be zero.
82 BOOST_ASIO_CHECK(*count == 0);
83 }
84}
85
86void sleep_increment(io_service* ios, int* count)
87{
88 timer t(*ios, chronons::seconds(2));
89 t.wait();
90
91 if (++(*count) < 3)
92 ios->post(handler: bindns::bind(f: sleep_increment, a1: ios, a2: count));
93}
94
95void start_sleep_increments(io_service* ios, int* count)
96{
97 // Give all threads a chance to start.
98 timer t(*ios, chronons::seconds(2));
99 t.wait();
100
101 // Start the first of three increments.
102 ios->post(handler: bindns::bind(f: sleep_increment, a1: ios, a2: count));
103}
104
105void throw_exception()
106{
107 throw 1;
108}
109
110void io_service_run(io_service* ios)
111{
112 ios->run();
113}
114
115void io_service_test()
116{
117 io_service ios;
118 int count = 0;
119
120 ios.post(handler: bindns::bind(f: increment, a1: &count));
121
122 // No handlers can be called until run() is called.
123 BOOST_ASIO_CHECK(!ios.stopped());
124 BOOST_ASIO_CHECK(count == 0);
125
126 ios.run();
127
128 // The run() call will not return until all work has finished.
129 BOOST_ASIO_CHECK(ios.stopped());
130 BOOST_ASIO_CHECK(count == 1);
131
132 count = 0;
133 ios.reset();
134 ios.post(handler: bindns::bind(f: increment, a1: &count));
135 ios.post(handler: bindns::bind(f: increment, a1: &count));
136 ios.post(handler: bindns::bind(f: increment, a1: &count));
137 ios.post(handler: bindns::bind(f: increment, a1: &count));
138 ios.post(handler: bindns::bind(f: increment, a1: &count));
139
140 // No handlers can be called until run() is called.
141 BOOST_ASIO_CHECK(!ios.stopped());
142 BOOST_ASIO_CHECK(count == 0);
143
144 ios.run();
145
146 // The run() call will not return until all work has finished.
147 BOOST_ASIO_CHECK(ios.stopped());
148 BOOST_ASIO_CHECK(count == 5);
149
150 count = 0;
151 ios.reset();
152 io_service::work* w = new io_service::work(ios);
153 ios.post(handler: bindns::bind(f: &io_service::stop, a1: &ios));
154 BOOST_ASIO_CHECK(!ios.stopped());
155 ios.run();
156
157 // The only operation executed should have been to stop run().
158 BOOST_ASIO_CHECK(ios.stopped());
159 BOOST_ASIO_CHECK(count == 0);
160
161 ios.reset();
162 ios.post(handler: bindns::bind(f: increment, a1: &count));
163 delete w;
164
165 // No handlers can be called until run() is called.
166 BOOST_ASIO_CHECK(!ios.stopped());
167 BOOST_ASIO_CHECK(count == 0);
168
169 ios.run();
170
171 // The run() call will not return until all work has finished.
172 BOOST_ASIO_CHECK(ios.stopped());
173 BOOST_ASIO_CHECK(count == 1);
174
175 count = 10;
176 ios.reset();
177 ios.post(handler: bindns::bind(f: decrement_to_zero, a1: &ios, a2: &count));
178
179 // No handlers can be called until run() is called.
180 BOOST_ASIO_CHECK(!ios.stopped());
181 BOOST_ASIO_CHECK(count == 10);
182
183 ios.run();
184
185 // The run() call will not return until all work has finished.
186 BOOST_ASIO_CHECK(ios.stopped());
187 BOOST_ASIO_CHECK(count == 0);
188
189 count = 10;
190 ios.reset();
191 ios.post(handler: bindns::bind(f: nested_decrement_to_zero, a1: &ios, a2: &count));
192
193 // No handlers can be called until run() is called.
194 BOOST_ASIO_CHECK(!ios.stopped());
195 BOOST_ASIO_CHECK(count == 10);
196
197 ios.run();
198
199 // The run() call will not return until all work has finished.
200 BOOST_ASIO_CHECK(ios.stopped());
201 BOOST_ASIO_CHECK(count == 0);
202
203 count = 10;
204 ios.reset();
205 ios.dispatch(handler: bindns::bind(f: nested_decrement_to_zero, a1: &ios, a2: &count));
206
207 // No handlers can be called until run() is called, even though nested
208 // delivery was specifically allowed in the previous call.
209 BOOST_ASIO_CHECK(!ios.stopped());
210 BOOST_ASIO_CHECK(count == 10);
211
212 ios.run();
213
214 // The run() call will not return until all work has finished.
215 BOOST_ASIO_CHECK(ios.stopped());
216 BOOST_ASIO_CHECK(count == 0);
217
218 count = 0;
219 int count2 = 0;
220 ios.reset();
221 BOOST_ASIO_CHECK(!ios.stopped());
222 ios.post(handler: bindns::bind(f: start_sleep_increments, a1: &ios, a2: &count));
223 ios.post(handler: bindns::bind(f: start_sleep_increments, a1: &ios, a2: &count2));
224 boost::asio::detail::thread thread1(bindns::bind(f: io_service_run, a1: &ios));
225 boost::asio::detail::thread thread2(bindns::bind(f: io_service_run, a1: &ios));
226 thread1.join();
227 thread2.join();
228
229 // The run() calls will not return until all work has finished.
230 BOOST_ASIO_CHECK(ios.stopped());
231 BOOST_ASIO_CHECK(count == 3);
232 BOOST_ASIO_CHECK(count2 == 3);
233
234 count = 10;
235 io_service ios2;
236 ios.dispatch(handler: ios2.wrap(handler: bindns::bind(f: decrement_to_zero, a1: &ios2, a2: &count)));
237 ios.reset();
238 BOOST_ASIO_CHECK(!ios.stopped());
239 ios.run();
240
241 // No decrement_to_zero handlers can be called until run() is called on the
242 // second io_service object.
243 BOOST_ASIO_CHECK(ios.stopped());
244 BOOST_ASIO_CHECK(count == 10);
245
246 ios2.run();
247
248 // The run() call will not return until all work has finished.
249 BOOST_ASIO_CHECK(count == 0);
250
251 count = 0;
252 int exception_count = 0;
253 ios.reset();
254 ios.post(handler: &throw_exception);
255 ios.post(handler: bindns::bind(f: increment, a1: &count));
256 ios.post(handler: bindns::bind(f: increment, a1: &count));
257 ios.post(handler: &throw_exception);
258 ios.post(handler: bindns::bind(f: increment, a1: &count));
259
260 // No handlers can be called until run() is called.
261 BOOST_ASIO_CHECK(!ios.stopped());
262 BOOST_ASIO_CHECK(count == 0);
263 BOOST_ASIO_CHECK(exception_count == 0);
264
265 for (;;)
266 {
267 try
268 {
269 ios.run();
270 break;
271 }
272 catch (int)
273 {
274 ++exception_count;
275 }
276 }
277
278 // The run() calls will not return until all work has finished.
279 BOOST_ASIO_CHECK(ios.stopped());
280 BOOST_ASIO_CHECK(count == 3);
281 BOOST_ASIO_CHECK(exception_count == 2);
282}
283
284class test_service : public boost::asio::io_service::service
285{
286public:
287 static boost::asio::io_service::id id;
288 test_service(boost::asio::io_service& s)
289 : boost::asio::io_service::service(s) {}
290private:
291 virtual void shutdown_service() {}
292};
293
294boost::asio::io_service::id test_service::id;
295
296void io_service_service_test()
297{
298 boost::asio::io_service ios1;
299 boost::asio::io_service ios2;
300 boost::asio::io_service ios3;
301
302 // Implicit service registration.
303
304 boost::asio::use_service<test_service>(ios&: ios1);
305
306 BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(ios1));
307
308 test_service* svc1 = new test_service(ios1);
309 try
310 {
311 boost::asio::add_service(ios&: ios1, svc: svc1);
312 BOOST_ASIO_ERROR("add_service did not throw");
313 }
314 catch (boost::asio::service_already_exists&)
315 {
316 }
317 delete svc1;
318
319 // Explicit service registration.
320
321 test_service* svc2 = new test_service(ios2);
322 boost::asio::add_service(ios&: ios2, svc: svc2);
323
324 BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(ios2));
325 BOOST_ASIO_CHECK(&boost::asio::use_service<test_service>(ios2) == svc2);
326
327 test_service* svc3 = new test_service(ios2);
328 try
329 {
330 boost::asio::add_service(ios&: ios2, svc: svc3);
331 BOOST_ASIO_ERROR("add_service did not throw");
332 }
333 catch (boost::asio::service_already_exists&)
334 {
335 }
336 delete svc3;
337
338 // Explicit registration with invalid owner.
339
340 test_service* svc4 = new test_service(ios2);
341 try
342 {
343 boost::asio::add_service(ios&: ios3, svc: svc4);
344 BOOST_ASIO_ERROR("add_service did not throw");
345 }
346 catch (boost::asio::invalid_service_owner&)
347 {
348 }
349 delete svc4;
350
351 BOOST_ASIO_CHECK(!boost::asio::has_service<test_service>(ios3));
352}
353
354BOOST_ASIO_TEST_SUITE
355(
356 "io_service",
357 BOOST_ASIO_TEST_CASE(io_service_test)
358 BOOST_ASIO_TEST_CASE(io_service_service_test)
359)
360

source code of boost/libs/asio/test/io_service.cpp