Luna/libluna/include/luna/CircularQueue.h

135 lines
3.3 KiB
C
Raw Normal View History

#pragma once
#include <luna/Atomic.h>
#include <luna/Heap.h>
#include <luna/Result.h>
#include <luna/Types.h>
template <typename T, usize Size> class CircularQueue
{
enum
{
Capacity = Size + 1
};
public:
CircularQueue()
{
}
2023-08-02 09:55:08 +00:00
bool is_empty()
{
return m_tail.load() == m_head.load();
}
bool try_push(const T& value)
{
usize current_tail = m_tail.load(MemoryOrder::Relaxed);
const usize new_tail = (current_tail + 1) % Capacity;
if (new_tail == m_head.load(MemoryOrder::Acquire))
{
// Queue is full
return false;
}
m_data[current_tail] = value;
if (!m_tail.compare_exchange_strong(current_tail, new_tail, MemoryOrder::Release, MemoryOrder::Relaxed))
{
// Someone else updated the tail
return false;
}
return true;
}
bool try_pop(T& value)
{
usize current_head = m_head.load(MemoryOrder::Relaxed);
if (current_head == m_tail.load(MemoryOrder::Acquire))
{
// Queue is empty
return false;
}
value = m_data[current_head];
const usize new_head = (current_head + 1) % Capacity;
if (!m_head.compare_exchange_strong(current_head, new_head, MemoryOrder::Release, MemoryOrder::Relaxed))
{
// Someone else updated the head
return false;
}
return true;
}
private:
T m_data[Capacity];
Atomic<usize> m_head = 0;
Atomic<usize> m_tail = 0;
};
template <typename T> class DynamicCircularQueue
{
public:
DynamicCircularQueue()
{
}
~DynamicCircularQueue()
{
if (m_data) free_impl(m_data);
}
2023-08-02 09:55:08 +00:00
bool is_empty()
{
return m_tail.load() == m_head.load();
}
Result<void> set_size(usize size)
{
m_data = (T*)TRY(calloc_impl(size + 1, sizeof(T), false));
m_capacity = size + 1;
return {};
}
bool try_push(const T& value)
{
check(m_capacity);
usize current_tail = m_tail.load(MemoryOrder::Relaxed);
const usize new_tail = (current_tail + 1) % m_capacity;
if (new_tail == m_head.load(MemoryOrder::Acquire))
{
// Queue is full
return false;
}
m_data[current_tail] = value;
if (!m_tail.compare_exchange_strong(current_tail, new_tail, MemoryOrder::Release, MemoryOrder::Relaxed))
{
// Someone else updated the tail
return false;
}
return true;
}
bool try_pop(T& value)
{
check(m_capacity);
usize current_head = m_head.load(MemoryOrder::Relaxed);
if (current_head == m_tail.load(MemoryOrder::Acquire))
{
// Queue is empty
return false;
}
value = m_data[current_head];
const usize new_head = (current_head + 1) % m_capacity;
if (!m_head.compare_exchange_strong(current_head, new_head, MemoryOrder::Release, MemoryOrder::Relaxed))
{
// Someone else updated the head
return false;
}
return true;
}
private:
T* m_data = nullptr;
usize m_capacity = 0;
Atomic<usize> m_head = 0;
Atomic<usize> m_tail = 0;
};