LockFreeRingBufferTest.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * Copyright 2015-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 <iostream>
  17. #include <thread>
  18. #include <folly/experimental/LockFreeRingBuffer.h>
  19. #include <folly/portability/GTest.h>
  20. #include <folly/test/DeterministicSchedule.h>
  21. namespace folly {
  22. TEST(LockFreeRingBuffer, writeReadSequentially) {
  23. const int capacity = 256;
  24. const int turns = 4;
  25. LockFreeRingBuffer<int> rb(capacity);
  26. LockFreeRingBuffer<int>::Cursor cur = rb.currentHead();
  27. for (unsigned int turn = 0; turn < turns; turn++) {
  28. for (unsigned int write = 0; write < capacity; write++) {
  29. int val = turn * capacity + write;
  30. rb.write(val);
  31. }
  32. for (unsigned int write = 0; write < capacity; write++) {
  33. int dest = 0;
  34. ASSERT_TRUE(rb.tryRead(dest, cur));
  35. ASSERT_EQ(turn * capacity + write, dest);
  36. cur.moveForward();
  37. }
  38. }
  39. }
  40. TEST(LockFreeRingBuffer, writeReadSequentiallyBackward) {
  41. const int capacity = 256;
  42. const int turns = 4;
  43. LockFreeRingBuffer<int> rb(capacity);
  44. for (unsigned int turn = 0; turn < turns; turn++) {
  45. for (unsigned int write = 0; write < capacity; write++) {
  46. int val = turn * capacity + write;
  47. rb.write(val);
  48. }
  49. LockFreeRingBuffer<int>::Cursor cur = rb.currentHead();
  50. cur.moveBackward(1); /// last write
  51. for (int write = capacity - 1; write >= 0; write--) {
  52. int foo = 0;
  53. ASSERT_TRUE(rb.tryRead(foo, cur));
  54. ASSERT_EQ(turn * capacity + write, foo);
  55. cur.moveBackward();
  56. }
  57. }
  58. }
  59. TEST(LockFreeRingBuffer, readsCanBlock) {
  60. // Start a reader thread, confirm that reading can block
  61. std::atomic<bool> readerHasRun(false);
  62. LockFreeRingBuffer<int> rb(1);
  63. auto cursor = rb.currentHead();
  64. cursor.moveForward(3); // wait for the 4th write
  65. const int sentinel = 0xfaceb00c;
  66. auto reader = std::thread([&]() {
  67. int val = 0;
  68. EXPECT_TRUE(rb.waitAndTryRead(val, cursor));
  69. readerHasRun = true;
  70. EXPECT_EQ(sentinel, val);
  71. });
  72. for (int i = 0; i < 4; i++) {
  73. EXPECT_FALSE(readerHasRun);
  74. int val = sentinel;
  75. rb.write(val);
  76. }
  77. reader.join();
  78. EXPECT_TRUE(readerHasRun);
  79. }
  80. // expose the cursor raw value via a wrapper type
  81. template <typename T, template <typename> class Atom>
  82. uint64_t value(const typename LockFreeRingBuffer<T, Atom>::Cursor& rbcursor) {
  83. typedef typename LockFreeRingBuffer<T, Atom>::Cursor RBCursor;
  84. struct ExposedCursor : RBCursor {
  85. ExposedCursor(const RBCursor& cursor) : RBCursor(cursor) {}
  86. uint64_t value() {
  87. return this->ticket;
  88. }
  89. };
  90. return ExposedCursor(rbcursor).value();
  91. }
  92. template <template <typename> class Atom>
  93. void runReader(
  94. LockFreeRingBuffer<int, Atom>& rb,
  95. std::atomic<int32_t>& writes) {
  96. int32_t idx;
  97. while ((idx = writes--) > 0) {
  98. rb.write(idx);
  99. }
  100. }
  101. template <template <typename> class Atom>
  102. void runWritesNeverFail(int capacity, int writes, int writers) {
  103. using folly::test::DeterministicSchedule;
  104. DeterministicSchedule sched(DeterministicSchedule::uniform(0));
  105. LockFreeRingBuffer<int, Atom> rb(capacity);
  106. std::atomic<int32_t> writes_remaining(writes);
  107. std::vector<std::thread> threads(writers);
  108. for (int i = 0; i < writers; i++) {
  109. threads[i] = DeterministicSchedule::thread(
  110. std::bind(runReader<Atom>, std::ref(rb), std::ref(writes_remaining)));
  111. }
  112. for (auto& thread : threads) {
  113. DeterministicSchedule::join(thread);
  114. }
  115. EXPECT_EQ(writes, (value<int, Atom>)(rb.currentHead()));
  116. }
  117. TEST(LockFreeRingBuffer, writesNeverFail) {
  118. using folly::detail::EmulatedFutexAtomic;
  119. using folly::test::DeterministicAtomic;
  120. runWritesNeverFail<DeterministicAtomic>(1, 100, 4);
  121. runWritesNeverFail<DeterministicAtomic>(10, 100, 4);
  122. runWritesNeverFail<DeterministicAtomic>(100, 1000, 8);
  123. runWritesNeverFail<DeterministicAtomic>(1000, 10000, 16);
  124. runWritesNeverFail<std::atomic>(1, 100, 4);
  125. runWritesNeverFail<std::atomic>(10, 100, 4);
  126. runWritesNeverFail<std::atomic>(100, 1000, 8);
  127. runWritesNeverFail<std::atomic>(1000, 10000, 16);
  128. runWritesNeverFail<EmulatedFutexAtomic>(1, 100, 4);
  129. runWritesNeverFail<EmulatedFutexAtomic>(10, 100, 4);
  130. runWritesNeverFail<EmulatedFutexAtomic>(100, 1000, 8);
  131. runWritesNeverFail<EmulatedFutexAtomic>(1000, 10000, 16);
  132. }
  133. TEST(LockFreeRingBuffer, readerCanDetectSkips) {
  134. const int capacity = 4;
  135. const int rounds = 4;
  136. LockFreeRingBuffer<int> rb(capacity);
  137. auto cursor = rb.currentHead();
  138. cursor.moveForward(1);
  139. for (int round = 0; round < rounds; round++) {
  140. for (int i = 0; i < capacity; i++) {
  141. int val = round * capacity + i;
  142. rb.write(val);
  143. }
  144. }
  145. int result = -1;
  146. EXPECT_FALSE(rb.tryRead(result, cursor));
  147. EXPECT_FALSE(rb.waitAndTryRead(result, cursor));
  148. EXPECT_EQ(-1, result);
  149. cursor = rb.currentTail();
  150. EXPECT_TRUE(rb.tryRead(result, cursor));
  151. EXPECT_EQ(capacity * (rounds - 1), result);
  152. cursor = rb.currentTail(1.0);
  153. EXPECT_TRUE(rb.tryRead(result, cursor));
  154. EXPECT_EQ((capacity * rounds) - 1, result);
  155. }
  156. TEST(LockFreeRingBuffer, currentTailRange) {
  157. const int capacity = 4;
  158. LockFreeRingBuffer<int> rb(capacity);
  159. // Workaround for template deduction failure
  160. auto (&cursorValue)(value<int, std::atomic>);
  161. // Empty buffer - everything points to 0
  162. EXPECT_EQ(0, cursorValue(rb.currentTail(0)));
  163. EXPECT_EQ(0, cursorValue(rb.currentTail(0.5)));
  164. EXPECT_EQ(0, cursorValue(rb.currentTail(1)));
  165. // Half-full
  166. int val = 5;
  167. rb.write(val);
  168. rb.write(val);
  169. EXPECT_EQ(0, cursorValue(rb.currentTail(0)));
  170. EXPECT_EQ(1, cursorValue(rb.currentTail(1)));
  171. // Full
  172. rb.write(val);
  173. rb.write(val);
  174. EXPECT_EQ(0, cursorValue(rb.currentTail(0)));
  175. EXPECT_EQ(3, cursorValue(rb.currentTail(1)));
  176. auto midvalue = cursorValue(rb.currentTail(0.5));
  177. // both rounding behaviours are acceptable
  178. EXPECT_TRUE(midvalue == 1 || midvalue == 2);
  179. }
  180. TEST(LockFreeRingBuffer, cursorFromWrites) {
  181. const int capacity = 3;
  182. LockFreeRingBuffer<int> rb(capacity);
  183. // Workaround for template deduction failure
  184. auto (&cursorValue)(value<int, std::atomic>);
  185. int val = 0xfaceb00c;
  186. EXPECT_EQ(0, cursorValue(rb.writeAndGetCursor(val)));
  187. EXPECT_EQ(1, cursorValue(rb.writeAndGetCursor(val)));
  188. EXPECT_EQ(2, cursorValue(rb.writeAndGetCursor(val)));
  189. // Check that rb is giving out actual cursors and not just
  190. // pointing to the current slot.
  191. EXPECT_EQ(3, cursorValue(rb.writeAndGetCursor(val)));
  192. }
  193. TEST(LockFreeRingBuffer, moveBackwardsCanFail) {
  194. const int capacity = 3;
  195. LockFreeRingBuffer<int> rb(capacity);
  196. // Workaround for template deduction failure
  197. auto (&cursorValue)(value<int, std::atomic>);
  198. int val = 0xfaceb00c;
  199. rb.write(val);
  200. rb.write(val);
  201. auto cursor = rb.currentHead(); // points to 2
  202. EXPECT_EQ(2, cursorValue(cursor));
  203. EXPECT_TRUE(cursor.moveBackward());
  204. EXPECT_TRUE(cursor.moveBackward()); // now at 0
  205. EXPECT_FALSE(cursor.moveBackward()); // moving back does nothing
  206. }
  207. } // namespace folly