1 // Copyright (c) 2011 Helge Bahmann
2 // Copyright (c) 2012 Tim Blechmann
3 //
4 // Distributed under the Boost Software License, Version 1.0.
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7
8 // Attempt to determine whether the memory ordering/ fence operations
9 // work as expected:
10 // Let two threads race accessing multiple shared variables and
11 // verify that "observable" order of operations matches with the
12 // ordering constraints specified.
13 //
14 // We assume that "memory ordering violation" events are exponentially
15 // distributed, with unknown "average time between violations"
16 // (which is just the reciprocal of exp distribution parameter lambda).
17 // Use a "relaxed ordering" implementation that intentionally exhibits
18 // a (hopefully observable) such violation to compute the maximum-likelihood
19 // estimate for this time. From this, compute an estimate that covers the
20 // unknown value with 0.995 confidence (using chi square quantile).
21 //
22 // Use this estimate to pick a timeout for the race tests of the
23 // atomic implementations such that under the assumed distribution
24 // we get 0.995 probability to detect a race (if there is one).
25 //
26 // Overall this yields 0.995 * 0.995 > 0.99 confidence that the
27 // fences work as expected if this test program does not
28 // report an error.
29
30 #include <boost/atomic.hpp>
31
32 #include <cstddef>
33 #include <boost/bind/bind.hpp>
34 #include <boost/date_time/posix_time/posix_time_types.hpp>
35 #include <boost/thread/thread.hpp>
36 #include <boost/thread/thread_time.hpp>
37 #include <boost/thread/lock_guard.hpp>
38 #include <boost/thread/lock_types.hpp>
39 #include <boost/thread/mutex.hpp>
40 #include <boost/thread/condition_variable.hpp>
41 #include <boost/thread/barrier.hpp>
42 #include <boost/core/lightweight_test.hpp>
43
44 // Two threads perform the following operations:
45 //
46 // thread # 1 thread # 2
47 // store(a, 1) store(b, 1)
48 // x = read(b) y = read(a)
49 //
50 // Under relaxed memory ordering, the case (x, y) == (0, 0) is
51 // possible. Under sequential consistency, this case is impossible.
52 //
53 // This "problem" is reproducible on all platforms, even x86.
54 template<boost::memory_order store_order, boost::memory_order load_order>
55 class total_store_order_test
56 {
57 public:
58 total_store_order_test(void);
59
60 void run(boost::posix_time::time_duration & timeout);
detected_conflict(void) const61 bool detected_conflict(void) const { return detected_conflict_; }
62
63 private:
64 void thread1fn(void);
65 void thread2fn(void);
66 void check_conflict(void);
67
68 private:
69 boost::atomic<int> a_;
70 /* insert a bit of padding to push the two variables into
71 different cache lines and increase the likelihood of detecting
72 a conflict */
73 char pad1_[512];
74 boost::atomic<int> b_;
75
76 char pad2_[512];
77 boost::barrier barrier_;
78
79 int vrfyb1_, vrfya2_;
80
81 boost::atomic<bool> terminate_threads_;
82 boost::atomic<int> termination_consensus_;
83
84 bool detected_conflict_;
85 boost::mutex m_;
86 boost::condition_variable c_;
87 };
88
89 template<boost::memory_order store_order, boost::memory_order load_order>
total_store_order_test(void)90 total_store_order_test<store_order, load_order>::total_store_order_test(void) :
91 a_(0), b_(0), barrier_(2),
92 vrfyb1_(0), vrfya2_(0),
93 terminate_threads_(false), termination_consensus_(0),
94 detected_conflict_(false)
95 {
96 }
97
98 template<boost::memory_order store_order, boost::memory_order load_order>
run(boost::posix_time::time_duration & timeout)99 void total_store_order_test<store_order, load_order>::run(boost::posix_time::time_duration & timeout)
100 {
101 boost::system_time start = boost::get_system_time();
102 boost::system_time end = start + timeout;
103
104 boost::thread t1(boost::bind(&total_store_order_test::thread1fn, this));
105 boost::thread t2(boost::bind(&total_store_order_test::thread2fn, this));
106
107 {
108 boost::unique_lock< boost::mutex > guard(m_);
109 while (boost::get_system_time() < end && !detected_conflict_)
110 c_.timed_wait(guard, end);
111 }
112
113 terminate_threads_.store(true, boost::memory_order_relaxed);
114
115 t2.join();
116 t1.join();
117
118 boost::posix_time::time_duration duration = boost::get_system_time() - start;
119 if (duration < timeout)
120 timeout = duration;
121 }
122
123 volatile int backoff_dummy;
124
125 template<boost::memory_order store_order, boost::memory_order load_order>
thread1fn(void)126 void total_store_order_test<store_order, load_order>::thread1fn(void)
127 {
128 while (true)
129 {
130 a_.store(1, store_order);
131 int b = b_.load(load_order);
132
133 barrier_.wait();
134
135 vrfyb1_ = b;
136
137 barrier_.wait();
138
139 check_conflict();
140
141 /* both threads synchronize via barriers, so either
142 both threads must exit here, or they must both do
143 another round, otherwise one of them will wait forever */
144 if (terminate_threads_.load(boost::memory_order_relaxed))
145 {
146 while (true)
147 {
148 int tmp = termination_consensus_.fetch_or(1, boost::memory_order_relaxed);
149
150 if (tmp == 3)
151 return;
152 if (tmp & 4)
153 break;
154 }
155 }
156
157 termination_consensus_.fetch_xor(4, boost::memory_order_relaxed);
158
159 unsigned int delay = rand() % 10000;
160 a_.store(0, boost::memory_order_relaxed);
161
162 barrier_.wait();
163
164 while (delay--)
165 backoff_dummy = delay;
166 }
167 }
168
169 template<boost::memory_order store_order, boost::memory_order load_order>
thread2fn(void)170 void total_store_order_test<store_order, load_order>::thread2fn(void)
171 {
172 while (true)
173 {
174 b_.store(1, store_order);
175 int a = a_.load(load_order);
176
177 barrier_.wait();
178
179 vrfya2_ = a;
180
181 barrier_.wait();
182
183 check_conflict();
184
185 /* both threads synchronize via barriers, so either
186 both threads must exit here, or they must both do
187 another round, otherwise one of them will wait forever */
188 if (terminate_threads_.load(boost::memory_order_relaxed))
189 {
190 while (true)
191 {
192 int tmp = termination_consensus_.fetch_or(2, boost::memory_order_relaxed);
193
194 if (tmp == 3)
195 return;
196 if (tmp & 4)
197 break;
198 }
199 }
200
201 termination_consensus_.fetch_xor(4, boost::memory_order_relaxed);
202
203 unsigned int delay = rand() % 10000;
204 b_.store(0, boost::memory_order_relaxed);
205
206 barrier_.wait();
207
208 while (delay--)
209 backoff_dummy = delay;
210 }
211 }
212
213 template<boost::memory_order store_order, boost::memory_order load_order>
check_conflict(void)214 void total_store_order_test<store_order, load_order>::check_conflict(void)
215 {
216 if (vrfyb1_ == 0 && vrfya2_ == 0)
217 {
218 boost::lock_guard< boost::mutex > guard(m_);
219 detected_conflict_ = true;
220 terminate_threads_.store(true, boost::memory_order_relaxed);
221 c_.notify_all();
222 }
223 }
224
test_seq_cst(void)225 void test_seq_cst(void)
226 {
227 double sum = 0.0;
228
229 /* take 10 samples */
230 for (std::size_t n = 0; n < 10; n++)
231 {
232 boost::posix_time::time_duration timeout(0, 0, 10);
233
234 total_store_order_test<boost::memory_order_relaxed, boost::memory_order_relaxed> test;
235 test.run(timeout);
236 if (!test.detected_conflict())
237 {
238 std::cout << "Failed to detect order=seq_cst violation while ith order=relaxed -- intrinsic ordering too strong for this test\n";
239 return;
240 }
241
242 std::cout << "seq_cst violation with order=relaxed after " << timeout.total_microseconds() << " us\n";
243
244 sum = sum + timeout.total_microseconds();
245 }
246
247 /* determine maximum likelihood estimate for average time between
248 race observations */
249 double avg_race_time_mle = (sum / 10);
250
251 /* pick 0.995 confidence (7.44 = chi square 0.995 confidence) */
252 double avg_race_time_995 = avg_race_time_mle * 2 * 10 / 7.44;
253
254 /* 5.298 = 0.995 quantile of exponential distribution */
255 boost::posix_time::time_duration timeout = boost::posix_time::microseconds((long)(5.298 * avg_race_time_995));
256
257 std::cout << "run seq_cst for " << timeout.total_microseconds() << " us\n";
258
259 total_store_order_test<boost::memory_order_seq_cst, boost::memory_order_seq_cst> test;
260 test.run(timeout);
261
262 BOOST_TEST(!test.detected_conflict()); // sequential consistency error
263 }
264
main(int,char * [])265 int main(int, char *[])
266 {
267 test_seq_cst();
268
269 return boost::report_errors();
270 }
271