libcruft-util/parallel/stack.hpp

90 lines
2.7 KiB
C++
Raw Normal View History

/*
* 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 <danny@nerdcruft.net>
*/
#pragma once
#include "../thread/primitive.hpp"
#include "../debug/assert.hpp"
#include "../thread/spinlock.hpp"
#include <atomic>
#include <vector>
#include <mutex>
#include <cstddef>
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 <typename ValueT>
class stack {
public:
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<ValueT*> (&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 <typename InputT>
bool push (InputT &&arg)
{
std::lock_guard lk (m_lock);
if (m_cursor >= m_store.size ())
return false;
auto ptr = reinterpret_cast<ValueT *> (&m_store[m_cursor++]);
new (ptr) ValueT (std::forward<InputT> (arg));
return true;
}
private:
using index_type = std::size_t;
using raw_type = std::aligned_storage_t<sizeof(ValueT), alignof(ValueT)>;
cruft::thread::spinlock m_lock;
std::atomic<index_type> m_cursor;
std::vector<raw_type> m_store;
};
}