1 /*
2 * Copyright Andrey Semashev 2007 - 2015.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
6 */
7 /*!
8 * \file util_once_block.cpp
9 * \author Andrey Semashev
10 * \date 24.06.2010
11 *
12 * \brief This header contains tests for once-blocks implementation.
13 *
14 * The test was adopted from test_once.cpp test of Boost.Thread.
15 */
16
17 #define BOOST_TEST_MODULE util_once_block
18
19 #include <boost/log/utility/once_block.hpp>
20 #include <boost/test/unit_test.hpp>
21
22 #if !defined(BOOST_LOG_NO_THREADS)
23
24 #include <boost/ref.hpp>
25 #include <boost/bind.hpp>
26 #include <boost/thread/thread.hpp>
27 #include <boost/thread/mutex.hpp>
28 #include <boost/thread/locks.hpp>
29 #include <boost/thread/barrier.hpp>
30
31 namespace logging = boost::log;
32
33 enum config
34 {
35 THREAD_COUNT = 20,
36 LOOP_COUNT = 100
37 };
38
39 boost::mutex m;
40 typedef boost::lock_guard< boost::mutex > scoped_lock;
41
42 logging::once_block_flag flag = BOOST_LOG_ONCE_BLOCK_INIT;
43 int var_to_init_once_flag = 0;
44
initialize_variable()45 void initialize_variable()
46 {
47 // ensure that if multiple threads get in here, they are serialized, so we can see the effect
48 scoped_lock lock(m);
49 ++var_to_init_once_flag;
50 }
51
52
once_block_flag_thread(boost::barrier & barrier)53 void once_block_flag_thread(boost::barrier& barrier)
54 {
55 int my_once_value = 0;
56 barrier.wait();
57 for (unsigned int i = 0; i < LOOP_COUNT; ++i)
58 {
59 BOOST_LOG_ONCE_BLOCK_FLAG(flag)
60 {
61 initialize_variable();
62 }
63
64 my_once_value = var_to_init_once_flag;
65 if (my_once_value != 1)
66 {
67 break;
68 }
69 }
70 scoped_lock lock(m);
71 BOOST_CHECK_EQUAL(my_once_value, 1);
72 }
73
74 // The test checks if the BOOST_LOG_ONCE_BLOCK_FLAG macro works
BOOST_AUTO_TEST_CASE(once_block_flag)75 BOOST_AUTO_TEST_CASE(once_block_flag)
76 {
77 boost::thread_group group;
78 boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT));
79
80 try
81 {
82 for (unsigned int i = 0; i < THREAD_COUNT; ++i)
83 {
84 group.create_thread(boost::bind(&once_block_flag_thread, boost::ref(barrier)));
85 }
86 group.join_all();
87 }
88 catch (...)
89 {
90 group.interrupt_all();
91 group.join_all();
92 throw;
93 }
94
95 BOOST_CHECK_EQUAL(var_to_init_once_flag, 1);
96 }
97
98 int var_to_init_once = 0;
99
once_block_thread(boost::barrier & barrier)100 void once_block_thread(boost::barrier& barrier)
101 {
102 int my_once_value = 0;
103 barrier.wait();
104 for (unsigned int i = 0; i < LOOP_COUNT; ++i)
105 {
106 BOOST_LOG_ONCE_BLOCK()
107 {
108 scoped_lock lock(m);
109 ++var_to_init_once;
110 }
111
112 my_once_value = var_to_init_once;
113 if (my_once_value != 1)
114 {
115 break;
116 }
117 }
118
119 scoped_lock lock(m);
120 BOOST_CHECK_EQUAL(my_once_value, 1);
121 }
122
123 // The test checks if the BOOST_LOG_ONCE_BLOCK macro works
BOOST_AUTO_TEST_CASE(once_block)124 BOOST_AUTO_TEST_CASE(once_block)
125 {
126 boost::thread_group group;
127 boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT));
128
129 try
130 {
131 for (unsigned int i = 0; i < THREAD_COUNT; ++i)
132 {
133 group.create_thread(boost::bind(&once_block_thread, boost::ref(barrier)));
134 }
135 group.join_all();
136 }
137 catch(...)
138 {
139 group.interrupt_all();
140 group.join_all();
141 throw;
142 }
143
144 BOOST_CHECK_EQUAL(var_to_init_once, 1);
145 }
146
147
148 struct my_exception
149 {
150 };
151
152 unsigned int pass_counter = 0;
153 unsigned int exception_counter = 0;
154
once_block_with_exception_thread(boost::barrier & barrier)155 void once_block_with_exception_thread(boost::barrier& barrier)
156 {
157 barrier.wait();
158 try
159 {
160 BOOST_LOG_ONCE_BLOCK()
161 {
162 scoped_lock lock(m);
163 ++pass_counter;
164 if (pass_counter < 3)
165 {
166 throw my_exception();
167 }
168 }
169 }
170 catch (my_exception&)
171 {
172 scoped_lock lock(m);
173 ++exception_counter;
174 }
175 }
176
177 // The test verifies that the once_block flag is not set if an exception is thrown from the once-block
BOOST_AUTO_TEST_CASE(once_block_retried_on_exception)178 BOOST_AUTO_TEST_CASE(once_block_retried_on_exception)
179 {
180 boost::thread_group group;
181 boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT));
182
183 try
184 {
185 for (unsigned int i = 0; i < THREAD_COUNT; ++i)
186 {
187 group.create_thread(boost::bind(&once_block_with_exception_thread, boost::ref(barrier)));
188 }
189 group.join_all();
190 }
191 catch(...)
192 {
193 group.interrupt_all();
194 group.join_all();
195 throw;
196 }
197
198 BOOST_CHECK_EQUAL(pass_counter, 3u);
199 BOOST_CHECK_EQUAL(exception_counter, 2u);
200 }
201
202 #else // BOOST_LOG_NO_THREADS
203
204 namespace logging = boost::log;
205
206 enum config
207 {
208 LOOP_COUNT = 100
209 };
210
211 logging::once_block_flag flag = BOOST_LOG_ONCE_BLOCK_INIT;
212 int var_to_init_once_flag = 0;
213
initialize_variable()214 void initialize_variable()
215 {
216 ++var_to_init_once_flag;
217 }
218
219
220 // The test checks if the BOOST_LOG_ONCE_BLOCK_FLAG macro works
BOOST_AUTO_TEST_CASE(once_block_flag)221 BOOST_AUTO_TEST_CASE(once_block_flag)
222 {
223 for (unsigned int i = 0; i < LOOP_COUNT; ++i)
224 {
225 BOOST_LOG_ONCE_BLOCK_FLAG(flag)
226 {
227 initialize_variable();
228 }
229
230 if (var_to_init_once_flag != 1)
231 {
232 break;
233 }
234 }
235
236 BOOST_CHECK_EQUAL(var_to_init_once_flag, 1);
237 }
238
239 int var_to_init_once = 0;
240
241 // The test checks if the BOOST_LOG_ONCE_BLOCK macro works
BOOST_AUTO_TEST_CASE(once_block)242 BOOST_AUTO_TEST_CASE(once_block)
243 {
244 for (unsigned int i = 0; i < LOOP_COUNT; ++i)
245 {
246 BOOST_LOG_ONCE_BLOCK()
247 {
248 ++var_to_init_once;
249 }
250
251 if (var_to_init_once != 1)
252 {
253 break;
254 }
255 }
256
257 BOOST_CHECK_EQUAL(var_to_init_once, 1);
258 }
259
260 struct my_exception
261 {
262 };
263
264 unsigned int pass_counter = 0;
265 unsigned int exception_counter = 0;
266
267 // The test verifies that the once_block flag is not set if an exception is thrown from the once-block
BOOST_AUTO_TEST_CASE(once_block_retried_on_exception)268 BOOST_AUTO_TEST_CASE(once_block_retried_on_exception)
269 {
270 for (unsigned int i = 0; i < LOOP_COUNT; ++i)
271 {
272 try
273 {
274 BOOST_LOG_ONCE_BLOCK()
275 {
276 ++pass_counter;
277 if (pass_counter < 3)
278 {
279 throw my_exception();
280 }
281 }
282 }
283 catch (my_exception&)
284 {
285 ++exception_counter;
286 }
287 }
288
289 BOOST_CHECK_EQUAL(pass_counter, 3u);
290 BOOST_CHECK_EQUAL(exception_counter, 2u);
291 }
292
293 #endif // BOOST_LOG_NO_THREADS
294