CppCommon  1.0.4.1
C++ Common Library
spsc_ring_buffer.inl
Go to the documentation of this file.
1 
9 namespace CppCommon {
10 
11 inline SPSCRingBuffer::SPSCRingBuffer(size_t capacity) : _capacity(capacity), _mask(capacity - 1), _buffer(new uint8_t[capacity]), _head(0), _tail(0)
12 {
13  assert((capacity > 1) && "Ring buffer capacity must be greater than one!");
14  assert(((capacity & (capacity - 1)) == 0) && "Ring buffer capacity must be a power of two!");
15 
16  memset(_pad0, 0, sizeof(cache_line_pad));
17  memset(_pad1, 0, sizeof(cache_line_pad));
18  memset(_pad2, 0, sizeof(cache_line_pad));
19  memset(_pad3, 0, sizeof(cache_line_pad));
20 }
21 
22 inline size_t SPSCRingBuffer::size() const noexcept
23 {
24  const size_t head = _head.load(std::memory_order_acquire);
25  const size_t tail = _tail.load(std::memory_order_acquire);
26 
27  return head - tail;
28 }
29 
30 inline bool SPSCRingBuffer::Enqueue(const void* data, size_t size)
31 {
32  assert((size <= _capacity) && "Data size should not be greater than ring buffer capacity!");
33  if (size > _capacity)
34  return false;
35 
36  if (size == 0)
37  return true;
38 
39  assert((data != nullptr) && "Pointer to the data should not be null!");
40  if (data == nullptr)
41  return false;
42 
43  const size_t head = _head.load(std::memory_order_relaxed);
44  const size_t tail = _tail.load(std::memory_order_acquire);
45 
46  // Check if there is required free space in the ring buffer
47  if ((size + head - tail) > _capacity)
48  return false;
49 
50  // Copy data into the ring buffer
51  size_t head_index = head & _mask;
52  size_t tail_index = tail & _mask;
53  size_t remain = (tail_index > head_index) ? (tail_index - head_index) : (_capacity - head_index);
54  size_t first = (size > remain) ? remain : size;
55  size_t last = (size > remain) ? size - remain : 0;
56  memcpy(&_buffer[head_index], (uint8_t*)data, first);
57  memcpy(_buffer, (uint8_t*)data + first, last);
58 
59  // Increase the head cursor
60  _head.store(head + size, std::memory_order_release);
61 
62  return true;
63 }
64 
65 inline bool SPSCRingBuffer::Dequeue(void* data, size_t& size)
66 {
67  if (size == 0)
68  return true;
69 
70  assert((data != nullptr) && "Pointer to the data should not be null!");
71  if (data == nullptr)
72  return false;
73 
74  const size_t tail = _tail.load(std::memory_order_relaxed);
75  const size_t head = _head.load(std::memory_order_acquire);
76 
77  // Get the ring buffer size
78  size_t available = head - tail;
79  if (size > available)
80  size = available;
81 
82  // Check if the ring buffer is empty
83  if (size == 0)
84  return false;
85 
86  // Copy data from the ring buffer
87  size_t head_index = head & _mask;
88  size_t tail_index = tail & _mask;
89  size_t remain = (head_index > tail_index) ? (head_index - tail_index) : (_capacity - tail_index);
90  size_t first = (size > remain) ? remain : size;
91  size_t last = (size > remain) ? size - remain : 0;
92  memcpy((uint8_t*)data, &_buffer[tail_index], first);
93  memcpy((uint8_t*)data + first, _buffer, last);
94 
95  // Increase the tail cursor
96  _tail.store(tail + size, std::memory_order_release);
97 
98  return true;
99 }
100 
101 } // namespace CppCommon
bool Enqueue(const void *data, size_t size)
Enqueue a data into the ring buffer (single producer thread method)
size_t size() const noexcept
Get ring buffer size in bytes.
bool Dequeue(void *data, size_t &size)
Dequeue a data from the ring buffer (single consumer thread method)
size_t capacity() const noexcept
Get ring buffer capacity in bytes.
SPSCRingBuffer(size_t capacity)
Default class constructor.
C++ Common project definitions.
Definition: token_bucket.h:15