/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2019 Danny Robson */ #pragma once #include "../thread/primitive.hpp" #include "../debug/assert.hpp" #include "../thread/spinlock.hpp" #include #include #include #include namespace cruft::parallel { /// A trivial thread safe stack with a fixed capacity. /// /// This implementation uses a spinlock, so don't try to push large /// objects under high contention. In the future it may be replaced with a /// different locking mechanism. template class stack { public: stack (stack const &) = delete; stack& operator=(stack const &) = delete; stack (stack &&); stack& operator= (stack &&); stack (std::size_t capacity) : m_cursor (0) , m_store (capacity) { CHECK_GT (capacity, 0u); } /// Move the value from the top of the stack into an output pointer. /// /// The internal object will be destroyed after the output has been set. /// /// The value `true` will be returned if the value was successfully /// copied. If no object is available for popping, or locking failed, /// the value `false` will be returned. /// /// NOTE: There are no exception guarantees at this time. bool pop (ValueT *out) { std::lock_guard lk (m_lock); if (m_cursor == 0) return false; auto ptr = reinterpret_cast (&m_store[--m_cursor]); *out = std::move (*ptr); ptr->~ValueT (); return true; } /// Constructs a ValueT on the top of the stack. /// /// The value `true` will be returned if the object was successfully /// stored. In the event of a lack of capacity, locking failure, or /// other error the value `false` will be returned and the user may /// retry the operation. /// /// NOTE: There are no exception guarantees at this time. template bool push (InputT &&arg) { std::lock_guard lk (m_lock); if (m_cursor >= m_store.size ()) return false; auto ptr = reinterpret_cast (&m_store[m_cursor++]); new (ptr) ValueT (std::forward (arg)); return true; } private: using index_type = std::size_t; using raw_type = std::aligned_storage_t; cruft::thread::spinlock m_lock; std::atomic m_cursor; std::vector m_store; }; }