1 // Copyright (c) 2011 Helge Bahmann
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)
7 // Attempt to determine whether the operations on atomic variables
8 // do in fact behave atomically: Let multiple threads race modifying
9 // a shared atomic variable and verify that it behaves as expected.
11 // We assume that "observable race condition" events are exponentially
12 // distributed, with unknown "average time between observable races"
13 // (which is just the reciprocal of exp distribution parameter lambda).
14 // Use a non-atomic implementation that intentionally exhibits a
15 // (hopefully tight) race to compute the maximum-likelihood estimate
16 // for this time. From this, compute an estimate that covers the
17 // unknown value with 0.995 confidence (using chi square quantile).
19 // Use this estimate to pick a timeout for the race tests of the
20 // atomic implementations such that under the assumed distribution
21 // we get 0.995 probability to detect a race (if there is one).
23 // Overall this yields 0.995 * 0.995 > 0.99 confidence that the
24 // operations truly behave atomic if this test program does not
27 #include <boost/atomic.hpp>
30 #include <boost/ref.hpp>
31 #include <boost/bind.hpp>
32 #include <boost/function.hpp>
33 #include <boost/date_time/posix_time/posix_time_types.hpp>
34 #include <boost/date_time/posix_time/time_formatters.hpp>
35 #include <boost/thread/thread.hpp>
36 #include <boost/thread/thread_time.hpp>
37 #include <boost/thread/locks.hpp>
38 #include <boost/thread/mutex.hpp>
39 #include <boost/thread/condition_variable.hpp>
40 #include <boost/core/lightweight_test.hpp>
42 /* helper class to let two instances of a function race against each
43 other, with configurable timeout and early abort on detection of error */
44 class concurrent_runner
{
46 /* concurrently run the function in two threads, until either timeout
47 or one of the functions returns "false"; returns true if timeout
48 was reached, or false if early abort and updates timeout accordingly */
51 const boost::function
<bool(size_t)> & fn
,
52 boost::posix_time::time_duration
& timeout
)
54 concurrent_runner
runner(fn
);
55 runner
.wait_finish(timeout
);
56 return !runner
.failure();
61 const boost::function
<bool(size_t)> & fn
)
62 : finished_(false), failure_(false)
64 boost::thread(boost::bind(&concurrent_runner::thread_function
, this, fn
, 0)).swap(first_thread_
);
65 boost::thread(boost::bind(&concurrent_runner::thread_function
, this, fn
, 1)).swap(second_thread_
);
69 wait_finish(boost::posix_time::time_duration
& timeout
)
71 boost::system_time start
= boost::get_system_time();
72 boost::system_time end
= start
+ timeout
;
75 boost::mutex::scoped_lock
guard(m_
);
76 while (boost::get_system_time() < end
&& !finished())
77 c_
.timed_wait(guard
, end
);
80 finished_
.store(true, boost::memory_order_relaxed
);
83 second_thread_
.join();
85 boost::posix_time::time_duration duration
= boost::get_system_time() - start
;
86 if (duration
< timeout
)
91 finished(void) const throw() {
92 return finished_
.load(boost::memory_order_relaxed
);
96 failure(void) const throw() {
101 thread_function(boost::function
<bool(size_t)> function
, size_t instance
)
103 while (!finished()) {
104 if (!function(instance
)) {
105 boost::mutex::scoped_lock
guard(m_
);
107 finished_
.store(true, boost::memory_order_relaxed
);
116 boost::condition_variable c_
;
118 boost::atomic
<bool> finished_
;
121 boost::thread first_thread_
;
122 boost::thread second_thread_
;
126 racy_add(volatile unsigned int & value
, size_t instance
)
128 size_t shift
= instance
* 8;
129 unsigned int mask
= 0xff << shift
;
130 for (size_t n
= 0; n
< 255; n
++) {
131 unsigned int tmp
= value
;
132 value
= tmp
+ (1 << shift
);
134 if ((tmp
& mask
) != (n
<< shift
))
138 unsigned int tmp
= value
;
140 if ((tmp
& mask
) != mask
)
146 /* compute estimate for average time between races being observable, in usecs */
148 estimate_avg_race_time(void)
152 /* take 10 samples */
153 for (size_t n
= 0; n
< 10; n
++) {
154 boost::posix_time::time_duration
timeout(0, 0, 10);
156 volatile unsigned int value(0);
157 bool success
= concurrent_runner::execute(
158 boost::bind(racy_add
, boost::ref(value
), _1
),
163 BOOST_ERROR("Failed to establish baseline time for reproducing race condition");
166 sum
= sum
+ timeout
.total_microseconds();
169 /* determine maximum likelihood estimate for average time between
171 double avg_race_time_mle
= (sum
/ 10);
173 /* pick 0.995 confidence (7.44 = chi square 0.995 confidence) */
174 double avg_race_time_995
= avg_race_time_mle
* 2 * 10 / 7.44;
176 return avg_race_time_995
;
179 template<typename value_type
, size_t shift_
>
181 test_arithmetic(boost::atomic
<value_type
> & shared_value
, size_t instance
)
183 size_t shift
= instance
* 8;
184 value_type mask
= 0xff << shift
;
185 value_type increment
= 1 << shift
;
187 value_type expected
= 0;
189 for (size_t n
= 0; n
< 255; n
++) {
190 value_type tmp
= shared_value
.fetch_add(increment
, boost::memory_order_relaxed
);
191 if ( (tmp
& mask
) != (expected
<< shift
) )
195 for (size_t n
= 0; n
< 255; n
++) {
196 value_type tmp
= shared_value
.fetch_sub(increment
, boost::memory_order_relaxed
);
197 if ( (tmp
& mask
) != (expected
<< shift
) )
205 template<typename value_type
, size_t shift_
>
207 test_bitops(boost::atomic
<value_type
> & shared_value
, size_t instance
)
209 size_t shift
= instance
* 8;
210 value_type mask
= 0xff << shift
;
212 value_type expected
= 0;
214 for (size_t k
= 0; k
< 8; k
++) {
215 value_type mod
= 1 << k
;
216 value_type tmp
= shared_value
.fetch_or(mod
<< shift
, boost::memory_order_relaxed
);
217 if ( (tmp
& mask
) != (expected
<< shift
))
219 expected
= expected
| mod
;
221 for (size_t k
= 0; k
< 8; k
++) {
222 value_type tmp
= shared_value
.fetch_and( ~ (1 << (shift
+ k
)), boost::memory_order_relaxed
);
223 if ( (tmp
& mask
) != (expected
<< shift
))
225 expected
= expected
& ~(1<<k
);
227 for (size_t k
= 0; k
< 8; k
++) {
228 value_type mod
= 255 ^ (1 << k
);
229 value_type tmp
= shared_value
.fetch_xor(mod
<< shift
, boost::memory_order_relaxed
);
230 if ( (tmp
& mask
) != (expected
<< shift
))
232 expected
= expected
^ mod
;
235 value_type tmp
= shared_value
.fetch_and( ~mask
, boost::memory_order_relaxed
);
236 if ( (tmp
& mask
) != (expected
<< shift
) )
242 int main(int, char *[])
244 boost::posix_time::time_duration reciprocal_lambda
;
246 double avg_race_time
= estimate_avg_race_time();
248 /* 5.298 = 0.995 quantile of exponential distribution */
249 const boost::posix_time::time_duration timeout
= boost::posix_time::microseconds((long)(5.298 * avg_race_time
));
252 boost::atomic
<unsigned int> value(0);
254 /* testing two different operations in this loop, therefore
256 boost::posix_time::time_duration
tmp(timeout
* 2);
258 bool success
= concurrent_runner::execute(
259 boost::bind(test_arithmetic
<unsigned int, 0>, boost::ref(value
), _1
),
263 BOOST_TEST(success
); // concurrent arithmetic error
267 boost::atomic
<unsigned int> value(0);
269 /* testing three different operations in this loop, therefore
271 boost::posix_time::time_duration
tmp(timeout
* 3);
273 bool success
= concurrent_runner::execute(
274 boost::bind(test_bitops
<unsigned int, 0>, boost::ref(value
), _1
),
278 BOOST_TEST(success
); // concurrent bit operations error
281 return boost::report_errors();