X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FAtomicIntrusiveLinkedList.h;h=c0ee1b144274a8252f924355140e95a12b4c7612;hb=61fbd6698139d3f797401da6350371c7afc4030c;hp=87e7d5b5235f264ac0f7e4acd3a0b6472b1caf6e;hpb=1ac421a539aaf2f349591d4bee36c75ff0ee052c;p=folly.git diff --git a/folly/AtomicIntrusiveLinkedList.h b/folly/AtomicIntrusiveLinkedList.h index 87e7d5b5..c0ee1b14 100644 --- a/folly/AtomicIntrusiveLinkedList.h +++ b/folly/AtomicIntrusiveLinkedList.h @@ -1,5 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #pragma once #include - -#include +#include +#include namespace folly { @@ -47,7 +48,9 @@ class AtomicIntrusiveLinkedList { AtomicIntrusiveLinkedList& operator=(const AtomicIntrusiveLinkedList&) = delete; AtomicIntrusiveLinkedList(AtomicIntrusiveLinkedList&& other) noexcept { - *this = std::move(other); + auto tmp = other.head_.load(); + other.head_ = head_.load(); + head_ = tmp; } AtomicIntrusiveLinkedList& operator=( AtomicIntrusiveLinkedList&& other) noexcept { @@ -61,9 +64,13 @@ class AtomicIntrusiveLinkedList { /** * Note: list must be empty on destruction. */ - ~AtomicIntrusiveLinkedList() { DCHECK(empty()); } + ~AtomicIntrusiveLinkedList() { + assert(empty()); + } - bool empty() const { return head_ == nullptr; } + bool empty() const { + return head_.load() == nullptr; + } /** * Atomically insert t at the head of the list. @@ -71,7 +78,7 @@ class AtomicIntrusiveLinkedList { * after the call. */ bool insertHead(T* t) { - DCHECK(next(t) == nullptr); + assert(next(t) == nullptr); auto oldHead = head_.load(std::memory_order_relaxed); do { @@ -82,8 +89,9 @@ class AtomicIntrusiveLinkedList { 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)); + } while (!head_.compare_exchange_weak(oldHead, t, + std::memory_order_release, + std::memory_order_relaxed)); return oldHead == nullptr; } @@ -97,19 +105,37 @@ class AtomicIntrusiveLinkedList { 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); - } + unlinkAll(rhead, std::forward(func)); } } + /** + * Similar to sweep() but calls func() on elements in LIFO order. + * + * func() is called for all elements in the list at the moment + * reverseSweep() is called. Unlike sweep() it does not loop to ensure the + * list is empty at some point after the last invocation. This way callers + * can reason about the ordering: elements inserted since the last call to + * reverseSweep() will be provided in LIFO order. + * + * Example: if elements are inserted in the order 1-2-3, the callback is + * invoked 3-2-1. If the callback moves elements onto a stack, popping off + * the stack will produce the original insertion order 1-2-3. + */ + template + void reverseSweep(F&& func) { + // We don't loop like sweep() does because the overall order of callbacks + // would be strand-wise LIFO which is meaningless to callers. + auto head = head_.exchange(nullptr); + unlinkAll(head, std::forward(func)); + } + private: std::atomic head_{nullptr}; - static T*& next(T* t) { return (t->*HookMember).next; } + static T*& next(T* t) { + return (t->*HookMember).next; + } /* Reverses a linked list, returning the pointer to the new head (old tail) */ @@ -123,6 +149,18 @@ class AtomicIntrusiveLinkedList { } return rhead; } + + /* Unlinks all elements in the linked list fragment pointed to by `head', + * calling func() on every element */ + template + void unlinkAll(T* head, F&& func) { + while (head != nullptr) { + auto t = head; + head = next(t); + next(t) = nullptr; + func(t); + } + } }; } // namespace folly