2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
25 * A very simple atomic single-linked list primitive.
30 * AtomicLinkedListHook<MyClass> hook_;
33 * AtomicLinkedList<MyClass, &MyClass::hook_> list;
35 * list.sweep([] (MyClass* c) { doSomething(c); }
38 struct AtomicLinkedListHook {
42 template <class T, AtomicLinkedListHook<T> T::* HookMember>
43 class AtomicLinkedList {
46 AtomicLinkedList(const AtomicLinkedList&) = delete;
47 AtomicLinkedList& operator=(const AtomicLinkedList&) = delete;
48 AtomicLinkedList(AtomicLinkedList&& other) noexcept {
49 auto tmp = other.head_.load();
50 other.head_ = head_.load();
53 AtomicLinkedList& operator=(AtomicLinkedList&& other) noexcept {
54 auto tmp = other.head_.load();
55 other.head_ = head_.load();
62 * Note: list must be empty on destruction.
69 return head_ == nullptr;
73 * Atomically insert t at the head of the list.
74 * @return True if the inserted element is the only one in the list
77 bool insertHead(T* t) {
78 assert(next(t) == nullptr);
80 auto oldHead = head_.load(std::memory_order_relaxed);
83 /* oldHead is updated by the call below.
85 NOTE: we don't use next(t) instead of oldHead directly due to
86 compiler bugs (GCC prior to 4.8.3 (bug 60272), clang (bug 18899),
87 MSVC (bug 819819); source:
88 http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange */
89 } while (!head_.compare_exchange_weak(oldHead, t,
90 std::memory_order_release,
91 std::memory_order_relaxed));
93 return oldHead == nullptr;
97 * Repeatedly replaces the head with nullptr,
98 * and calls func() on the removed elements in the order from tail to head.
99 * Stops when the list is empty.
101 template <typename F>
102 void sweep(F&& func) {
103 while (auto head = head_.exchange(nullptr)) {
104 auto rhead = reverse(head);
105 while (rhead != nullptr) {
115 std::atomic<T*> head_{nullptr};
117 static T*& next(T* t) {
118 return (t->*HookMember).next;
121 /* Reverses a linked list, returning the pointer to the new head
123 static T* reverse(T* head) {
125 while (head != nullptr) {