157 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma once
 | |
| 
 | |
| #include "spinlock.h"
 | |
| #include <thread>
 | |
| 
 | |
| namespace vlc
 | |
| {
 | |
| 	template<typename T, size_t max_capacity = 8> class FixedRingBuffer
 | |
| 	{
 | |
| 		static_assert((max_capacity & (max_capacity - 1)) == 0, "Capacity is not power of 2!");
 | |
| 
 | |
| 		Spinlock m_lock;
 | |
| 		volatile uint32_t m_write = 0;
 | |
| 		volatile uint32_t m_read = 0;
 | |
| 
 | |
| 		T m_buffer[max_capacity];
 | |
| 
 | |
| 		static inline size_t index(uint32_t x)
 | |
| 		{
 | |
| 			return x & (max_capacity - 1);
 | |
| 		}
 | |
| 
 | |
| 	public:
 | |
| 		FixedRingBuffer() = default;
 | |
| 
 | |
| 		FixedRingBuffer(const FixedRingBuffer& src) : m_write(src.m_write), m_read(src.m_read)
 | |
| 		{
 | |
| 			memcpy(m_buffer, src.m_buffer, max_capacity * sizeof(T));
 | |
| 		}
 | |
| 
 | |
| 		FixedRingBuffer& operator = (const FixedRingBuffer& src)
 | |
| 		{
 | |
| 			*this = src;
 | |
| 			return *this;
 | |
| 		}
 | |
| 
 | |
| 		FixedRingBuffer(FixedRingBuffer&& src) = delete;
 | |
| 		FixedRingBuffer& operator = (FixedRingBuffer&&) = delete;
 | |
| 
 | |
| 		size_t size() const
 | |
| 		{
 | |
| 			return m_write - m_read;
 | |
| 		}
 | |
| 
 | |
| 		size_t capacity() const
 | |
| 		{
 | |
| 			return max_capacity;
 | |
| 		}
 | |
| 
 | |
| 		bool empty() const
 | |
| 		{
 | |
| 			return m_write == m_read;
 | |
| 		}
 | |
| 
 | |
| 		bool full() const
 | |
| 		{
 | |
| 			return size() == capacity();
 | |
| 		}
 | |
| 
 | |
| 		void clear()
 | |
| 		{
 | |
| 			m_write = m_read = 0;
 | |
| 		}
 | |
| 
 | |
| 		bool try_push(const T& value)
 | |
| 		{
 | |
| 			uint32_t target_idx;
 | |
| 			{
 | |
| 				SpinlockGuard guard(m_lock);
 | |
| 
 | |
| 				if (full())
 | |
| 					return false;
 | |
| 
 | |
| 				target_idx = m_write;
 | |
| 
 | |
| 				m_buffer[index(target_idx)] = value;
 | |
| 				
 | |
| 				std::atomic_thread_fence(std::memory_order_release);
 | |
| 
 | |
| 				m_write = target_idx + 1;
 | |
| 			}			
 | |
| 
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		bool try_push(T&& value)
 | |
| 		{
 | |
| 			uint32_t target_idx;
 | |
| 			{
 | |
| 				SpinlockGuard guard(m_lock);
 | |
| 
 | |
| 				if (full())
 | |
| 					return false;
 | |
| 
 | |
| 				target_idx = m_write;
 | |
| 
 | |
| 				m_buffer[index(target_idx)] = std::forward<T>(value);
 | |
| 
 | |
| 				std::atomic_thread_fence(std::memory_order_release);
 | |
| 
 | |
| 				m_write = target_idx + 1;
 | |
| 			}			
 | |
| 
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		bool try_pop(T& value)
 | |
| 		{
 | |
| 			uint32_t target_idx;
 | |
| 			{
 | |
| 				SpinlockGuard guard(m_lock);
 | |
| 
 | |
| 				if (empty())
 | |
| 					return false;
 | |
| 
 | |
| 				target_idx = m_read;
 | |
| 
 | |
| 				value = std::move(m_buffer[index(target_idx)]);
 | |
| 
 | |
| 				m_read = target_idx + 1;
 | |
| 
 | |
| 				assert(m_read <= m_write);
 | |
| 			}			
 | |
| 
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		void push(const T& value)
 | |
| 		{
 | |
| 			while (!try_push(value))
 | |
| 			{
 | |
| 				std::this_thread::yield();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		void push(T& value)
 | |
| 		{
 | |
| 			while (!try_push(value))
 | |
| 			{
 | |
| 				std::this_thread::yield();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		T pop()
 | |
| 		{
 | |
| 			T result;
 | |
| 
 | |
| 			while (!try_pop(result))
 | |
| 			{
 | |
| 				std::this_thread::yield();
 | |
| 			}
 | |
| 
 | |
| 			return result;
 | |
| 		}
 | |
| 	};
 | |
| }
 |