]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // |
b32b8144 | 2 | // io_context.cpp |
7c673cae FG |
3 | // ~~~~~~~~~~~~~~ |
4 | // | |
f67539c2 | 5 | // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
7c673cae FG |
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. | |
b32b8144 | 17 | #include <boost/asio/io_context.hpp> |
7c673cae FG |
18 | |
19 | #include <sstream> | |
b32b8144 FG |
20 | #include <boost/asio/bind_executor.hpp> |
21 | #include <boost/asio/dispatch.hpp> | |
22 | #include <boost/asio/post.hpp> | |
7c673cae FG |
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) | |
f67539c2 | 33 | # include <boost/bind/bind.hpp> |
7c673cae FG |
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; | |
b32b8144 | 49 | #elif defined(BOOST_ASIO_HAS_CHRONO) |
7c673cae | 50 | typedef steady_timer timer; |
b32b8144 | 51 | namespace chronons = boost::asio::chrono; |
7c673cae FG |
52 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
53 | ||
54 | void increment(int* count) | |
55 | { | |
56 | ++(*count); | |
57 | } | |
58 | ||
b32b8144 | 59 | void decrement_to_zero(io_context* ioc, int* count) |
7c673cae FG |
60 | { |
61 | if (*count > 0) | |
62 | { | |
63 | --(*count); | |
64 | ||
65 | int before_value = *count; | |
b32b8144 | 66 | boost::asio::post(*ioc, bindns::bind(decrement_to_zero, ioc, count)); |
7c673cae FG |
67 | |
68 | // Handler execution cannot nest, so count value should remain unchanged. | |
69 | BOOST_ASIO_CHECK(*count == before_value); | |
70 | } | |
71 | } | |
72 | ||
b32b8144 | 73 | void nested_decrement_to_zero(io_context* ioc, int* count) |
7c673cae FG |
74 | { |
75 | if (*count > 0) | |
76 | { | |
77 | --(*count); | |
78 | ||
b32b8144 FG |
79 | boost::asio::dispatch(*ioc, |
80 | bindns::bind(nested_decrement_to_zero, ioc, count)); | |
7c673cae FG |
81 | |
82 | // Handler execution is nested, so count value should now be zero. | |
83 | BOOST_ASIO_CHECK(*count == 0); | |
84 | } | |
85 | } | |
86 | ||
b32b8144 | 87 | void sleep_increment(io_context* ioc, int* count) |
7c673cae | 88 | { |
b32b8144 | 89 | timer t(*ioc, chronons::seconds(2)); |
7c673cae FG |
90 | t.wait(); |
91 | ||
92 | if (++(*count) < 3) | |
b32b8144 | 93 | boost::asio::post(*ioc, bindns::bind(sleep_increment, ioc, count)); |
7c673cae FG |
94 | } |
95 | ||
b32b8144 | 96 | void start_sleep_increments(io_context* ioc, int* count) |
7c673cae FG |
97 | { |
98 | // Give all threads a chance to start. | |
b32b8144 | 99 | timer t(*ioc, chronons::seconds(2)); |
7c673cae FG |
100 | t.wait(); |
101 | ||
102 | // Start the first of three increments. | |
b32b8144 | 103 | boost::asio::post(*ioc, bindns::bind(sleep_increment, ioc, count)); |
7c673cae FG |
104 | } |
105 | ||
106 | void throw_exception() | |
107 | { | |
108 | throw 1; | |
109 | } | |
110 | ||
b32b8144 | 111 | void io_context_run(io_context* ioc) |
7c673cae | 112 | { |
b32b8144 | 113 | ioc->run(); |
7c673cae FG |
114 | } |
115 | ||
b32b8144 | 116 | void io_context_test() |
7c673cae | 117 | { |
b32b8144 | 118 | io_context ioc; |
7c673cae FG |
119 | int count = 0; |
120 | ||
b32b8144 | 121 | boost::asio::post(ioc, bindns::bind(increment, &count)); |
7c673cae FG |
122 | |
123 | // No handlers can be called until run() is called. | |
b32b8144 | 124 | BOOST_ASIO_CHECK(!ioc.stopped()); |
7c673cae FG |
125 | BOOST_ASIO_CHECK(count == 0); |
126 | ||
b32b8144 | 127 | ioc.run(); |
7c673cae FG |
128 | |
129 | // The run() call will not return until all work has finished. | |
b32b8144 | 130 | BOOST_ASIO_CHECK(ioc.stopped()); |
7c673cae FG |
131 | BOOST_ASIO_CHECK(count == 1); |
132 | ||
133 | count = 0; | |
b32b8144 FG |
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)); | |
7c673cae FG |
140 | |
141 | // No handlers can be called until run() is called. | |
b32b8144 | 142 | BOOST_ASIO_CHECK(!ioc.stopped()); |
7c673cae FG |
143 | BOOST_ASIO_CHECK(count == 0); |
144 | ||
b32b8144 | 145 | ioc.run(); |
7c673cae FG |
146 | |
147 | // The run() call will not return until all work has finished. | |
b32b8144 | 148 | BOOST_ASIO_CHECK(ioc.stopped()); |
7c673cae FG |
149 | BOOST_ASIO_CHECK(count == 5); |
150 | ||
151 | count = 0; | |
b32b8144 FG |
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(); | |
7c673cae FG |
157 | |
158 | // The only operation executed should have been to stop run(). | |
b32b8144 | 159 | BOOST_ASIO_CHECK(ioc.stopped()); |
7c673cae FG |
160 | BOOST_ASIO_CHECK(count == 0); |
161 | ||
b32b8144 FG |
162 | ioc.restart(); |
163 | boost::asio::post(ioc, bindns::bind(increment, &count)); | |
164 | w.reset(); | |
7c673cae FG |
165 | |
166 | // No handlers can be called until run() is called. | |
b32b8144 | 167 | BOOST_ASIO_CHECK(!ioc.stopped()); |
7c673cae FG |
168 | BOOST_ASIO_CHECK(count == 0); |
169 | ||
b32b8144 | 170 | ioc.run(); |
7c673cae FG |
171 | |
172 | // The run() call will not return until all work has finished. | |
b32b8144 | 173 | BOOST_ASIO_CHECK(ioc.stopped()); |
7c673cae FG |
174 | BOOST_ASIO_CHECK(count == 1); |
175 | ||
176 | count = 10; | |
b32b8144 FG |
177 | ioc.restart(); |
178 | boost::asio::post(ioc, bindns::bind(decrement_to_zero, &ioc, &count)); | |
7c673cae FG |
179 | |
180 | // No handlers can be called until run() is called. | |
b32b8144 | 181 | BOOST_ASIO_CHECK(!ioc.stopped()); |
7c673cae FG |
182 | BOOST_ASIO_CHECK(count == 10); |
183 | ||
b32b8144 | 184 | ioc.run(); |
7c673cae FG |
185 | |
186 | // The run() call will not return until all work has finished. | |
b32b8144 | 187 | BOOST_ASIO_CHECK(ioc.stopped()); |
7c673cae FG |
188 | BOOST_ASIO_CHECK(count == 0); |
189 | ||
190 | count = 10; | |
b32b8144 FG |
191 | ioc.restart(); |
192 | boost::asio::post(ioc, bindns::bind(nested_decrement_to_zero, &ioc, &count)); | |
7c673cae FG |
193 | |
194 | // No handlers can be called until run() is called. | |
b32b8144 | 195 | BOOST_ASIO_CHECK(!ioc.stopped()); |
7c673cae FG |
196 | BOOST_ASIO_CHECK(count == 10); |
197 | ||
b32b8144 | 198 | ioc.run(); |
7c673cae FG |
199 | |
200 | // The run() call will not return until all work has finished. | |
b32b8144 | 201 | BOOST_ASIO_CHECK(ioc.stopped()); |
7c673cae FG |
202 | BOOST_ASIO_CHECK(count == 0); |
203 | ||
204 | count = 10; | |
b32b8144 FG |
205 | ioc.restart(); |
206 | boost::asio::dispatch(ioc, | |
207 | bindns::bind(nested_decrement_to_zero, &ioc, &count)); | |
7c673cae FG |
208 | |
209 | // No handlers can be called until run() is called, even though nested | |
210 | // delivery was specifically allowed in the previous call. | |
b32b8144 | 211 | BOOST_ASIO_CHECK(!ioc.stopped()); |
7c673cae FG |
212 | BOOST_ASIO_CHECK(count == 10); |
213 | ||
b32b8144 | 214 | ioc.run(); |
7c673cae FG |
215 | |
216 | // The run() call will not return until all work has finished. | |
b32b8144 | 217 | BOOST_ASIO_CHECK(ioc.stopped()); |
7c673cae FG |
218 | BOOST_ASIO_CHECK(count == 0); |
219 | ||
220 | count = 0; | |
221 | int count2 = 0; | |
b32b8144 FG |
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)); | |
7c673cae FG |
228 | thread1.join(); |
229 | thread2.join(); | |
230 | ||
231 | // The run() calls will not return until all work has finished. | |
b32b8144 | 232 | BOOST_ASIO_CHECK(ioc.stopped()); |
7c673cae FG |
233 | BOOST_ASIO_CHECK(count == 3); |
234 | BOOST_ASIO_CHECK(count2 == 3); | |
235 | ||
236 | count = 10; | |
b32b8144 FG |
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(); | |
7c673cae FG |
243 | |
244 | // No decrement_to_zero handlers can be called until run() is called on the | |
b32b8144 FG |
245 | // second io_context object. |
246 | BOOST_ASIO_CHECK(ioc.stopped()); | |
7c673cae FG |
247 | BOOST_ASIO_CHECK(count == 10); |
248 | ||
b32b8144 | 249 | ioc2.run(); |
7c673cae FG |
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; | |
b32b8144 FG |
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)); | |
7c673cae FG |
262 | |
263 | // No handlers can be called until run() is called. | |
b32b8144 | 264 | BOOST_ASIO_CHECK(!ioc.stopped()); |
7c673cae FG |
265 | BOOST_ASIO_CHECK(count == 0); |
266 | BOOST_ASIO_CHECK(exception_count == 0); | |
267 | ||
268 | for (;;) | |
269 | { | |
270 | try | |
271 | { | |
b32b8144 | 272 | ioc.run(); |
7c673cae FG |
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. | |
b32b8144 | 282 | BOOST_ASIO_CHECK(ioc.stopped()); |
7c673cae FG |
283 | BOOST_ASIO_CHECK(count == 3); |
284 | BOOST_ASIO_CHECK(exception_count == 2); | |
285 | } | |
286 | ||
b32b8144 | 287 | class test_service : public boost::asio::io_context::service |
7c673cae FG |
288 | { |
289 | public: | |
b32b8144 FG |
290 | static boost::asio::io_context::id id; |
291 | test_service(boost::asio::io_context& s) | |
292 | : boost::asio::io_context::service(s) {} | |
7c673cae FG |
293 | private: |
294 | virtual void shutdown_service() {} | |
295 | }; | |
296 | ||
b32b8144 | 297 | boost::asio::io_context::id test_service::id; |
7c673cae | 298 | |
b32b8144 | 299 | void io_context_service_test() |
7c673cae | 300 | { |
b32b8144 FG |
301 | boost::asio::io_context ioc1; |
302 | boost::asio::io_context ioc2; | |
303 | boost::asio::io_context ioc3; | |
7c673cae FG |
304 | |
305 | // Implicit service registration. | |
306 | ||
b32b8144 | 307 | boost::asio::use_service<test_service>(ioc1); |
7c673cae | 308 | |
b32b8144 | 309 | BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(ioc1)); |
7c673cae | 310 | |
b32b8144 | 311 | test_service* svc1 = new test_service(ioc1); |
7c673cae FG |
312 | try |
313 | { | |
b32b8144 | 314 | boost::asio::add_service(ioc1, svc1); |
7c673cae FG |
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 | ||
b32b8144 FG |
324 | test_service* svc2 = new test_service(ioc2); |
325 | boost::asio::add_service(ioc2, svc2); | |
7c673cae | 326 | |
b32b8144 FG |
327 | BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(ioc2)); |
328 | BOOST_ASIO_CHECK(&boost::asio::use_service<test_service>(ioc2) == svc2); | |
7c673cae | 329 | |
b32b8144 | 330 | test_service* svc3 = new test_service(ioc2); |
7c673cae FG |
331 | try |
332 | { | |
b32b8144 | 333 | boost::asio::add_service(ioc2, svc3); |
7c673cae FG |
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 | ||
b32b8144 | 343 | test_service* svc4 = new test_service(ioc2); |
7c673cae FG |
344 | try |
345 | { | |
b32b8144 | 346 | boost::asio::add_service(ioc3, svc4); |
7c673cae FG |
347 | BOOST_ASIO_ERROR("add_service did not throw"); |
348 | } | |
349 | catch (boost::asio::invalid_service_owner&) | |
350 | { | |
351 | } | |
352 | delete svc4; | |
353 | ||
b32b8144 | 354 | BOOST_ASIO_CHECK(!boost::asio::has_service<test_service>(ioc3)); |
7c673cae FG |
355 | } |
356 | ||
357 | BOOST_ASIO_TEST_SUITE | |
358 | ( | |
b32b8144 FG |
359 | "io_context", |
360 | BOOST_ASIO_TEST_CASE(io_context_test) | |
361 | BOOST_ASIO_TEST_CASE(io_context_service_test) | |
7c673cae | 362 | ) |