AtomicIntrusiveLinkedList.h 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*
  2. * Copyright 2014-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. #pragma once
  17. #include <atomic>
  18. #include <cassert>
  19. #include <utility>
  20. namespace folly {
  21. /**
  22. * A very simple atomic single-linked list primitive.
  23. *
  24. * Usage:
  25. *
  26. * class MyClass {
  27. * AtomicIntrusiveLinkedListHook<MyClass> hook_;
  28. * }
  29. *
  30. * AtomicIntrusiveLinkedList<MyClass, &MyClass::hook_> list;
  31. * list.insert(&a);
  32. * list.sweep([] (MyClass* c) { doSomething(c); }
  33. */
  34. template <class T>
  35. struct AtomicIntrusiveLinkedListHook {
  36. T* next{nullptr};
  37. };
  38. template <class T, AtomicIntrusiveLinkedListHook<T> T::*HookMember>
  39. class AtomicIntrusiveLinkedList {
  40. public:
  41. AtomicIntrusiveLinkedList() {}
  42. AtomicIntrusiveLinkedList(const AtomicIntrusiveLinkedList&) = delete;
  43. AtomicIntrusiveLinkedList& operator=(const AtomicIntrusiveLinkedList&) =
  44. delete;
  45. AtomicIntrusiveLinkedList(AtomicIntrusiveLinkedList&& other) noexcept {
  46. auto tmp = other.head_.load();
  47. other.head_ = head_.load();
  48. head_ = tmp;
  49. }
  50. AtomicIntrusiveLinkedList& operator=(
  51. AtomicIntrusiveLinkedList&& other) noexcept {
  52. auto tmp = other.head_.load();
  53. other.head_ = head_.load();
  54. head_ = tmp;
  55. return *this;
  56. }
  57. /**
  58. * Note: list must be empty on destruction.
  59. */
  60. ~AtomicIntrusiveLinkedList() {
  61. assert(empty());
  62. }
  63. bool empty() const {
  64. return head_.load() == nullptr;
  65. }
  66. /**
  67. * Atomically insert t at the head of the list.
  68. * @return True if the inserted element is the only one in the list
  69. * after the call.
  70. */
  71. bool insertHead(T* t) {
  72. assert(next(t) == nullptr);
  73. auto oldHead = head_.load(std::memory_order_relaxed);
  74. do {
  75. next(t) = oldHead;
  76. /* oldHead is updated by the call below.
  77. NOTE: we don't use next(t) instead of oldHead directly due to
  78. compiler bugs (GCC prior to 4.8.3 (bug 60272), clang (bug 18899),
  79. MSVC (bug 819819); source:
  80. http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange */
  81. } while (!head_.compare_exchange_weak(
  82. oldHead, t, std::memory_order_release, std::memory_order_relaxed));
  83. return oldHead == nullptr;
  84. }
  85. /**
  86. * Replaces the head with nullptr,
  87. * and calls func() on the removed elements in the order from tail to head.
  88. * Returns false if the list was empty.
  89. */
  90. template <typename F>
  91. bool sweepOnce(F&& func) {
  92. if (auto head = head_.exchange(nullptr)) {
  93. auto rhead = reverse(head);
  94. unlinkAll(rhead, std::forward<F>(func));
  95. return true;
  96. }
  97. return false;
  98. }
  99. /**
  100. * Repeatedly replaces the head with nullptr,
  101. * and calls func() on the removed elements in the order from tail to head.
  102. * Stops when the list is empty.
  103. */
  104. template <typename F>
  105. void sweep(F&& func) {
  106. while (sweepOnce(func)) {
  107. }
  108. }
  109. /**
  110. * Similar to sweep() but calls func() on elements in LIFO order.
  111. *
  112. * func() is called for all elements in the list at the moment
  113. * reverseSweep() is called. Unlike sweep() it does not loop to ensure the
  114. * list is empty at some point after the last invocation. This way callers
  115. * can reason about the ordering: elements inserted since the last call to
  116. * reverseSweep() will be provided in LIFO order.
  117. *
  118. * Example: if elements are inserted in the order 1-2-3, the callback is
  119. * invoked 3-2-1. If the callback moves elements onto a stack, popping off
  120. * the stack will produce the original insertion order 1-2-3.
  121. */
  122. template <typename F>
  123. void reverseSweep(F&& func) {
  124. // We don't loop like sweep() does because the overall order of callbacks
  125. // would be strand-wise LIFO which is meaningless to callers.
  126. auto head = head_.exchange(nullptr);
  127. unlinkAll(head, std::forward<F>(func));
  128. }
  129. private:
  130. std::atomic<T*> head_{nullptr};
  131. static T*& next(T* t) {
  132. return (t->*HookMember).next;
  133. }
  134. /* Reverses a linked list, returning the pointer to the new head
  135. (old tail) */
  136. static T* reverse(T* head) {
  137. T* rhead = nullptr;
  138. while (head != nullptr) {
  139. auto t = head;
  140. head = next(t);
  141. next(t) = rhead;
  142. rhead = t;
  143. }
  144. return rhead;
  145. }
  146. /* Unlinks all elements in the linked list fragment pointed to by `head',
  147. * calling func() on every element */
  148. template <typename F>
  149. void unlinkAll(T* head, F&& func) {
  150. while (head != nullptr) {
  151. auto t = head;
  152. head = next(t);
  153. next(t) = nullptr;
  154. func(t);
  155. }
  156. }
  157. };
  158. } // namespace folly