AtomicNotificationTest.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. * Copyright 2018-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/synchronization/AtomicNotification.h>
  17. #include <folly/portability/GTest.h>
  18. #include <thread>
  19. using namespace std::literals;
  20. namespace folly {
  21. namespace {
  22. template <typename Integer>
  23. void run_atomic_wait_basic() {
  24. auto&& atomic = std::atomic<Integer>{0};
  25. auto&& one = std::thread{[&]() {
  26. while (true) {
  27. atomic_wait(&atomic, Integer{0});
  28. if (atomic.load() == 1) {
  29. break;
  30. }
  31. }
  32. }};
  33. atomic.store(1);
  34. atomic_notify_one(&atomic);
  35. one.join();
  36. }
  37. template <typename Integer>
  38. void run_atomic_wait_until_with_timeout() {
  39. auto&& atomic = std::atomic<Integer>{0};
  40. auto&& one = std::thread{[&]() {
  41. auto deadline = std::chrono::steady_clock::now() + 10ms;
  42. while (true) {
  43. auto result = atomic_wait_until(&atomic, Integer{0}, deadline);
  44. // Any sort of spurious wakeup caused due to aliasing should not be
  45. // changing the value in the futex word in proper usage, so we can
  46. // assert that the value should remain unchanged
  47. EXPECT_TRUE(!atomic.load());
  48. if (result == std::cv_status::timeout) {
  49. EXPECT_TRUE(std::chrono::steady_clock::now() >= deadline);
  50. break;
  51. }
  52. }
  53. }};
  54. one.join();
  55. }
  56. template <typename Integer>
  57. void run_atomic_wait_until_with_notification() {
  58. auto&& atomic = std::atomic<Integer>{0};
  59. auto&& one = std::thread{[&]() {
  60. while (true) {
  61. auto result = atomic_wait_until(
  62. &atomic, Integer{0}, std::chrono::steady_clock::time_point::max());
  63. // note that it is safe to check if we returned from the
  64. // atomic_wait_until() call due to a timeout, futex aliasing can cause
  65. // spurious wakeups due to address reuse, but will not cause spurious
  66. // timeouts, since a futex word has only one timeout on the futex queue,
  67. // and does not inherit timeout from a previous futex at the same
  68. // address
  69. EXPECT_TRUE(result != std::cv_status::timeout);
  70. break;
  71. }
  72. EXPECT_EQ(atomic.load(), 1);
  73. }};
  74. atomic.store(1);
  75. atomic_notify_one(&atomic);
  76. one.join();
  77. }
  78. class SimpleBaton {
  79. public:
  80. void wait() {
  81. auto lck = std::unique_lock<std::mutex>{mutex_};
  82. while (!signalled_) {
  83. cv_.wait(lck);
  84. }
  85. EXPECT_TRUE(signalled_);
  86. }
  87. bool try_wait() {
  88. auto lck = std::unique_lock<std::mutex>{mutex_};
  89. return signalled_;
  90. }
  91. void post() {
  92. auto lck = std::unique_lock<std::mutex>{mutex_};
  93. signalled_ = true;
  94. cv_.notify_one();
  95. }
  96. private:
  97. std::mutex mutex_;
  98. std::condition_variable cv_;
  99. bool signalled_{false};
  100. };
  101. template <typename Integer>
  102. void run_atomic_aliasing() {
  103. auto&& atomic = folly::Optional<std::atomic<Integer>>{folly::in_place, 0};
  104. auto&& one = SimpleBaton{};
  105. auto&& two = SimpleBaton{};
  106. auto threadOne = std::thread{[&]() {
  107. while (true) {
  108. one.wait();
  109. atomic_wait(atomic.get_pointer(), Integer{0});
  110. if (atomic->load() == 1) {
  111. break;
  112. }
  113. }
  114. }};
  115. atomic->store(1);
  116. one.post();
  117. threadOne.join();
  118. // reset the atomic variable
  119. atomic.reset();
  120. atomic.emplace(0);
  121. auto threadTwo = std::thread{[&]() {
  122. atomic_wait(atomic.get_pointer(), Integer{0});
  123. two.post();
  124. }};
  125. while (!two.try_wait()) {
  126. atomic_notify_one(atomic.get_pointer());
  127. }
  128. threadTwo.join();
  129. }
  130. } // namespace
  131. TEST(AtomicWait, Basic) {
  132. run_atomic_wait_basic<std::uint32_t>();
  133. }
  134. TEST(AtomicWait, BasicNonStandardWidths) {
  135. run_atomic_wait_basic<std::uint8_t>();
  136. run_atomic_wait_basic<std::uint16_t>();
  137. run_atomic_wait_basic<std::uint64_t>();
  138. }
  139. TEST(AtomicWait, AtomicWaitUntilTimeout) {
  140. run_atomic_wait_until_with_timeout<std::uint32_t>();
  141. }
  142. TEST(AtomicWait, AtomicWaitUntilTimeoutNonStandardWidths) {
  143. run_atomic_wait_until_with_timeout<std::uint8_t>();
  144. run_atomic_wait_until_with_timeout<std::uint16_t>();
  145. run_atomic_wait_until_with_timeout<std::uint64_t>();
  146. }
  147. TEST(AtomicWait, AtomicWaitUntilNotified) {
  148. run_atomic_wait_until_with_notification<std::uint32_t>();
  149. }
  150. TEST(AtomicWait, AtomicWaitUntilNotifiedNonStandardWidths) {
  151. run_atomic_wait_until_with_notification<std::uint8_t>();
  152. run_atomic_wait_until_with_notification<std::uint16_t>();
  153. run_atomic_wait_until_with_notification<std::uint64_t>();
  154. }
  155. TEST(AtomicWait, AtomicWaitAliasing) {
  156. run_atomic_aliasing<std::uint32_t>();
  157. }
  158. TEST(AtomicWait, AtomicWaitAliasingNonStandardWidths) {
  159. run_atomic_aliasing<std::uint8_t>();
  160. run_atomic_aliasing<std::uint16_t>();
  161. run_atomic_aliasing<std::uint64_t>();
  162. }
  163. } // namespace folly