Move AtomicLinkedList to folly
authorRushi Desai <rushix@fb.com>
Thu, 2 Apr 2015 04:51:08 +0000 (21:51 -0700)
committerafrind <afrind@fb.com>
Thu, 2 Apr 2015 19:02:39 +0000 (12:02 -0700)
Summary:
Adding new atomic data structure from the mcrouter project.

Test Plan: fbconfig -r mcrouter && fbmake runtests

Reviewed By: andrii@fb.com

Subscribers: alikhtarov, folly-diffs@, yfeldblum, chalfant, achao, jpearce

FB internal diff: D1960219

Signature: t1:1960219:1427921204:78e5313ea916b8a249d32e31cc7ba1aa9e890d95

folly/AtomicLinkedList.h [new file with mode: 0644]

diff --git a/folly/AtomicLinkedList.h b/folly/AtomicLinkedList.h
new file mode 100644 (file)
index 0000000..7e9db6b
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FOLLY_ATOMIC_LINKED_LIST_H_
+#define FOLLY_ATOMIC_LINKED_LIST_H_
+
+#include <atomic>
+#include <cassert>
+
+namespace folly {
+
+/**
+ * A very simple atomic single-linked list primitive.
+ *
+ * Usage:
+ *
+ * class MyClass {
+ *   AtomicLinkedListHook<MyClass> hook_;
+ * }
+ *
+ * AtomicLinkedList<MyClass, &MyClass::hook_> list;
+ * list.insert(&a);
+ * list.sweep([] (MyClass* c) { doSomething(c); }
+ */
+template <class T>
+struct AtomicLinkedListHook {
+  T* next{nullptr};
+};
+
+template <class T, AtomicLinkedListHook<T> T::* HookMember>
+class AtomicLinkedList {
+ public:
+  AtomicLinkedList() {}
+
+  /**
+   * Note: list must be empty on destruction.
+   */
+  ~AtomicLinkedList() {
+    assert(head_ == nullptr);
+  }
+
+  bool empty() const {
+    return head_ == nullptr;
+  }
+
+  /**
+   * Atomically insert t at the head of the list.
+   * @return True if the inserted element is the only one in the list
+   *         after the call.
+   */
+  bool insertHead(T* t) {
+    assert(next(t) == nullptr);
+
+    auto oldHead = head_.load(std::memory_order_relaxed);
+    do {
+      next(t) = oldHead;
+      /* oldHead is updated by the call below.
+
+         NOTE: we don't use next(t) instead of oldHead directly due to
+         compiler bugs (GCC prior to 4.8.3 (bug 60272), clang (bug 18899),
+         MSVC (bug 819819); source:
+         http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange */
+    } while (!head_.compare_exchange_weak(oldHead, t,
+                                          std::memory_order_release,
+                                          std::memory_order_relaxed));
+
+    return oldHead == nullptr;
+  }
+
+  /**
+   * Repeatedly replaces the head with nullptr,
+   * and calls func() on the removed elements in the order from tail to head.
+   * Stops when the list is empty.
+   */
+  template <typename F>
+  void sweep(F&& func) {
+    while (auto head = head_.exchange(nullptr)) {
+      auto rhead = reverse(head);
+      while (rhead != nullptr) {
+        auto t = rhead;
+        rhead = next(t);
+        next(t) = nullptr;
+        func(t);
+      }
+    }
+  }
+
+ private:
+  std::atomic<T*> head_{nullptr};
+
+  static T*& next(T* t) {
+    return (t->*HookMember).next;
+  }
+
+  /* Reverses a linked list, returning the pointer to the new head
+     (old tail) */
+  static T* reverse(T* head) {
+    T* rhead = nullptr;
+    while (head != nullptr) {
+      auto t = head;
+      head = next(t);
+      next(t) = rhead;
+      rhead = t;
+    }
+    return rhead;
+  }
+};
+
+} // namespace folly
+
+#endif