#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() { } bool is_empty() const { 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); } bool is_empty() const { 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; };