atomicity.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. // Copyright (c) 2011 Helge Bahmann
  2. //
  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. // Attempt to determine whether the operations on atomic variables
  7. // do in fact behave atomically: Let multiple threads race modifying
  8. // a shared atomic variable and verify that it behaves as expected.
  9. //
  10. // We assume that "observable race condition" events are exponentially
  11. // distributed, with unknown "average time between observable races"
  12. // (which is just the reciprocal of exp distribution parameter lambda).
  13. // Use a non-atomic implementation that intentionally exhibits a
  14. // (hopefully tight) race to compute the maximum-likelihood estimate
  15. // for this time. From this, compute an estimate that covers the
  16. // unknown value with 0.995 confidence (using chi square quantile).
  17. //
  18. // Use this estimate to pick a timeout for the race tests of the
  19. // atomic implementations such that under the assumed distribution
  20. // we get 0.995 probability to detect a race (if there is one).
  21. //
  22. // Overall this yields 0.995 * 0.995 > 0.99 confidence that the
  23. // operations truly behave atomic if this test program does not
  24. // report an error.
  25. #include <boost/atomic.hpp>
  26. #include <algorithm>
  27. #include <boost/ref.hpp>
  28. #include <boost/bind.hpp>
  29. #include <boost/function.hpp>
  30. #include <boost/date_time/posix_time/posix_time_types.hpp>
  31. #include <boost/thread/thread.hpp>
  32. #include <boost/thread/thread_time.hpp>
  33. #include <boost/thread/locks.hpp>
  34. #include <boost/thread/mutex.hpp>
  35. #include <boost/thread/condition_variable.hpp>
  36. #include <boost/core/lightweight_test.hpp>
  37. /* helper class to let two instances of a function race against each
  38. other, with configurable timeout and early abort on detection of error */
  39. class concurrent_runner {
  40. public:
  41. /* concurrently run the function in two threads, until either timeout
  42. or one of the functions returns "false"; returns true if timeout
  43. was reached, or false if early abort and updates timeout accordingly */
  44. static bool
  45. execute(
  46. const boost::function<bool(size_t)> & fn,
  47. boost::posix_time::time_duration & timeout)
  48. {
  49. concurrent_runner runner(fn);
  50. runner.wait_finish(timeout);
  51. return !runner.failure();
  52. }
  53. concurrent_runner(
  54. const boost::function<bool(size_t)> & fn)
  55. : finished_(false), failure_(false)
  56. {
  57. boost::thread(boost::bind(&concurrent_runner::thread_function, this, fn, 0)).swap(first_thread_);
  58. boost::thread(boost::bind(&concurrent_runner::thread_function, this, fn, 1)).swap(second_thread_);
  59. }
  60. void
  61. wait_finish(boost::posix_time::time_duration & timeout)
  62. {
  63. boost::system_time start = boost::get_system_time();
  64. boost::system_time end = start + timeout;
  65. {
  66. boost::mutex::scoped_lock guard(m_);
  67. while (boost::get_system_time() < end && !finished())
  68. c_.timed_wait(guard, end);
  69. }
  70. finished_.store(true, boost::memory_order_relaxed);
  71. first_thread_.join();
  72. second_thread_.join();
  73. boost::posix_time::time_duration duration = boost::get_system_time() - start;
  74. if (duration < timeout)
  75. timeout = duration;
  76. }
  77. bool
  78. finished(void) const throw() {
  79. return finished_.load(boost::memory_order_relaxed);
  80. }
  81. bool
  82. failure(void) const throw() {
  83. return failure_;
  84. }
  85. private:
  86. void
  87. thread_function(boost::function<bool(size_t)> function, size_t instance)
  88. {
  89. while (!finished()) {
  90. if (!function(instance)) {
  91. boost::mutex::scoped_lock guard(m_);
  92. failure_ = true;
  93. finished_.store(true, boost::memory_order_relaxed);
  94. c_.notify_all();
  95. break;
  96. }
  97. }
  98. }
  99. boost::mutex m_;
  100. boost::condition_variable c_;
  101. boost::atomic<bool> finished_;
  102. bool failure_;
  103. boost::thread first_thread_;
  104. boost::thread second_thread_;
  105. };
  106. bool
  107. racy_add(volatile unsigned int & value, size_t instance)
  108. {
  109. size_t shift = instance * 8;
  110. unsigned int mask = 0xff << shift;
  111. for (size_t n = 0; n < 255; n++) {
  112. unsigned int tmp = value;
  113. value = tmp + (1 << shift);
  114. if ((tmp & mask) != (n << shift))
  115. return false;
  116. }
  117. unsigned int tmp = value;
  118. value = tmp & ~mask;
  119. if ((tmp & mask) != mask)
  120. return false;
  121. return true;
  122. }
  123. /* compute estimate for average time between races being observable, in usecs */
  124. static double
  125. estimate_avg_race_time(void)
  126. {
  127. double sum = 0.0;
  128. /* take 10 samples */
  129. for (size_t n = 0; n < 10; n++) {
  130. boost::posix_time::time_duration timeout(0, 0, 10);
  131. volatile unsigned int value(0);
  132. bool success = concurrent_runner::execute(
  133. boost::bind(racy_add, boost::ref(value), _1),
  134. timeout
  135. );
  136. if (success) {
  137. BOOST_ERROR("Failed to establish baseline time for reproducing race condition");
  138. }
  139. sum = sum + timeout.total_microseconds();
  140. }
  141. /* determine maximum likelihood estimate for average time between
  142. race observations */
  143. double avg_race_time_mle = (sum / 10);
  144. /* pick 0.995 confidence (7.44 = chi square 0.995 confidence) */
  145. double avg_race_time_995 = avg_race_time_mle * 2 * 10 / 7.44;
  146. return avg_race_time_995;
  147. }
  148. template<typename value_type, size_t shift_>
  149. bool
  150. test_arithmetic(boost::atomic<value_type> & shared_value, size_t instance)
  151. {
  152. size_t shift = instance * 8;
  153. value_type mask = 0xff << shift;
  154. value_type increment = 1 << shift;
  155. value_type expected = 0;
  156. for (size_t n = 0; n < 255; n++) {
  157. value_type tmp = shared_value.fetch_add(increment, boost::memory_order_relaxed);
  158. if ( (tmp & mask) != (expected << shift) )
  159. return false;
  160. expected ++;
  161. }
  162. for (size_t n = 0; n < 255; n++) {
  163. value_type tmp = shared_value.fetch_sub(increment, boost::memory_order_relaxed);
  164. if ( (tmp & mask) != (expected << shift) )
  165. return false;
  166. expected --;
  167. }
  168. return true;
  169. }
  170. template<typename value_type, size_t shift_>
  171. bool
  172. test_bitops(boost::atomic<value_type> & shared_value, size_t instance)
  173. {
  174. size_t shift = instance * 8;
  175. value_type mask = 0xff << shift;
  176. value_type expected = 0;
  177. for (size_t k = 0; k < 8; k++) {
  178. value_type mod = 1 << k;
  179. value_type tmp = shared_value.fetch_or(mod << shift, boost::memory_order_relaxed);
  180. if ( (tmp & mask) != (expected << shift))
  181. return false;
  182. expected = expected | mod;
  183. }
  184. for (size_t k = 0; k < 8; k++) {
  185. value_type tmp = shared_value.fetch_and( ~ (1 << (shift + k)), boost::memory_order_relaxed);
  186. if ( (tmp & mask) != (expected << shift))
  187. return false;
  188. expected = expected & ~(1<<k);
  189. }
  190. for (size_t k = 0; k < 8; k++) {
  191. value_type mod = 255 ^ (1 << k);
  192. value_type tmp = shared_value.fetch_xor(mod << shift, boost::memory_order_relaxed);
  193. if ( (tmp & mask) != (expected << shift))
  194. return false;
  195. expected = expected ^ mod;
  196. }
  197. value_type tmp = shared_value.fetch_and( ~mask, boost::memory_order_relaxed);
  198. if ( (tmp & mask) != (expected << shift) )
  199. return false;
  200. return true;
  201. }
  202. int main(int, char *[])
  203. {
  204. boost::posix_time::time_duration reciprocal_lambda;
  205. double avg_race_time = estimate_avg_race_time();
  206. /* 5.298 = 0.995 quantile of exponential distribution */
  207. const boost::posix_time::time_duration timeout = boost::posix_time::microseconds((long)(5.298 * avg_race_time));
  208. {
  209. boost::atomic<unsigned int> value(0);
  210. /* testing two different operations in this loop, therefore
  211. enlarge timeout */
  212. boost::posix_time::time_duration tmp(timeout * 2);
  213. bool success = concurrent_runner::execute(
  214. boost::bind(test_arithmetic<unsigned int, 0>, boost::ref(value), _1),
  215. tmp
  216. );
  217. BOOST_TEST(success); // concurrent arithmetic error
  218. }
  219. {
  220. boost::atomic<unsigned int> value(0);
  221. /* testing three different operations in this loop, therefore
  222. enlarge timeout */
  223. boost::posix_time::time_duration tmp(timeout * 3);
  224. bool success = concurrent_runner::execute(
  225. boost::bind(test_bitops<unsigned int, 0>, boost::ref(value), _1),
  226. tmp
  227. );
  228. BOOST_TEST(success); // concurrent bit operations error
  229. }
  230. return boost::report_errors();
  231. }