@@ -216,7 +216,7 @@ namespace std {
216216* See the License for the specific language governing permissions and
217217* limitations under the License.
218218*/
219- namespace atomic {
219+ namespace lock_free {
220220
221221 size_t nextPowTwo (size_t v) {
222222 #ifdef _MSC_VER
@@ -593,4 +593,289 @@ namespace atomic {
593593 explicit MutableData (const T& init) : data(init) {}
594594 };
595595
596- } // namespace atomic
596+ /* *
597+ * A very simple atomic single-linked list primitive.
598+ *
599+ * Usage:
600+ *
601+ * class MyClass {
602+ * _linked_list_hook<MyClass> hook_;
603+ * }
604+ *
605+ * _linked_list<MyClass, &MyClass::hook_> list;
606+ * list.insert(&a);
607+ * list.sweep([] (MyClass* c) { doSomething(c); }
608+ */
609+ template <class T >
610+ struct _linked_list_hook {
611+ T* next{nullptr };
612+ };
613+
614+ template <class T , _linked_list_hook<T> T::*HookMember>
615+ class _linked_list {
616+ public:
617+ _linked_list () {}
618+
619+ _linked_list (const _linked_list&) = delete ;
620+ _linked_list& operator =(const _linked_list&) =
621+ delete ;
622+
623+ _linked_list (_linked_list&& other) noexcept
624+ : head_(other.head_.exchange(nullptr , std::memory_order_acq_rel)) {}
625+
626+ // Absent because would be too error-prone to use correctly because of
627+ // the requirement that lists are empty upon destruction.
628+ _linked_list& operator =(
629+ _linked_list&& other) noexcept = delete ;
630+
631+ /* *
632+ * Move the currently held elements to a new list.
633+ * The current list becomes empty, but concurrent threads
634+ * might still add new elements to it.
635+ *
636+ * Equivalent to calling a move constructor, but more linter-friendly
637+ * in case you still need the old list.
638+ */
639+ _linked_list spliceAll () { return std::move (*this ); }
640+
641+ /* *
642+ * Move-assign the current list to `other`, then reverse-sweep
643+ * the old list with the provided callback `func`.
644+ *
645+ * A safe replacement for the move assignment operator, which is absent
646+ * because of the resource leak concerns.
647+ */
648+ template <typename F>
649+ void reverseSweepAndAssign (_linked_list&& other, F&& func) {
650+ auto otherHead = other.head_ .exchange (nullptr , std::memory_order_acq_rel);
651+ auto head = head_.exchange (otherHead, std::memory_order_acq_rel);
652+ unlinkAll (head, std::forward<F>(func));
653+ }
654+
655+ /* *
656+ * Note: The list must be empty on destruction.
657+ */
658+ ~_linked_list () { assert (empty ()); }
659+
660+ /* *
661+ * Returns the current head of the list.
662+ *
663+ * WARNING: The returned pointer might not be valid if the list
664+ * is modified concurrently!
665+ */
666+ T* unsafeHead () const { return head_.load (std::memory_order_acquire); }
667+
668+ /* *
669+ * Returns true if the list is empty.
670+ *
671+ * WARNING: This method's return value is only valid for a snapshot
672+ * of the state, it might become stale as soon as it's returned.
673+ */
674+ bool empty () const { return unsafeHead () == nullptr ; }
675+
676+ /* *
677+ * Atomically insert t at the head of the list.
678+ * @return True if the inserted element is the only one in the list
679+ * after the call.
680+ */
681+ bool insertHead (T* t) {
682+ assert (next (t) == nullptr );
683+
684+ auto oldHead = head_.load (std::memory_order_relaxed);
685+ do {
686+ next (t) = oldHead;
687+ /* oldHead is updated by the call below.
688+
689+ NOTE: we don't use next(t) instead of oldHead directly due to
690+ compiler bugs (GCC prior to 4.8.3 (bug 60272), clang (bug 18899),
691+ MSVC (bug 819819); source:
692+ http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange */
693+ } while (!head_.compare_exchange_weak (
694+ oldHead, t, std::memory_order_release, std::memory_order_relaxed));
695+
696+ return oldHead == nullptr ;
697+ }
698+
699+ /* *
700+ * Replaces the head with nullptr,
701+ * and calls func() on the removed elements in the order from tail to head.
702+ * Returns false if the list was empty.
703+ */
704+ template <typename F>
705+ bool sweepOnce (F&& func) {
706+ if (auto head = head_.exchange (nullptr , std::memory_order_acq_rel)) {
707+ auto rhead = reverse (head);
708+ unlinkAll (rhead, std::forward<F>(func));
709+ return true ;
710+ }
711+ return false ;
712+ }
713+
714+ /* *
715+ * Repeatedly replaces the head with nullptr,
716+ * and calls func() on the removed elements in the order from tail to head.
717+ * Stops when the list is empty.
718+ */
719+ template <typename F>
720+ void sweep (F&& func) {
721+ while (sweepOnce (func)) {
722+ }
723+ }
724+
725+ /* *
726+ * Similar to sweep() but calls func() on elements in LIFO order.
727+ *
728+ * func() is called for all elements in the list at the moment
729+ * reverseSweep() is called. Unlike sweep() it does not loop to ensure the
730+ * list is empty at some point after the last invocation. This way callers
731+ * can reason about the ordering: elements inserted since the last call to
732+ * reverseSweep() will be provided in LIFO order.
733+ *
734+ * Example: if elements are inserted in the order 1-2-3, the callback is
735+ * invoked 3-2-1. If the callback moves elements onto a stack, popping off
736+ * the stack will produce the original insertion order 1-2-3.
737+ */
738+ template <typename F>
739+ void reverseSweep (F&& func) {
740+ // We don't loop like sweep() does because the overall order of callbacks
741+ // would be strand-wise LIFO which is meaningless to callers.
742+ auto head = head_.exchange (nullptr , std::memory_order_acq_rel);
743+ unlinkAll (head, std::forward<F>(func));
744+ }
745+
746+ private:
747+ std::atomic<T*> head_{nullptr };
748+
749+ static T*& next (T* t) { return (t->*HookMember).next ; }
750+
751+ /* Reverses a linked list, returning the pointer to the new head
752+ (old tail) */
753+ static T* reverse (T* head) {
754+ T* rhead = nullptr ;
755+ while (head != nullptr ) {
756+ auto t = head;
757+ head = next (t);
758+ next (t) = rhead;
759+ rhead = t;
760+ }
761+ return rhead;
762+ }
763+
764+ /* Unlinks all elements in the linked list fragment pointed to by `head',
765+ * calling func() on every element */
766+ template <typename F>
767+ static void unlinkAll (T* head, F&& func) {
768+ while (head != nullptr ) {
769+ auto t = head;
770+ head = next (t);
771+ next (t) = nullptr ;
772+ func (t);
773+ }
774+ }
775+ };
776+
777+ /* *
778+ * A very simple atomic single-linked list primitive.
779+ *
780+ * Usage:
781+ *
782+ * linked_list<MyClass> list;
783+ * list.insert(a);
784+ * list.sweep([] (MyClass& c) { doSomething(c); }
785+ */
786+
787+ template <class T >
788+ class linked_list {
789+ public:
790+ linked_list () {}
791+ linked_list (const linked_list&) = delete ;
792+ linked_list& operator =(const linked_list&) = delete ;
793+ linked_list (linked_list&& other) noexcept = default ;
794+ linked_list& operator =(linked_list&& other) noexcept {
795+ list_.reverseSweepAndAssign (std::move (other.list_ ), [](Wrapper* node) {
796+ delete node;
797+ });
798+ return *this ;
799+ }
800+
801+ ~linked_list () {
802+ sweep ([](T&&) {});
803+ }
804+
805+ bool empty () const { return list_.empty (); }
806+
807+ /* *
808+ * Atomically insert t at the head of the list.
809+ * @return True if the inserted element is the only one in the list
810+ * after the call.
811+ */
812+ bool insertHead (T t) {
813+ auto wrapper = std::make_unique<Wrapper>(std::move (t));
814+
815+ return list_.insertHead (wrapper.release ());
816+ }
817+
818+ /* *
819+ * Repeatedly pops element from head,
820+ * and calls func() on the removed elements in the order from tail to head.
821+ * Stops when the list is empty.
822+ */
823+ template <typename F>
824+ void sweep (F&& func) {
825+ list_.sweep ([&](Wrapper* wrapperPtr) mutable {
826+ std::unique_ptr<Wrapper> wrapper (wrapperPtr);
827+
828+ func (std::move (wrapper->data ));
829+ });
830+ }
831+
832+ /* *
833+ * Sweeps the list a single time, as a single point in time swap with the
834+ * current contents of the list.
835+ *
836+ * Unlike sweep() it does not loop to ensure the list is empty at some point
837+ * after the last invocation.
838+ *
839+ * Returns false if the list is empty.
840+ */
841+ template <typename F>
842+ bool sweepOnce (F&& func) {
843+ return list_.sweepOnce ([&](Wrapper* wrappedPtr) {
844+ std::unique_ptr<Wrapper> wrapper (wrappedPtr);
845+ func (std::move (wrapper->data ));
846+ });
847+ }
848+
849+ /* *
850+ * Similar to sweep() but calls func() on elements in LIFO order.
851+ *
852+ * func() is called for all elements in the list at the moment
853+ * reverseSweep() is called. Unlike sweep() it does not loop to ensure the
854+ * list is empty at some point after the last invocation. This way callers
855+ * can reason about the ordering: elements inserted since the last call to
856+ * reverseSweep() will be provided in LIFO order.
857+ *
858+ * Example: if elements are inserted in the order 1-2-3, the callback is
859+ * invoked 3-2-1. If the callback moves elements onto a stack, popping off
860+ * the stack will produce the original insertion order 1-2-3.
861+ */
862+ template <typename F>
863+ void reverseSweep (F&& func) {
864+ list_.reverseSweep ([&](Wrapper* wrapperPtr) mutable {
865+ std::unique_ptr<Wrapper> wrapper (wrapperPtr);
866+
867+ func (std::move (wrapper->data ));
868+ });
869+ }
870+
871+ private:
872+ struct Wrapper {
873+ explicit Wrapper (T&& t) : data(std::move(t)) {}
874+
875+ _linked_list_hook<Wrapper> hook;
876+ T data;
877+ };
878+ _linked_list<Wrapper, &Wrapper::hook> list_;
879+ };
880+
881+ } // namespace lock_free
0 commit comments