AtomicLinkedListTest.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Copyright 2016-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 <algorithm>
  17. #include <thread>
  18. #include <folly/AtomicLinkedList.h>
  19. #include <folly/portability/GTest.h>
  20. class TestIntrusiveObject {
  21. public:
  22. explicit TestIntrusiveObject(size_t id__) : id_(id__) {}
  23. size_t id() {
  24. return id_;
  25. }
  26. private:
  27. folly::AtomicIntrusiveLinkedListHook<TestIntrusiveObject> hook_;
  28. size_t id_;
  29. public:
  30. using List = folly::AtomicIntrusiveLinkedList<
  31. TestIntrusiveObject,
  32. &TestIntrusiveObject::hook_>;
  33. };
  34. TEST(AtomicIntrusiveLinkedList, Basic) {
  35. TestIntrusiveObject a(1), b(2), c(3);
  36. TestIntrusiveObject::List list;
  37. EXPECT_TRUE(list.empty());
  38. {
  39. EXPECT_TRUE(list.insertHead(&a));
  40. EXPECT_FALSE(list.insertHead(&b));
  41. EXPECT_FALSE(list.empty());
  42. size_t id = 0;
  43. list.sweep([&](TestIntrusiveObject* obj) mutable {
  44. ++id;
  45. EXPECT_EQ(id, obj->id());
  46. });
  47. EXPECT_TRUE(list.empty());
  48. }
  49. // Try re-inserting the same item (b) and a new item (c)
  50. {
  51. EXPECT_TRUE(list.insertHead(&b));
  52. EXPECT_FALSE(list.insertHead(&c));
  53. EXPECT_FALSE(list.empty());
  54. size_t id = 1;
  55. list.sweep([&](TestIntrusiveObject* obj) mutable {
  56. ++id;
  57. EXPECT_EQ(id, obj->id());
  58. });
  59. EXPECT_TRUE(list.empty());
  60. }
  61. TestIntrusiveObject::List movedList = std::move(list);
  62. }
  63. TEST(AtomicIntrusiveLinkedList, ReverseSweep) {
  64. TestIntrusiveObject a(1), b(2), c(3);
  65. TestIntrusiveObject::List list;
  66. list.insertHead(&a);
  67. list.insertHead(&b);
  68. list.insertHead(&c);
  69. size_t next_expected_id = 3;
  70. list.reverseSweep([&](TestIntrusiveObject* obj) {
  71. auto const expected = next_expected_id--;
  72. EXPECT_EQ(expected, obj->id());
  73. });
  74. EXPECT_TRUE(list.empty());
  75. // Test that we can still insert
  76. list.insertHead(&a);
  77. EXPECT_FALSE(list.empty());
  78. list.reverseSweep([](TestIntrusiveObject*) {});
  79. }
  80. TEST(AtomicIntrusiveLinkedList, Move) {
  81. TestIntrusiveObject a(1), b(2);
  82. TestIntrusiveObject::List list1;
  83. EXPECT_TRUE(list1.insertHead(&a));
  84. EXPECT_FALSE(list1.insertHead(&b));
  85. EXPECT_FALSE(list1.empty());
  86. TestIntrusiveObject::List list2(std::move(list1));
  87. EXPECT_TRUE(list1.empty());
  88. EXPECT_FALSE(list2.empty());
  89. TestIntrusiveObject::List list3;
  90. EXPECT_TRUE(list3.empty());
  91. list3 = std::move(list2);
  92. EXPECT_TRUE(list2.empty());
  93. EXPECT_FALSE(list3.empty());
  94. size_t id = 0;
  95. list3.sweep([&](TestIntrusiveObject* obj) mutable {
  96. ++id;
  97. EXPECT_EQ(id, obj->id());
  98. });
  99. }
  100. TEST(AtomicIntrusiveLinkedList, Stress) {
  101. static constexpr size_t kNumThreads = 32;
  102. static constexpr size_t kNumElements = 100000;
  103. std::vector<TestIntrusiveObject> elements;
  104. for (size_t i = 0; i < kNumThreads * kNumElements; ++i) {
  105. elements.emplace_back(i);
  106. }
  107. TestIntrusiveObject::List list;
  108. std::vector<std::thread> threads;
  109. for (size_t threadId = 0; threadId < kNumThreads; ++threadId) {
  110. threads.emplace_back([threadId, &list, &elements] {
  111. for (size_t id = 0; id < kNumElements; ++id) {
  112. list.insertHead(&elements[threadId + kNumThreads * id]);
  113. }
  114. });
  115. }
  116. std::vector<size_t> ids;
  117. TestIntrusiveObject* prev{nullptr};
  118. while (ids.size() < kNumThreads * kNumElements) {
  119. list.sweep([&](TestIntrusiveObject* current) {
  120. ids.push_back(current->id());
  121. if (prev && prev->id() % kNumThreads == current->id() % kNumThreads) {
  122. EXPECT_EQ(prev->id() + kNumThreads, current->id());
  123. }
  124. prev = current;
  125. });
  126. }
  127. std::sort(ids.begin(), ids.end());
  128. for (size_t i = 0; i < kNumThreads * kNumElements; ++i) {
  129. EXPECT_EQ(i, ids[i]);
  130. }
  131. for (auto& thread : threads) {
  132. thread.join();
  133. }
  134. }
  135. class TestObject {
  136. public:
  137. TestObject(size_t id__, const std::shared_ptr<void>& ptr)
  138. : id_(id__), ptr_(ptr) {}
  139. size_t id() {
  140. return id_;
  141. }
  142. private:
  143. size_t id_;
  144. std::shared_ptr<void> ptr_;
  145. };
  146. TEST(AtomicLinkedList, Basic) {
  147. constexpr size_t kNumElements = 10;
  148. using List = folly::AtomicLinkedList<TestObject>;
  149. List list;
  150. std::shared_ptr<void> ptr = std::make_shared<int>(42);
  151. for (size_t id = 0; id < kNumElements; ++id) {
  152. list.insertHead({id, ptr});
  153. }
  154. size_t counter = 0;
  155. list.sweep([&](TestObject object) {
  156. EXPECT_EQ(counter, object.id());
  157. EXPECT_EQ(1 + kNumElements - counter, ptr.use_count());
  158. ++counter;
  159. });
  160. EXPECT_TRUE(ptr.unique());
  161. }