FutexTest.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Copyright 2013-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <folly/detail/Futex.h>
  17. #include <folly/test/DeterministicSchedule.h>
  18. #include <chrono>
  19. #include <condition_variable>
  20. #include <functional>
  21. #include <ratio>
  22. #include <thread>
  23. #include <glog/logging.h>
  24. #include <folly/Chrono.h>
  25. #include <folly/portability/GTest.h>
  26. #include <folly/portability/Time.h>
  27. using namespace folly::detail;
  28. using namespace folly::test;
  29. using namespace std;
  30. using namespace std::chrono;
  31. using folly::chrono::coarse_steady_clock;
  32. typedef DeterministicSchedule DSched;
  33. template <template <typename> class Atom>
  34. void run_basic_thread(Futex<Atom>& f) {
  35. EXPECT_EQ(FutexResult::AWOKEN, futexWait(&f, 0));
  36. }
  37. template <template <typename> class Atom>
  38. void run_basic_tests() {
  39. Futex<Atom> f(0);
  40. EXPECT_EQ(FutexResult::VALUE_CHANGED, futexWait(&f, 1));
  41. EXPECT_EQ(futexWake(&f), 0);
  42. auto thr = DSched::thread(std::bind(run_basic_thread<Atom>, std::ref(f)));
  43. while (futexWake(&f) != 1) {
  44. std::this_thread::yield();
  45. }
  46. DSched::join(thr);
  47. }
  48. template <template <typename> class Atom, typename Clock, typename Duration>
  49. void liveClockWaitUntilTests() {
  50. Futex<Atom> f(0);
  51. for (int stress = 0; stress < 1000; ++stress) {
  52. auto fp = &f; // workaround for t5336595
  53. auto thrA = DSched::thread([fp, stress] {
  54. while (true) {
  55. const auto deadline = time_point_cast<Duration>(
  56. Clock::now() + microseconds(1 << (stress % 20)));
  57. const auto res = futexWaitUntil(fp, 0, deadline);
  58. EXPECT_TRUE(res == FutexResult::TIMEDOUT || res == FutexResult::AWOKEN);
  59. if (res == FutexResult::AWOKEN) {
  60. break;
  61. }
  62. }
  63. });
  64. while (futexWake(&f) != 1) {
  65. std::this_thread::yield();
  66. }
  67. DSched::join(thrA);
  68. }
  69. {
  70. const auto start = Clock::now();
  71. const auto deadline = time_point_cast<Duration>(start + milliseconds(100));
  72. EXPECT_EQ(futexWaitUntil(&f, 0, deadline), FutexResult::TIMEDOUT);
  73. LOG(INFO) << "Futex wait timed out after waiting for "
  74. << duration_cast<milliseconds>(Clock::now() - start).count()
  75. << "ms using clock with " << Duration::period::den
  76. << " precision, should be ~100ms";
  77. }
  78. {
  79. const auto start = Clock::now();
  80. const auto deadline =
  81. time_point_cast<Duration>(start - 2 * start.time_since_epoch());
  82. EXPECT_EQ(futexWaitUntil(&f, 0, deadline), FutexResult::TIMEDOUT);
  83. LOG(INFO) << "Futex wait with invalid deadline timed out after waiting for "
  84. << duration_cast<milliseconds>(Clock::now() - start).count()
  85. << "ms using clock with " << Duration::period::den
  86. << " precision, should be ~0ms";
  87. }
  88. }
  89. template <typename Clock>
  90. void deterministicAtomicWaitUntilTests() {
  91. Futex<DeterministicAtomic> f(0);
  92. // Futex wait must eventually fail with either FutexResult::TIMEDOUT or
  93. // FutexResult::INTERRUPTED
  94. const auto res = futexWaitUntil(&f, 0, Clock::now() + milliseconds(100));
  95. EXPECT_TRUE(res == FutexResult::TIMEDOUT || res == FutexResult::INTERRUPTED);
  96. }
  97. template <template <typename> class Atom>
  98. void run_wait_until_tests() {
  99. liveClockWaitUntilTests<Atom, system_clock, system_clock::duration>();
  100. liveClockWaitUntilTests<Atom, steady_clock, steady_clock::duration>();
  101. liveClockWaitUntilTests<Atom, steady_clock, coarse_steady_clock::duration>();
  102. typedef duration<int64_t, std::ratio<1, 10000000>> decimicroseconds;
  103. liveClockWaitUntilTests<Atom, system_clock, decimicroseconds>();
  104. }
  105. template <>
  106. void run_wait_until_tests<DeterministicAtomic>() {
  107. deterministicAtomicWaitUntilTests<system_clock>();
  108. deterministicAtomicWaitUntilTests<steady_clock>();
  109. deterministicAtomicWaitUntilTests<coarse_steady_clock>();
  110. }
  111. uint64_t diff(uint64_t a, uint64_t b) {
  112. return a > b ? a - b : b - a;
  113. }
  114. void run_system_clock_test() {
  115. /* Test to verify that system_clock uses clock_gettime(CLOCK_REALTIME, ...)
  116. * for the time_points */
  117. struct timespec ts;
  118. const int maxIters = 1000;
  119. int iter = 0;
  120. const uint64_t delta = 10000000 /* 10 ms */;
  121. /** The following loop is only to make the test more robust in the presence of
  122. * clock adjustments that can occur. We just run the loop maxIter times and
  123. * expect with very high probability that there will be atleast one iteration
  124. * of the test during which clock adjustments > delta have not occurred. */
  125. while (iter < maxIters) {
  126. uint64_t a =
  127. duration_cast<nanoseconds>(system_clock::now().time_since_epoch())
  128. .count();
  129. clock_gettime(CLOCK_REALTIME, &ts);
  130. uint64_t b = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
  131. uint64_t c =
  132. duration_cast<nanoseconds>(system_clock::now().time_since_epoch())
  133. .count();
  134. if (diff(a, b) <= delta && diff(b, c) <= delta && diff(a, c) <= 2 * delta) {
  135. /* Success! system_clock uses CLOCK_REALTIME for time_points */
  136. break;
  137. }
  138. iter++;
  139. }
  140. EXPECT_TRUE(iter < maxIters);
  141. }
  142. void run_steady_clock_test() {
  143. /* Test to verify that steady_clock uses clock_gettime(CLOCK_MONOTONIC, ...)
  144. * for the time_points */
  145. EXPECT_TRUE(steady_clock::is_steady);
  146. const uint64_t A =
  147. duration_cast<nanoseconds>(steady_clock::now().time_since_epoch())
  148. .count();
  149. struct timespec ts;
  150. clock_gettime(CLOCK_MONOTONIC, &ts);
  151. const uint64_t B = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
  152. const uint64_t C =
  153. duration_cast<nanoseconds>(steady_clock::now().time_since_epoch())
  154. .count();
  155. EXPECT_TRUE(A <= B && B <= C);
  156. }
  157. template <template <typename> class Atom>
  158. void run_wake_blocked_test() {
  159. for (auto delay = std::chrono::milliseconds(1);; delay *= 2) {
  160. bool success = false;
  161. Futex<Atom> f(0);
  162. auto thr = DSched::thread(
  163. [&] { success = FutexResult::AWOKEN == futexWait(&f, 0); });
  164. /* sleep override */ std::this_thread::sleep_for(delay);
  165. f.store(1);
  166. futexWake(&f, 1);
  167. DSched::join(thr);
  168. LOG(INFO) << "delay=" << delay.count() << "_ms, success=" << success;
  169. if (success) {
  170. break;
  171. }
  172. }
  173. }
  174. TEST(Futex, clock_source) {
  175. run_system_clock_test();
  176. /* On some systems steady_clock is just an alias for system_clock. So,
  177. * we must skip run_steady_clock_test if the two clocks are the same. */
  178. if (!std::is_same<system_clock, steady_clock>::value) {
  179. run_steady_clock_test();
  180. }
  181. }
  182. TEST(Futex, basic_live) {
  183. run_basic_tests<std::atomic>();
  184. run_wait_until_tests<std::atomic>();
  185. }
  186. TEST(Futex, basic_emulated) {
  187. run_basic_tests<EmulatedFutexAtomic>();
  188. run_wait_until_tests<EmulatedFutexAtomic>();
  189. }
  190. TEST(Futex, basic_deterministic) {
  191. DSched sched(DSched::uniform(0));
  192. run_basic_tests<DeterministicAtomic>();
  193. run_wait_until_tests<DeterministicAtomic>();
  194. }
  195. TEST(Futex, wake_blocked_live) {
  196. run_wake_blocked_test<std::atomic>();
  197. }
  198. TEST(Futex, wake_blocked_emulated) {
  199. run_wake_blocked_test<EmulatedFutexAtomic>();
  200. }