2019-09-01 13:26:16 +00:00
|
|
|
#include <util/thread/queue_manager.hpp>
|
2019-01-10 19:41:51 +00:00
|
|
|
|
2020-05-01 19:51:15 +00:00
|
|
|
#include <optional>
|
2018-11-17 21:13:34 +00:00
|
|
|
#include <vector>
|
2021-03-01 21:07:32 +00:00
|
|
|
#include <catch2/catch.hpp>
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
using namespace llarp::thread;
|
|
|
|
|
|
|
|
void
|
|
|
|
generation(QueueManager& manager, uint32_t pushIndex, uint32_t popIndex)
|
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(pushIndex >= popIndex);
|
|
|
|
REQUIRE(pushIndex - popIndex <= manager.capacity());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
for (uint32_t i = 0; i < popIndex; ++i)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
|
|
|
(void)manager.reservePushIndex(gen, index);
|
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
|
|
|
|
auto result = manager.reservePopIndex(gen, index);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(result == QueueReturn::Success);
|
|
|
|
REQUIRE(index == i % manager.capacity());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
manager.commitPopIndex(gen, index);
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
for (uint32_t i = popIndex; i < pushIndex; ++i)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
|
|
|
auto result = manager.reservePushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(result == QueueReturn::Success);
|
|
|
|
REQUIRE(index == i % manager.capacity());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class IntQueue
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
QueueManager manager;
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
std::vector<int> data;
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
IntQueue(const IntQueue&) = delete;
|
|
|
|
|
|
|
|
explicit IntQueue(size_t capacity) : manager(capacity), data(capacity, 0)
|
2021-03-01 21:07:32 +00:00
|
|
|
{}
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
~IntQueue() = default;
|
|
|
|
|
|
|
|
bool
|
|
|
|
tryPushBack(int value)
|
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
if (manager.reservePushIndex(gen, index) == QueueReturn::Success)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
data[index] = value;
|
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2019-02-03 02:13:31 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
std::optional<int>
|
2018-11-17 21:13:34 +00:00
|
|
|
tryPopFront()
|
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
if (manager.reservePopIndex(gen, index) == QueueReturn::Success)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
int result = data[index];
|
|
|
|
manager.commitPopIndex(gen, index);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-05-17 17:44:00 +00:00
|
|
|
return std::nullopt;
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
size() const
|
|
|
|
{
|
|
|
|
return manager.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
capacity() const
|
|
|
|
{
|
|
|
|
return manager.capacity();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// This class exactly mirrors the data of the QueueManager, and is used for
|
|
|
|
// both debugging and whitebox testing.
|
|
|
|
struct QueueData
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
QueueManager::AtomicIndex m_pushIndex; // Index in the buffer that the next
|
|
|
|
// element will be added to.
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
char m_pushPadding[QueueManager::Alignment - sizeof(QueueManager::AtomicIndex)];
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
QueueManager::AtomicIndex m_popIndex; // Index in the buffer that the next
|
|
|
|
// element will be removed from.
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
char m_popPadding[QueueManager::Alignment - sizeof(QueueManager::AtomicIndex)];
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
const size_t m_capacity; // max size of the manager.
|
|
|
|
|
|
|
|
const uint32_t m_maxGeneration; // Maximum generation for this object.
|
|
|
|
|
|
|
|
const uint32_t m_maxCombinedIndex; // Maximum combined value of index and
|
|
|
|
// generation for this object.
|
|
|
|
|
|
|
|
std::uint32_t* m_states; // Array of index states.
|
|
|
|
};
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
static_assert(sizeof(QueueData) == sizeof(QueueManager), "QueueData not updated");
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
static constexpr uint32_t GENERATION_COUNT_SHIFT = 0x2;
|
2021-03-01 21:07:32 +00:00
|
|
|
static constexpr uint32_t ELEMENT_STATE_MASK = 0x3;
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
struct QueueIntrospection
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
const QueueData* data;
|
|
|
|
|
|
|
|
public:
|
|
|
|
QueueIntrospection(const QueueManager& manager)
|
2021-03-01 21:07:32 +00:00
|
|
|
: data(reinterpret_cast<const QueueData*>(&manager))
|
|
|
|
{}
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
uint32_t
|
|
|
|
pushIndex() const
|
|
|
|
{
|
|
|
|
return data->m_pushIndex % capacity();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
pushGeneration() const
|
|
|
|
{
|
|
|
|
return data->m_pushIndex / capacity();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
popIndex() const
|
|
|
|
{
|
|
|
|
return data->m_popIndex % capacity();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
popGeneration() const
|
|
|
|
{
|
|
|
|
return data->m_popIndex / capacity();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
elementGen(uint32_t index) const
|
|
|
|
{
|
|
|
|
return data->m_states[index] >> GENERATION_COUNT_SHIFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
ElementState
|
|
|
|
elementState(uint32_t index) const
|
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
return static_cast<ElementState>(data->m_states[index] & ELEMENT_STATE_MASK);
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
maxGen() const
|
|
|
|
{
|
|
|
|
return data->m_maxGeneration;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
maxCombinedIndex() const
|
|
|
|
{
|
|
|
|
return data->m_maxCombinedIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
capacity() const
|
|
|
|
{
|
|
|
|
return data->m_capacity;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
adjustGeneration(QueueManager& manager, uint32_t gen)
|
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
QueueData* data = reinterpret_cast<QueueData*>(&manager);
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
auto capacity = manager.capacity();
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
for (size_t i = 0; i < capacity; ++i)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
data->m_states[i] = gen << GENERATION_COUNT_SHIFT;
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
*reinterpret_cast<QueueManager::AtomicIndex*>(&data->m_pushIndex) = (gen * capacity);
|
|
|
|
*reinterpret_cast<QueueManager::AtomicIndex*>(&data->m_popIndex) = (gen * capacity);
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-03-01 21:07:32 +00:00
|
|
|
dirtyGenerate(QueueManager& manager, uint32_t pushCombinedIndex, uint32_t popCombinedIndex)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(pushCombinedIndex >= popCombinedIndex);
|
|
|
|
REQUIRE(pushCombinedIndex - popCombinedIndex <= manager.capacity());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
uint32_t capacity = manager.capacity();
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t start = static_cast<uint32_t>(popCombinedIndex / manager.capacity());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
adjustGeneration(manager, start);
|
2021-03-01 21:07:32 +00:00
|
|
|
generation(
|
|
|
|
manager, pushCombinedIndex - (start * capacity), popCombinedIndex - (start * capacity));
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Simple usage")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
IntQueue queue(2);
|
|
|
|
|
|
|
|
bool rc = queue.tryPushBack(1);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(rc);
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
rc = queue.tryPushBack(2);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(rc);
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
rc = queue.tryPushBack(3);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE_FALSE(rc);
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(2u == queue.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
auto result = queue.tryPopFront();
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(result);
|
|
|
|
REQUIRE(1 == *result);
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Push")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t val = GENERATE(range(1u, 100u));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
QueueManager manager(val);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(0u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
for (uint32_t i = 0; i < val; ++i)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(i == index);
|
|
|
|
REQUIRE(0u == gen);
|
|
|
|
REQUIRE(i == manager.size() - 1);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::QueueFull == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(val == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Basic functionality, acquiringPopIndex")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t capacity = GENERATE(range(1u, 100u));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
QueueManager manager(capacity);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(0u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
for (uint32_t g = 0; g < 3; ++g)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
for (uint32_t idx = 0; idx < capacity; ++idx)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::QueueEmpty == manager.reservePopIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(g == gen);
|
|
|
|
REQUIRE(index == idx);
|
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
manager.commitPushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePopIndex(gen, index));
|
|
|
|
REQUIRE(g == gen);
|
|
|
|
REQUIRE(index == idx);
|
|
|
|
REQUIRE(0u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
manager.commitPopIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(0u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Basic functionality, pushIndex")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t capacity = GENERATE(range(1u, 100u));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
QueueManager manager(capacity);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(0u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
uint32_t generation = 0;
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t index = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Fill the queue
|
2021-03-01 21:07:32 +00:00
|
|
|
for (uint32_t idx = 0; idx < capacity; ++idx)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
manager.reservePushIndex(generation, index);
|
|
|
|
manager.commitPushIndex(generation, index);
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(capacity == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
for (uint32_t gen = 0; gen < 3; ++gen)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
for (uint32_t idx = 0; idx < capacity; ++idx)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::QueueFull == manager.reservePushIndex(generation, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePopIndex(generation, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(generation == gen);
|
|
|
|
REQUIRE(index == idx);
|
|
|
|
REQUIRE(capacity - 1 == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
manager.commitPopIndex(generation, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(capacity - 1 == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(generation, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(generation == gen + 1);
|
|
|
|
REQUIRE(index == idx);
|
|
|
|
REQUIRE(manager.size() == capacity);
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
manager.commitPushIndex(generation, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(manager.size() == capacity);
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Potential issues:
|
|
|
|
// - That pushing an element at the max combined index will push the next
|
|
|
|
// element at index 0
|
|
|
|
// - That popping an element at the max combined index will pop the next
|
|
|
|
// element at index 0
|
|
|
|
// - That size returns the correct size when the push index has gone past the
|
|
|
|
// max combined index
|
|
|
|
// - That reservePopIndexForClear and abortPushIndexReservation clear the
|
|
|
|
// correct element and increment push/pop
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Push at max")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
QueueManager manager(1);
|
|
|
|
|
|
|
|
QueueIntrospection state{manager};
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
const uint32_t MAX_COMBINED_INDEX = std::numeric_limits<uint32_t>::max() >> 2;
|
|
|
|
const uint32_t MAX_GENERATION = std::numeric_limits<uint32_t>::max() >> 2;
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
const uint32_t maxGeneration = QueueIntrospection(manager).maxGen();
|
2021-03-01 21:07:32 +00:00
|
|
|
const uint32_t maxCombinedIndex = QueueIntrospection(manager).maxCombinedIndex();
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(maxGeneration == MAX_GENERATION);
|
|
|
|
REQUIRE(maxCombinedIndex == MAX_COMBINED_INDEX);
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
dirtyGenerate(manager, MAX_COMBINED_INDEX, MAX_COMBINED_INDEX);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(0u == manager.size());
|
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(MAX_GENERATION == gen);
|
|
|
|
REQUIRE(0u == index);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePopIndex(gen, index));
|
|
|
|
REQUIRE(MAX_GENERATION == gen);
|
|
|
|
REQUIRE(0u == index);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(0u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(0u == gen);
|
|
|
|
REQUIRE(0u == index);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePopIndex(gen, index));
|
|
|
|
REQUIRE(0u == gen);
|
|
|
|
REQUIRE(0u == index);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(0u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct CombinedIndexData
|
|
|
|
{
|
|
|
|
uint32_t capacity;
|
|
|
|
uint32_t pushIndex;
|
|
|
|
uint32_t popIndex;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& os, CombinedIndexData d)
|
|
|
|
{
|
|
|
|
os << "[ capacity = " << d.capacity << " pushIndex = " << d.pushIndex
|
|
|
|
<< " popIndex = " << d.popIndex << " ]";
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
std::vector<CombinedIndexData> PopAtMaxData{// Capacity 2 queues for a couple generations
|
|
|
|
{2, 1, 0},
|
|
|
|
{2, 2, 0},
|
|
|
|
{2, 2, 1},
|
|
|
|
{2, 2, 2},
|
|
|
|
{2, 3, 1},
|
|
|
|
{2, 3, 2},
|
|
|
|
{2, 3, 3},
|
|
|
|
{2, 4, 2},
|
|
|
|
{2, 4, 3},
|
|
|
|
{2, 4, 4},
|
|
|
|
|
|
|
|
// Capacity 3 queues for a couple generations
|
|
|
|
{3, 2, 0},
|
|
|
|
{3, 3, 0},
|
|
|
|
{3, 3, 1},
|
|
|
|
{3, 3, 2},
|
|
|
|
{3, 3, 3},
|
|
|
|
{3, 4, 1},
|
|
|
|
{3, 4, 2},
|
|
|
|
{3, 4, 3},
|
|
|
|
{3, 4, 4},
|
|
|
|
{3, 5, 2},
|
|
|
|
{3, 5, 3},
|
|
|
|
{3, 5, 4},
|
|
|
|
{3, 5, 5},
|
|
|
|
|
|
|
|
// Capacity 7 queue
|
|
|
|
{7, 6, 0},
|
|
|
|
{7, 7, 0},
|
|
|
|
{7, 7, 6},
|
|
|
|
{7, 13, 7},
|
|
|
|
{7, 14, 7}};
|
|
|
|
|
|
|
|
TEST_CASE("Pop at max")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
const auto& d = GENERATE(from_range(PopAtMaxData));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
QueueManager manager(d.capacity);
|
|
|
|
|
|
|
|
const uint32_t NUM_GEN = QueueManager::numGenerations(d.capacity);
|
|
|
|
const uint32_t MAX_GEN = NUM_GEN - 1;
|
|
|
|
|
|
|
|
adjustGeneration(manager, MAX_GEN);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
|
|
|
// Push and pop elements up until the pop-index.
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
for (size_t j = 0; j < d.popIndex; ++j)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
uint32_t INDEX = j % d.capacity;
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t GEN = (MAX_GEN + j / d.capacity) % NUM_GEN;
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(INDEX == index);
|
|
|
|
REQUIRE(GEN == gen);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePopIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(INDEX == index);
|
|
|
|
REQUIRE(GEN == gen);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(0u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Push elements up to the push index
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
for (size_t j = d.popIndex; j < d.pushIndex; ++j)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
uint32_t INDEX = j % d.capacity;
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t GEN = (MAX_GEN + j / d.capacity) % NUM_GEN;
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(INDEX == index);
|
|
|
|
REQUIRE(GEN == gen);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(j - d.popIndex + 1 == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pop elements until the queue is empty.
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
for (size_t j = d.popIndex; j < d.pushIndex; ++j)
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
uint32_t INDEX = j % d.capacity;
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t GEN = (MAX_GEN + j / d.capacity) % NUM_GEN;
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePopIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(INDEX == index);
|
|
|
|
REQUIRE(GEN == gen);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(d.pushIndex - j - 1 == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
std::vector<CombinedIndexData> ReservePopIndexForClearData{
|
2018-11-17 21:13:34 +00:00
|
|
|
// Capacity 2 queues for a couple generations
|
|
|
|
{2, 1, 0},
|
|
|
|
{2, 2, 1},
|
|
|
|
{2, 2, 2},
|
|
|
|
{2, 3, 2},
|
|
|
|
{2, 3, 3},
|
|
|
|
{2, 4, 3},
|
|
|
|
{2, 4, 4},
|
|
|
|
|
|
|
|
// Capacity 3 queues for a couple generations
|
|
|
|
{3, 2, 0},
|
|
|
|
{3, 3, 1},
|
|
|
|
{3, 3, 2},
|
|
|
|
{3, 3, 3},
|
|
|
|
{3, 4, 2},
|
|
|
|
{3, 4, 3},
|
|
|
|
{3, 4, 4},
|
|
|
|
{3, 5, 3},
|
|
|
|
{3, 5, 4},
|
|
|
|
{3, 5, 5},
|
|
|
|
|
|
|
|
// Capacity 7 queue
|
|
|
|
{7, 6, 0},
|
|
|
|
{7, 7, 6},
|
|
|
|
{7, 13, 7},
|
|
|
|
};
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Reserve pop index for clear")
|
|
|
|
{
|
|
|
|
const auto& d = GENERATE(from_range(ReservePopIndexForClearData));
|
|
|
|
|
|
|
|
QueueManager manager(d.capacity);
|
|
|
|
const uint32_t NUM_GEN = QueueManager::numGenerations(d.capacity);
|
|
|
|
const uint32_t MAX_GEN = NUM_GEN - 1;
|
|
|
|
|
|
|
|
adjustGeneration(manager, MAX_GEN);
|
|
|
|
|
|
|
|
generation(manager, d.pushIndex, d.popIndex);
|
|
|
|
|
|
|
|
// Pop elements until the queue is empty
|
|
|
|
|
|
|
|
uint32_t endGeneration = 0;
|
|
|
|
uint32_t endIndex = 0;
|
|
|
|
uint32_t gen = 0;
|
|
|
|
uint32_t index = 0;
|
|
|
|
|
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(endGeneration, endIndex));
|
|
|
|
|
|
|
|
for (uint32_t j = d.popIndex; j < d.pushIndex; ++j)
|
|
|
|
{
|
|
|
|
uint32_t INDEX = j % d.capacity;
|
|
|
|
uint32_t GEN = (MAX_GEN + j / d.capacity) % NUM_GEN;
|
|
|
|
|
|
|
|
REQUIRE(manager.reservePopForClear(gen, index, endGeneration, endIndex));
|
|
|
|
|
|
|
|
REQUIRE(INDEX == index);
|
|
|
|
REQUIRE(GEN == gen);
|
|
|
|
manager.commitPopIndex(gen, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
REQUIRE_FALSE(manager.reservePopForClear(gen, index, endGeneration, endIndex));
|
|
|
|
manager.abortPushIndexReservation(endGeneration, endIndex);
|
|
|
|
REQUIRE(0u == manager.size());
|
|
|
|
}
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
struct CircularDifferenceData
|
|
|
|
{
|
|
|
|
uint32_t minuend;
|
|
|
|
uint32_t subtrahend;
|
|
|
|
uint32_t maxSize;
|
|
|
|
int32_t expectedValue;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& os, CircularDifferenceData d)
|
|
|
|
{
|
|
|
|
os << "[ minuend = " << d.minuend << " subtrahend = " << d.subtrahend
|
2021-03-01 21:07:32 +00:00
|
|
|
<< " maxSize = " << d.maxSize << " expectedValue = " << d.expectedValue << " ]";
|
2018-11-17 21:13:34 +00:00
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
constexpr uint32_t OUR_INT32_MAX = std::numeric_limits<int32_t>::max();
|
|
|
|
constexpr uint32_t OUR_INT32_MAX_1 = OUR_INT32_MAX + 1;
|
2018-11-17 21:13:34 +00:00
|
|
|
constexpr int32_t OUR_INT32_MAX_DIV = OUR_INT32_MAX_1 / 2;
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
std::vector<CircularDifferenceData> circularDifferenceData{
|
2018-11-17 21:13:34 +00:00
|
|
|
// capacity 1
|
|
|
|
{0, 0, 1, 0},
|
|
|
|
|
|
|
|
// capacity 2
|
|
|
|
{1, 1, 2, 0},
|
|
|
|
{1, 0, 2, 1},
|
|
|
|
{0, 1, 2, -1},
|
|
|
|
|
|
|
|
// capacity 3
|
|
|
|
{2, 0, 3, -1},
|
|
|
|
{2, 1, 3, 1},
|
|
|
|
{2, 2, 3, 0},
|
|
|
|
{1, 0, 3, 1},
|
|
|
|
{1, 1, 3, 0},
|
|
|
|
{1, 2, 3, -1},
|
|
|
|
{0, 0, 3, 0},
|
|
|
|
{0, 1, 3, -1},
|
|
|
|
{0, 2, 3, 1},
|
|
|
|
|
|
|
|
// capacity 4
|
|
|
|
{3, 0, 4, -1},
|
|
|
|
{3, 1, 4, 2},
|
|
|
|
{3, 2, 4, 1},
|
|
|
|
{3, 3, 4, 0},
|
|
|
|
{0, 3, 4, 1},
|
|
|
|
{1, 3, 4, -2},
|
|
|
|
{2, 3, 4, -1},
|
|
|
|
{3, 3, 4, 0},
|
|
|
|
|
|
|
|
// capacity INT_MAX
|
|
|
|
{OUR_INT32_MAX, 0, OUR_INT32_MAX_1, -1},
|
|
|
|
{0, OUR_INT32_MAX, OUR_INT32_MAX_1, 1},
|
|
|
|
{OUR_INT32_MAX_DIV, 0, OUR_INT32_MAX_1, OUR_INT32_MAX_DIV},
|
|
|
|
{0, OUR_INT32_MAX_DIV, OUR_INT32_MAX_1, -OUR_INT32_MAX_DIV},
|
|
|
|
|
|
|
|
// Examples circularDifference( 0, 359, 360) == 1
|
|
|
|
// circularDifference( 359, 0, 360) == -1 circularDifference(
|
|
|
|
// 180, 0, 360) == 180 circularDifference( 0, 180, 360) == -180
|
|
|
|
|
|
|
|
{0, 359, 360, 1},
|
|
|
|
{359, 0, 360, -1},
|
|
|
|
{180, 0, 360, 180},
|
|
|
|
{0, 180, 360, -180},
|
|
|
|
};
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Circular difference")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
const auto& data = GENERATE(from_range(circularDifferenceData));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(
|
|
|
|
data.expectedValue
|
|
|
|
== QueueManager::circularDifference(data.minuend, data.subtrahend, data.maxSize));
|
|
|
|
}
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
std::vector<uint32_t> GenerationData{
|
|
|
|
1, 2, 3, 4, 15, 16, 17, QueueManager::MAX_CAPACITY - 1, QueueManager::MAX_CAPACITY};
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Num generations")
|
|
|
|
{
|
|
|
|
uint32_t capacity = GENERATE(from_range(GenerationData));
|
|
|
|
uint32_t numGen = QueueManager::numGenerations(capacity);
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
static const uint32_t MAX_ELEMENT_STATE_GEN = std::numeric_limits<uint32_t>::max() >> 2;
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
static const uint32_t MAX_COMBINED_INDEX = std::numeric_limits<uint32_t>::max() >> 1;
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(numGen >= 2u);
|
|
|
|
REQUIRE(
|
|
|
|
((MAX_ELEMENT_STATE_GEN == numGen - 1)
|
|
|
|
|| ((numGen * capacity - 1 <= MAX_COMBINED_INDEX)
|
|
|
|
&& ((numGen + 1) * capacity - 1 > MAX_COMBINED_INDEX))));
|
|
|
|
}
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Abort push index reservation")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t genA = 0;
|
|
|
|
uint32_t genB = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t indexA = 0;
|
|
|
|
uint32_t indexB = 0;
|
|
|
|
|
|
|
|
QueueManager manager(1);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(genA, indexA));
|
|
|
|
REQUIRE(QueueReturn::Success != manager.reservePushIndex(genA, indexA));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
manager.abortPushIndexReservation(genA, indexA);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(0u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(genB, indexB));
|
|
|
|
REQUIRE(genA + 1 == genB);
|
|
|
|
REQUIRE(indexA == indexB);
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct AbortData
|
|
|
|
{
|
|
|
|
uint32_t capacity;
|
|
|
|
uint32_t pushIndex;
|
|
|
|
uint32_t popIndex;
|
|
|
|
uint32_t expectedClears;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& os, AbortData d)
|
|
|
|
{
|
|
|
|
os << "[ capacity = " << d.capacity << " pushIndex = " << d.pushIndex
|
2021-03-01 21:07:32 +00:00
|
|
|
<< " popIndex = " << d.popIndex << " expectedClears = " << d.expectedClears << " ]";
|
2018-11-17 21:13:34 +00:00
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
std::vector<AbortData> abortData{
|
2018-11-17 21:13:34 +00:00
|
|
|
{1, 0, 0, 0},
|
|
|
|
|
|
|
|
// Capacity 2 queues for a couple generations
|
|
|
|
{2, 0, 0, 0},
|
|
|
|
{2, 1, 0, 1},
|
|
|
|
{2, 1, 1, 0},
|
|
|
|
{2, 2, 1, 1},
|
|
|
|
{2, 2, 2, 0},
|
|
|
|
{2, 3, 2, 1},
|
|
|
|
{2, 3, 3, 0},
|
|
|
|
|
|
|
|
// Capacity 3 queues for a couple generations
|
|
|
|
{3, 0, 0, 0},
|
|
|
|
{3, 1, 0, 1},
|
|
|
|
{3, 1, 1, 0},
|
|
|
|
{3, 2, 0, 2},
|
|
|
|
{3, 2, 1, 1},
|
|
|
|
{3, 2, 2, 0},
|
|
|
|
{3, 3, 1, 2},
|
|
|
|
{3, 3, 2, 1},
|
|
|
|
{3, 3, 3, 0},
|
|
|
|
{3, 4, 2, 2},
|
|
|
|
{3, 4, 3, 1},
|
|
|
|
{3, 4, 4, 0},
|
|
|
|
|
|
|
|
// Capacity 7 queue
|
|
|
|
{7, 14, 14, 0},
|
|
|
|
{7, 15, 14, 1},
|
|
|
|
{7, 20, 14, 6},
|
|
|
|
{7, 18, 18, 0},
|
|
|
|
{7, 19, 18, 1},
|
|
|
|
{7, 24, 18, 6},
|
|
|
|
};
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Abort push")
|
|
|
|
{
|
|
|
|
const auto& data = GENERATE(from_range(abortData));
|
|
|
|
|
|
|
|
QueueManager manager(data.capacity);
|
|
|
|
|
|
|
|
generation(manager, data.pushIndex, data.popIndex);
|
|
|
|
|
|
|
|
const uint32_t END_GENERATION = data.pushIndex / data.capacity;
|
|
|
|
const uint32_t END_INDEX = data.pushIndex % data.capacity;
|
|
|
|
|
|
|
|
uint32_t gen = 0;
|
|
|
|
uint32_t index = 0;
|
|
|
|
|
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(END_GENERATION == gen);
|
|
|
|
REQUIRE(END_INDEX == index);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < data.expectedClears; ++i)
|
|
|
|
{
|
|
|
|
REQUIRE(manager.reservePopForClear(gen, index, END_GENERATION, END_INDEX));
|
|
|
|
|
|
|
|
REQUIRE((data.popIndex + i) / data.capacity == gen);
|
|
|
|
REQUIRE((data.popIndex + i) % data.capacity == index);
|
|
|
|
|
|
|
|
manager.commitPopIndex(gen, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
REQUIRE_FALSE(manager.reservePopForClear(gen, index, END_GENERATION, END_INDEX));
|
|
|
|
|
|
|
|
manager.abortPushIndexReservation(END_GENERATION, END_INDEX);
|
|
|
|
|
|
|
|
// Verify the queue is now empty, and the current push index has changed
|
|
|
|
|
|
|
|
REQUIRE(0u == manager.size());
|
|
|
|
for (uint32_t i = 0; i < data.capacity; ++i)
|
|
|
|
{
|
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(i + 1 == manager.size());
|
|
|
|
|
|
|
|
REQUIRE(END_GENERATION * data.capacity + END_INDEX + i + 1 == gen * data.capacity + index);
|
|
|
|
}
|
|
|
|
|
|
|
|
REQUIRE(QueueReturn::Success != manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(data.capacity == manager.size());
|
|
|
|
}
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Testing reservePopForClear
|
|
|
|
// - Failure is returned when the head of the queue is the same as the given end
|
|
|
|
// generation and index
|
|
|
|
// - Success is returned and clears the queue head when the current pop index is
|
|
|
|
// not the given end generation and index
|
|
|
|
// - We do not clear an index reserved for popping
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Capacity 1")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
// It is not possible to clear a pop index when the capacity is 1.
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
|
|
|
// Random values to verify we didn't change them.
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t resultGen = 1024;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t resultIndex = 1023;
|
|
|
|
|
|
|
|
QueueManager manager(1);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE_FALSE(manager.reservePopForClear(resultGen, resultIndex, gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(1024u == resultGen);
|
|
|
|
REQUIRE(1023u == resultIndex);
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Capacity 2")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
|
|
|
// Random values to verify we didn't change them.
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t resultGen = 1024;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t resultIndex = 1023;
|
|
|
|
|
|
|
|
QueueManager manager(2);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE_FALSE(manager.reservePopForClear(resultGen, resultIndex, gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(1024u == resultGen);
|
|
|
|
REQUIRE(1023u == resultIndex);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(manager.reservePopForClear(resultGen, resultIndex, gen, index));
|
|
|
|
REQUIRE(0u == resultGen);
|
|
|
|
REQUIRE(0u == resultIndex);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(resultGen, resultIndex);
|
|
|
|
|
|
|
|
manager.commitPushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(manager.reservePopForClear(resultGen, resultIndex, gen, index));
|
|
|
|
REQUIRE(0u == resultGen);
|
|
|
|
REQUIRE(1u == resultIndex);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(resultGen, resultIndex);
|
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(manager.reservePopForClear(resultGen, resultIndex, gen, index));
|
|
|
|
REQUIRE(1u == resultGen);
|
|
|
|
REQUIRE(0u == resultIndex);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(resultGen, resultIndex);
|
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(manager.reservePopForClear(resultGen, resultIndex, gen, index));
|
|
|
|
REQUIRE(1u == resultGen);
|
|
|
|
REQUIRE(1u == resultIndex);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(resultGen, resultIndex);
|
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(manager.reservePopForClear(resultGen, resultIndex, gen, index));
|
|
|
|
REQUIRE(2u == resultGen);
|
|
|
|
REQUIRE(0u == resultIndex);
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(resultGen, resultIndex);
|
|
|
|
manager.commitPushIndex(gen, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ReserveData
|
|
|
|
{
|
|
|
|
uint32_t capacity;
|
|
|
|
uint32_t pushIndex;
|
|
|
|
uint32_t popIndex;
|
|
|
|
uint32_t expectedClears;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& os, ReserveData d)
|
|
|
|
{
|
|
|
|
os << "[ capacity = " << d.capacity << " pushIndex = " << d.pushIndex
|
2021-03-01 21:07:32 +00:00
|
|
|
<< " popIndex = " << d.popIndex << " expectedClears = " << d.expectedClears << " ]";
|
2018-11-17 21:13:34 +00:00
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
std::vector<ReserveData> reserveData{
|
2018-11-17 21:13:34 +00:00
|
|
|
{1, 0, 0, 0},
|
|
|
|
|
|
|
|
// Capacity 2 queues for a couple generations
|
|
|
|
{2, 0, 0, 0},
|
|
|
|
{2, 1, 0, 1},
|
|
|
|
{2, 1, 1, 0},
|
|
|
|
{2, 2, 1, 1},
|
|
|
|
{2, 2, 2, 0},
|
|
|
|
{2, 3, 2, 1},
|
|
|
|
{2, 3, 3, 0},
|
|
|
|
|
|
|
|
// Capacity 3 queues for a couple generations
|
|
|
|
{3, 0, 0, 0},
|
|
|
|
{3, 1, 0, 1},
|
|
|
|
{3, 1, 1, 0},
|
|
|
|
{3, 2, 0, 2},
|
|
|
|
{3, 2, 1, 1},
|
|
|
|
{3, 2, 2, 0},
|
|
|
|
{3, 3, 1, 2},
|
|
|
|
{3, 3, 2, 1},
|
|
|
|
{3, 3, 3, 0},
|
|
|
|
{3, 4, 2, 2},
|
|
|
|
{3, 4, 3, 1},
|
|
|
|
{3, 4, 4, 0},
|
|
|
|
|
|
|
|
// Capacity 7 queue
|
|
|
|
{7, 14, 14, 0},
|
|
|
|
{7, 15, 14, 1},
|
|
|
|
{7, 20, 14, 6},
|
|
|
|
{7, 18, 18, 0},
|
|
|
|
{7, 19, 18, 1},
|
|
|
|
{7, 24, 18, 6},
|
|
|
|
};
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Reserve, clear")
|
|
|
|
{
|
|
|
|
const auto& data = GENERATE(from_range(reserveData));
|
|
|
|
QueueManager manager(data.capacity);
|
|
|
|
|
|
|
|
generation(manager, data.pushIndex, data.popIndex);
|
|
|
|
|
|
|
|
const uint32_t endGen = data.pushIndex / data.capacity;
|
|
|
|
const uint32_t endIdx = data.pushIndex % data.capacity;
|
|
|
|
|
|
|
|
uint32_t gen = 0;
|
|
|
|
uint32_t index = 0;
|
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(endGen == gen);
|
|
|
|
REQUIRE(endIdx == index);
|
|
|
|
|
|
|
|
for (unsigned int j = 0; j < data.expectedClears; ++j)
|
|
|
|
{
|
|
|
|
REQUIRE(manager.reservePopForClear(gen, index, endGen, endIdx));
|
|
|
|
REQUIRE((data.popIndex + j) / data.capacity == gen);
|
|
|
|
REQUIRE((data.popIndex + j) % data.capacity == index);
|
|
|
|
manager.commitPopIndex(gen, index);
|
|
|
|
}
|
|
|
|
REQUIRE_FALSE(manager.reservePopForClear(gen, index, endGen, endIdx));
|
|
|
|
manager.commitPushIndex(endGen, endIdx);
|
|
|
|
REQUIRE(1u == manager.size());
|
|
|
|
}
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
TEST_CASE("Enabled")
|
2018-11-17 21:13:34 +00:00
|
|
|
{
|
|
|
|
QueueManager manager(3);
|
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(manager.enabled());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
uint32_t gen = 0;
|
2018-11-17 21:13:34 +00:00
|
|
|
uint32_t index = 0;
|
|
|
|
|
|
|
|
// Insert 2 elements.
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(2u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Disable the queue.
|
|
|
|
manager.disable();
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE_FALSE(manager.enabled());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Test that attempting to push fails.
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::QueueDisabled == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(2u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Test that attempting to pop succeeds.
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePopIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Test that attempting to push still fails.
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::QueueDisabled == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Disable the queue a second time, and verify that has no effect.
|
|
|
|
manager.disable();
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE_FALSE(manager.enabled());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Test that attempting to push still fails.
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::QueueDisabled == manager.reservePushIndex(gen, index));
|
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Enable the queue.
|
|
|
|
manager.enable();
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(manager.enabled());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Test that attempting to push succeeds.
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(2u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Test that attempting to pop succeeds.
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePopIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPopIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(1u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Enable the queue a second time, and verify that has no effect.
|
|
|
|
manager.enable();
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(manager.enabled());
|
2018-11-17 21:13:34 +00:00
|
|
|
|
|
|
|
// Test that attempting to push succeeds.
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(QueueReturn::Success == manager.reservePushIndex(gen, index));
|
2018-11-17 21:13:34 +00:00
|
|
|
manager.commitPushIndex(gen, index);
|
2021-03-01 21:07:32 +00:00
|
|
|
REQUIRE(2u == manager.size());
|
2018-11-17 21:13:34 +00:00
|
|
|
}
|