#pragma once #include #include #include #include template class CircularQueue { enum { Capacity = Size + 1 }; public: CircularQueue() { } 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 m_head = 0; Atomic m_tail = 0; }; template class DynamicCircularQueue { public: DynamicCircularQueue() { } ~DynamicCircularQueue() { if (m_data) free_impl(m_data); } bool is_empty() { return m_tail.load() == m_head.load(); } Result 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 m_head = 0; Atomic m_tail = 0; };