]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // strand.cpp | |
3 | // ~~~~~~~~~~ | |
4 | // | |
92f5a8d4 | 5 | // Copyright (c) 2003-2019 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. | |
17 | #include <boost/asio/strand.hpp> | |
18 | ||
19 | #include <sstream> | |
b32b8144 FG |
20 | #include <boost/asio/io_context.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) | |
33 | # include <boost/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; | |
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 | ||
92f5a8d4 | 59 | void increment_without_lock(strand<io_context::executor_type>* s, int* count) |
7c673cae FG |
60 | { |
61 | BOOST_ASIO_CHECK(!s->running_in_this_thread()); | |
62 | ||
63 | int original_count = *count; | |
64 | ||
b32b8144 | 65 | dispatch(*s, bindns::bind(increment, count)); |
7c673cae FG |
66 | |
67 | // No other functions are currently executing through the locking dispatcher, | |
68 | // so the previous call to dispatch should have successfully nested. | |
69 | BOOST_ASIO_CHECK(*count == original_count + 1); | |
70 | } | |
71 | ||
92f5a8d4 | 72 | void increment_with_lock(strand<io_context::executor_type>* s, int* count) |
7c673cae FG |
73 | { |
74 | BOOST_ASIO_CHECK(s->running_in_this_thread()); | |
75 | ||
76 | int original_count = *count; | |
77 | ||
b32b8144 | 78 | dispatch(*s, bindns::bind(increment, count)); |
7c673cae FG |
79 | |
80 | // The current function already holds the strand's lock, so the | |
81 | // previous call to dispatch should have successfully nested. | |
82 | BOOST_ASIO_CHECK(*count == original_count + 1); | |
83 | } | |
84 | ||
b32b8144 | 85 | void sleep_increment(io_context* ioc, int* count) |
7c673cae | 86 | { |
b32b8144 | 87 | timer t(*ioc, chronons::seconds(2)); |
7c673cae FG |
88 | t.wait(); |
89 | ||
90 | ++(*count); | |
91 | } | |
92 | ||
b32b8144 FG |
93 | void increment_by_a(int* count, int a) |
94 | { | |
95 | (*count) += a; | |
96 | } | |
97 | ||
98 | void increment_by_a_b(int* count, int a, int b) | |
99 | { | |
100 | (*count) += a + b; | |
101 | } | |
102 | ||
103 | void increment_by_a_b_c(int* count, int a, int b, int c) | |
104 | { | |
105 | (*count) += a + b + c; | |
106 | } | |
107 | ||
108 | void increment_by_a_b_c_d(int* count, int a, int b, int c, int d) | |
109 | { | |
110 | (*count) += a + b + c + d; | |
111 | } | |
112 | ||
92f5a8d4 TL |
113 | void start_sleep_increments(io_context* ioc, |
114 | strand<io_context::executor_type>* s, int* count) | |
7c673cae FG |
115 | { |
116 | // Give all threads a chance to start. | |
b32b8144 | 117 | timer t(*ioc, chronons::seconds(2)); |
7c673cae FG |
118 | t.wait(); |
119 | ||
120 | // Start three increments. | |
b32b8144 FG |
121 | post(*s, bindns::bind(sleep_increment, ioc, count)); |
122 | post(*s, bindns::bind(sleep_increment, ioc, count)); | |
123 | post(*s, bindns::bind(sleep_increment, ioc, count)); | |
7c673cae FG |
124 | } |
125 | ||
126 | void throw_exception() | |
127 | { | |
128 | throw 1; | |
129 | } | |
130 | ||
b32b8144 | 131 | void io_context_run(io_context* ioc) |
7c673cae | 132 | { |
b32b8144 | 133 | ioc->run(); |
7c673cae FG |
134 | } |
135 | ||
136 | void strand_test() | |
137 | { | |
b32b8144 | 138 | io_context ioc; |
92f5a8d4 | 139 | strand<io_context::executor_type> s = make_strand(ioc); |
7c673cae FG |
140 | int count = 0; |
141 | ||
b32b8144 | 142 | post(ioc, bindns::bind(increment_without_lock, &s, &count)); |
7c673cae FG |
143 | |
144 | // No handlers can be called until run() is called. | |
145 | BOOST_ASIO_CHECK(count == 0); | |
146 | ||
b32b8144 | 147 | ioc.run(); |
7c673cae FG |
148 | |
149 | // The run() call will not return until all work has finished. | |
150 | BOOST_ASIO_CHECK(count == 1); | |
151 | ||
152 | count = 0; | |
b32b8144 FG |
153 | ioc.restart(); |
154 | post(s, bindns::bind(increment_with_lock, &s, &count)); | |
7c673cae FG |
155 | |
156 | // No handlers can be called until run() is called. | |
157 | BOOST_ASIO_CHECK(count == 0); | |
158 | ||
b32b8144 | 159 | ioc.run(); |
7c673cae FG |
160 | |
161 | // The run() call will not return until all work has finished. | |
162 | BOOST_ASIO_CHECK(count == 1); | |
163 | ||
164 | count = 0; | |
b32b8144 FG |
165 | ioc.restart(); |
166 | post(ioc, bindns::bind(start_sleep_increments, &ioc, &s, &count)); | |
167 | boost::asio::detail::thread thread1(bindns::bind(io_context_run, &ioc)); | |
168 | boost::asio::detail::thread thread2(bindns::bind(io_context_run, &ioc)); | |
7c673cae FG |
169 | |
170 | // Check all events run one after another even though there are two threads. | |
b32b8144 | 171 | timer timer1(ioc, chronons::seconds(3)); |
7c673cae FG |
172 | timer1.wait(); |
173 | BOOST_ASIO_CHECK(count == 0); | |
b32b8144 | 174 | #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
7c673cae | 175 | timer1.expires_at(timer1.expires_at() + chronons::seconds(2)); |
b32b8144 FG |
176 | #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
177 | timer1.expires_at(timer1.expiry() + chronons::seconds(2)); | |
178 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) | |
7c673cae FG |
179 | timer1.wait(); |
180 | BOOST_ASIO_CHECK(count == 1); | |
b32b8144 | 181 | #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
7c673cae | 182 | timer1.expires_at(timer1.expires_at() + chronons::seconds(2)); |
b32b8144 FG |
183 | #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
184 | timer1.expires_at(timer1.expiry() + chronons::seconds(2)); | |
185 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) | |
7c673cae FG |
186 | timer1.wait(); |
187 | BOOST_ASIO_CHECK(count == 2); | |
188 | ||
189 | thread1.join(); | |
190 | thread2.join(); | |
191 | ||
192 | // The run() calls will not return until all work has finished. | |
193 | BOOST_ASIO_CHECK(count == 3); | |
194 | ||
195 | count = 0; | |
196 | int exception_count = 0; | |
b32b8144 FG |
197 | ioc.restart(); |
198 | post(s, throw_exception); | |
199 | post(s, bindns::bind(increment, &count)); | |
200 | post(s, bindns::bind(increment, &count)); | |
201 | post(s, throw_exception); | |
202 | post(s, bindns::bind(increment, &count)); | |
7c673cae FG |
203 | |
204 | // No handlers can be called until run() is called. | |
205 | BOOST_ASIO_CHECK(count == 0); | |
206 | BOOST_ASIO_CHECK(exception_count == 0); | |
207 | ||
208 | for (;;) | |
209 | { | |
210 | try | |
211 | { | |
b32b8144 | 212 | ioc.run(); |
7c673cae FG |
213 | break; |
214 | } | |
215 | catch (int) | |
216 | { | |
217 | ++exception_count; | |
218 | } | |
219 | } | |
220 | ||
221 | // The run() calls will not return until all work has finished. | |
222 | BOOST_ASIO_CHECK(count == 3); | |
223 | BOOST_ASIO_CHECK(exception_count == 2); | |
224 | ||
225 | count = 0; | |
b32b8144 | 226 | ioc.restart(); |
7c673cae FG |
227 | |
228 | // Check for clean shutdown when handlers posted through an orphaned strand | |
229 | // are abandoned. | |
230 | { | |
92f5a8d4 | 231 | strand<io_context::executor_type> s2 = make_strand(ioc.get_executor()); |
b32b8144 FG |
232 | post(s2, bindns::bind(increment, &count)); |
233 | post(s2, bindns::bind(increment, &count)); | |
234 | post(s2, bindns::bind(increment, &count)); | |
7c673cae FG |
235 | } |
236 | ||
237 | // No handlers can be called until run() is called. | |
238 | BOOST_ASIO_CHECK(count == 0); | |
239 | } | |
240 | ||
241 | BOOST_ASIO_TEST_SUITE | |
242 | ( | |
243 | "strand", | |
244 | BOOST_ASIO_TEST_CASE(strand_test) | |
245 | ) |