From: Rushi Desai Date: Thu, 2 Apr 2015 04:51:08 +0000 (-0700) Subject: Move AtomicLinkedList to folly X-Git-Tag: v0.33.0~5 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=773ee3cb6a4d52079e1efb8492354cc54bf6d813;p=folly.git Move AtomicLinkedList to folly 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 --- diff --git a/folly/AtomicLinkedList.h b/folly/AtomicLinkedList.h new file mode 100644 index 00000000..7e9db6bd --- /dev/null +++ b/folly/AtomicLinkedList.h @@ -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 +#include + +namespace folly { + +/** + * A very simple atomic single-linked list primitive. + * + * Usage: + * + * class MyClass { + * AtomicLinkedListHook hook_; + * } + * + * AtomicLinkedList list; + * list.insert(&a); + * list.sweep([] (MyClass* c) { doSomething(c); } + */ +template +struct AtomicLinkedListHook { + T* next{nullptr}; +}; + +template 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 + 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 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