diff --git a/Utilities/lockless.h b/Utilities/lockless.h index 4402b2d044..af73c9772e 100644 --- a/Utilities/lockless.h +++ b/Utilities/lockless.h @@ -163,6 +163,9 @@ class lf_queue_item final template friend class lf_queue; + template + friend class lf_bunch; + constexpr lf_queue_item() = default; template @@ -195,6 +198,9 @@ class lf_queue_iterator template friend class lf_queue_slice; + template + friend class lf_bunch; + public: constexpr lf_queue_iterator() = default; @@ -456,6 +462,81 @@ public: } }; +// Concurrent linked list, elements remain until destroyed. +template +class lf_bunch final +{ + atomic_t*> m_head{nullptr}; + +public: + constexpr lf_bunch() noexcept = default; + + ~lf_bunch() + { + delete m_head.load(); + } + + // Add unconditionally + template + T* push(Args&&... args) noexcept + { + auto _old = m_head.load(); + auto item = new lf_queue_item(_old, std::forward(args)...); + + while (!m_head.compare_exchange(_old, item)) + { + item->m_link = _old; + } + + return &item->m_data; + } + + // Add if pred(item, all_items) is true for all existing items + template + T* push_if(F pred, Args&&... args) noexcept + { + auto _old = m_head.load(); + auto _chk = _old; + auto item = new lf_queue_item(_old, std::forward(args)...); + + _chk = nullptr; + + do + { + item->m_link = _old; + + // Check all items in the queue + for (auto ptr = _old; ptr != _chk; ptr = ptr->m_link) + { + if (!pred(item->m_data, ptr->m_data)) + { + item->m_link = nullptr; + delete item; + return nullptr; + } + } + + // Set to not check already checked items + _chk = _old; + } + while (!m_head.compare_exchange(_old, item)); + + return &item->m_data; + } + + lf_queue_iterator begin() const + { + lf_queue_iterator result; + result.m_ptr = m_head.load(); + return result; + } + + lf_queue_iterator end() const + { + return {}; + } +}; + // Assignable lock-free thread-safe value of any type (memory-inefficient) template class lf_value final