1//
2// strand.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/strand.hpp>
18
19#include <sstream>
20#include <boost/asio/io_service.hpp>
21#include <boost/asio/detail/thread.hpp>
22#include "unit_test.hpp"
23
24#if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
25# include <boost/asio/deadline_timer.hpp>
26#else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
27# include <boost/asio/steady_timer.hpp>
28#endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
29
30#if defined(BOOST_ASIO_HAS_BOOST_BIND)
31# include <boost/bind.hpp>
32#else // defined(BOOST_ASIO_HAS_BOOST_BIND)
33# include <functional>
34#endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
35
36using namespace boost::asio;
37
38#if defined(BOOST_ASIO_HAS_BOOST_BIND)
39namespace bindns = boost;
40#else // defined(BOOST_ASIO_HAS_BOOST_BIND)
41namespace bindns = std;
42#endif
43
44#if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
45typedef deadline_timer timer;
46namespace chronons = boost::posix_time;
47#elif defined(BOOST_ASIO_HAS_STD_CHRONO)
48typedef steady_timer timer;
49namespace chronons = std::chrono;
50#elif defined(BOOST_ASIO_HAS_BOOST_CHRONO)
51typedef steady_timer timer;
52namespace chronons = boost::chrono;
53#endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
54
55void increment(int* count)
56{
57 ++(*count);
58}
59
60void increment_without_lock(io_service::strand* s, int* count)
61{
62 BOOST_ASIO_CHECK(!s->running_in_this_thread());
63
64 int original_count = *count;
65
66 s->dispatch(handler: bindns::bind(f: increment, a1: count));
67
68 // No other functions are currently executing through the locking dispatcher,
69 // so the previous call to dispatch should have successfully nested.
70 BOOST_ASIO_CHECK(*count == original_count + 1);
71}
72
73void increment_with_lock(io_service::strand* s, int* count)
74{
75 BOOST_ASIO_CHECK(s->running_in_this_thread());
76
77 int original_count = *count;
78
79 s->dispatch(handler: bindns::bind(f: increment, a1: count));
80
81 // The current function already holds the strand's lock, so the
82 // previous call to dispatch should have successfully nested.
83 BOOST_ASIO_CHECK(*count == original_count + 1);
84}
85
86void sleep_increment(io_service* ios, int* count)
87{
88 timer t(*ios, chronons::seconds(2));
89 t.wait();
90
91 ++(*count);
92}
93
94void start_sleep_increments(io_service* ios, io_service::strand* s, int* count)
95{
96 // Give all threads a chance to start.
97 timer t(*ios, chronons::seconds(2));
98 t.wait();
99
100 // Start three increments.
101 s->post(handler: bindns::bind(f: sleep_increment, a1: ios, a2: count));
102 s->post(handler: bindns::bind(f: sleep_increment, a1: ios, a2: count));
103 s->post(handler: bindns::bind(f: sleep_increment, a1: ios, a2: count));
104}
105
106void throw_exception()
107{
108 throw 1;
109}
110
111void io_service_run(io_service* ios)
112{
113 ios->run();
114}
115
116void strand_test()
117{
118 io_service ios;
119 io_service::strand s(ios);
120 int count = 0;
121
122 ios.post(handler: bindns::bind(f: increment_without_lock, a1: &s, a2: &count));
123
124 // No handlers can be called until run() is called.
125 BOOST_ASIO_CHECK(count == 0);
126
127 ios.run();
128
129 // The run() call will not return until all work has finished.
130 BOOST_ASIO_CHECK(count == 1);
131
132 count = 0;
133 ios.reset();
134 s.post(handler: bindns::bind(f: increment_with_lock, a1: &s, a2: &count));
135
136 // No handlers can be called until run() is called.
137 BOOST_ASIO_CHECK(count == 0);
138
139 ios.run();
140
141 // The run() call will not return until all work has finished.
142 BOOST_ASIO_CHECK(count == 1);
143
144 count = 0;
145 ios.reset();
146 ios.post(handler: bindns::bind(f: start_sleep_increments, a1: &ios, a2: &s, a3: &count));
147 boost::asio::detail::thread thread1(bindns::bind(f: io_service_run, a1: &ios));
148 boost::asio::detail::thread thread2(bindns::bind(f: io_service_run, a1: &ios));
149
150 // Check all events run one after another even though there are two threads.
151 timer timer1(ios, chronons::seconds(3));
152 timer1.wait();
153 BOOST_ASIO_CHECK(count == 0);
154 timer1.expires_at(expiry_time: timer1.expires_at() + chronons::seconds(2));
155 timer1.wait();
156 BOOST_ASIO_CHECK(count == 1);
157 timer1.expires_at(expiry_time: timer1.expires_at() + chronons::seconds(2));
158 timer1.wait();
159 BOOST_ASIO_CHECK(count == 2);
160
161 thread1.join();
162 thread2.join();
163
164 // The run() calls will not return until all work has finished.
165 BOOST_ASIO_CHECK(count == 3);
166
167 count = 0;
168 int exception_count = 0;
169 ios.reset();
170 s.post(handler&: throw_exception);
171 s.post(handler: bindns::bind(f: increment, a1: &count));
172 s.post(handler: bindns::bind(f: increment, a1: &count));
173 s.post(handler&: throw_exception);
174 s.post(handler: bindns::bind(f: increment, a1: &count));
175
176 // No handlers can be called until run() is called.
177 BOOST_ASIO_CHECK(count == 0);
178 BOOST_ASIO_CHECK(exception_count == 0);
179
180 for (;;)
181 {
182 try
183 {
184 ios.run();
185 break;
186 }
187 catch (int)
188 {
189 ++exception_count;
190 }
191 }
192
193 // The run() calls will not return until all work has finished.
194 BOOST_ASIO_CHECK(count == 3);
195 BOOST_ASIO_CHECK(exception_count == 2);
196
197 count = 0;
198 ios.reset();
199
200 // Check for clean shutdown when handlers posted through an orphaned strand
201 // are abandoned.
202 {
203 strand s2(ios);
204 s2.post(handler: bindns::bind(f: increment, a1: &count));
205 s2.post(handler: bindns::bind(f: increment, a1: &count));
206 s2.post(handler: bindns::bind(f: increment, a1: &count));
207 }
208
209 // No handlers can be called until run() is called.
210 BOOST_ASIO_CHECK(count == 0);
211}
212
213BOOST_ASIO_TEST_SUITE
214(
215 "strand",
216 BOOST_ASIO_TEST_CASE(strand_test)
217)
218

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