#ifndef BEAST_INTRUSIVE_LOCKFREESTACK_H_INCLUDED #define BEAST_INTRUSIVE_LOCKFREESTACK_H_INCLUDED #include #include #include namespace beast { //------------------------------------------------------------------------------ template class LockFreeStackIterator { protected: using Node = typename Container::Node; using NodePtr = typename std::conditional::type; public: using iterator_category = std::forward_iterator_tag; using value_type = typename Container::value_type; using difference_type = typename Container::difference_type; using pointer = typename std::conditional< IsConst, typename Container::const_pointer, typename Container::pointer>::type; using reference = typename std::conditional< IsConst, typename Container::const_reference, typename Container::reference>::type; LockFreeStackIterator() : m_node() { } LockFreeStackIterator(NodePtr node) : m_node(node) { } template explicit LockFreeStackIterator( LockFreeStackIterator const& other) : m_node(other.m_node) { } LockFreeStackIterator& operator=(NodePtr node) { m_node = node; return static_cast(*this); } LockFreeStackIterator& operator++() { m_node = m_node->m_next.load(); return static_cast(*this); } LockFreeStackIterator operator++(int) { LockFreeStackIterator result(*this); m_node = m_node->m_next; return result; } NodePtr node() const { return m_node; } reference operator*() const { return *this->operator->(); } pointer operator->() const { return static_cast(m_node); } private: NodePtr m_node; }; //------------------------------------------------------------------------------ template bool operator==( LockFreeStackIterator const& lhs, LockFreeStackIterator const& rhs) { return lhs.node() == rhs.node(); } template bool operator!=( LockFreeStackIterator const& lhs, LockFreeStackIterator const& rhs) { return lhs.node() != rhs.node(); } //------------------------------------------------------------------------------ /** Multiple Producer, Multiple Consumer (MPMC) intrusive stack. This stack is implemented using the same intrusive interface as List. All mutations are lock-free. The caller is responsible for preventing the "ABA" problem: http://en.wikipedia.org/wiki/ABA_problem @param Tag A type name used to distinguish lists and nodes, for putting objects in multiple lists. If this parameter is omitted, the default tag is used. */ template class LockFreeStack { public: class Node { public: Node() : m_next(nullptr) { } explicit Node(Node* next) : m_next(next) { } Node(Node const&) = delete; Node& operator=(Node const&) = delete; private: friend class LockFreeStack; template friend class LockFreeStackIterator; std::atomic m_next; }; public: using value_type = Element; using pointer = Element*; using reference = Element&; using const_pointer = Element const*; using const_reference = Element const&; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using iterator = LockFreeStackIterator, false>; using const_iterator = LockFreeStackIterator, true>; LockFreeStack() : m_end(nullptr), m_head(&m_end) { } LockFreeStack(LockFreeStack const&) = delete; LockFreeStack& operator=(LockFreeStack const&) = delete; /** Returns true if the stack is empty. */ bool empty() const { return m_head.load() == &m_end; } /** Push a node onto the stack. The caller is responsible for preventing the ABA problem. This operation is lock-free. Thread safety: Safe to call from any thread. @param node The node to push. @return `true` if the stack was previously empty. If multiple threads are attempting to push, only one will receive `true`. */ // VFALCO NOTE Fix this, shouldn't it be a reference like intrusive list? bool push_front(Node* node) { bool first; Node* old_head = m_head.load(std::memory_order_relaxed); do { first = (old_head == &m_end); node->m_next = old_head; } while (!m_head.compare_exchange_strong( old_head, node, std::memory_order_release, std::memory_order_relaxed)); return first; } /** Pop an element off the stack. The caller is responsible for preventing the ABA problem. This operation is lock-free. Thread safety: Safe to call from any thread. @return The element that was popped, or `nullptr` if the stack was empty. */ Element* pop_front() { Node* node = m_head.load(); Node* new_head; do { if (node == &m_end) return nullptr; new_head = node->m_next.load(); } while (!m_head.compare_exchange_strong( node, new_head, std::memory_order_release, std::memory_order_relaxed)); return static_cast(node); } /** Return a forward iterator to the beginning or end of the stack. Undefined behavior results if push_front or pop_front is called while an iteration is in progress. Thread safety: Caller is responsible for synchronization. */ /** @{ */ iterator begin() { return iterator(m_head.load()); } iterator end() { return iterator(&m_end); } const_iterator begin() const { return const_iterator(m_head.load()); } const_iterator end() const { return const_iterator(&m_end); } const_iterator cbegin() const { return const_iterator(m_head.load()); } const_iterator cend() const { return const_iterator(&m_end); } /** @} */ private: Node m_end; std::atomic m_head; }; } // namespace beast #endif