You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
2.4 KiB

1 year ago
  1. #pragma once
  2. #include "spinlock.h"
  3. #include <thread>
  4. namespace vlc
  5. {
  6. template<typename T, size_t max_capacity = 8> class FixedRingBuffer
  7. {
  8. static_assert((max_capacity & (max_capacity - 1)) == 0, "Capacity is not power of 2!");
  9. Spinlock m_lock;
  10. volatile uint32_t m_write = 0;
  11. volatile uint32_t m_read = 0;
  12. T m_buffer[max_capacity];
  13. static inline size_t index(uint32_t x)
  14. {
  15. return x & (max_capacity - 1);
  16. }
  17. public:
  18. FixedRingBuffer() = default;
  19. FixedRingBuffer(const FixedRingBuffer& src) : m_write(src.m_write), m_read(src.m_read)
  20. {
  21. memcpy(m_buffer, src.m_buffer, max_capacity * sizeof(T));
  22. }
  23. FixedRingBuffer& operator = (const FixedRingBuffer& src)
  24. {
  25. *this = src;
  26. return *this;
  27. }
  28. FixedRingBuffer(FixedRingBuffer&& src) = delete;
  29. FixedRingBuffer& operator = (FixedRingBuffer&&) = delete;
  30. size_t size() const
  31. {
  32. return m_write - m_read;
  33. }
  34. size_t capacity() const
  35. {
  36. return max_capacity;
  37. }
  38. bool empty() const
  39. {
  40. return m_write == m_read;
  41. }
  42. bool full() const
  43. {
  44. return size() == capacity();
  45. }
  46. void clear()
  47. {
  48. m_write = m_read = 0;
  49. }
  50. bool try_push(const T& value)
  51. {
  52. uint32_t target_idx;
  53. {
  54. SpinlockGuard guard(m_lock);
  55. if (full())
  56. return false;
  57. target_idx = m_write;
  58. m_buffer[index(target_idx)] = value;
  59. std::atomic_thread_fence(std::memory_order_release);
  60. m_write = target_idx + 1;
  61. }
  62. return true;
  63. }
  64. bool try_push(T&& value)
  65. {
  66. uint32_t target_idx;
  67. {
  68. SpinlockGuard guard(m_lock);
  69. if (full())
  70. return false;
  71. target_idx = m_write;
  72. m_buffer[index(target_idx)] = std::forward<T>(value);
  73. std::atomic_thread_fence(std::memory_order_release);
  74. m_write = target_idx + 1;
  75. }
  76. return true;
  77. }
  78. bool try_pop(T& value)
  79. {
  80. uint32_t target_idx;
  81. {
  82. SpinlockGuard guard(m_lock);
  83. if (empty())
  84. return false;
  85. target_idx = m_read;
  86. value = std::move(m_buffer[index(target_idx)]);
  87. m_read = target_idx + 1;
  88. assert(m_read <= m_write);
  89. }
  90. return true;
  91. }
  92. void push(const T& value)
  93. {
  94. while (!try_push(value))
  95. {
  96. std::this_thread::yield();
  97. }
  98. }
  99. void push(T& value)
  100. {
  101. while (!try_push(value))
  102. {
  103. std::this_thread::yield();
  104. }
  105. }
  106. T pop()
  107. {
  108. T result;
  109. while (!try_pop(result))
  110. {
  111. std::this_thread::yield();
  112. }
  113. return result;
  114. }
  115. };
  116. }