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