stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
ring.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2023 Amebis
4*/
5
6#pragma once
7
8#include "sal.hpp"
9#include <assert.h>
10#include <condition_variable>
11#include <mutex>
12#include <tuple>
13
14namespace stdex
15{
22 template <class T, size_t CAPACITY>
23 class ring
24 {
25 public:
26#pragma warning(suppress:26495) // Don't bother to initialize m_data
27 ring() :
28 m_head(0),
29 m_size(0),
30 m_quit(false)
31 {}
32
38 std::tuple<T*, size_t> back()
39 {
40 std::unique_lock<std::mutex> lk(m_mutex);
41 if (!space()) {
42 m_head_moved.wait(lk, [&]{return m_quit || space();});
43 if (m_quit) _Unlikely_
44 return { nullptr, 0 };
45 }
46 size_t tail = wrap(m_head + m_size);
47 return { &m_data[tail], m_head <= tail ? CAPACITY - tail : m_head - tail };
48 }
49
55 void push(_In_ size_t size)
56 {
57 {
58 const std::lock_guard<std::mutex> lg(m_mutex);
59#ifdef _DEBUG
60 size_t tail = wrap(m_head + m_size);
61 assert(size <= (m_head <= tail ? CAPACITY - tail : m_head - tail));
62#endif
63 m_size += size;
64 }
65 m_tail_moved.notify_one();
66 }
67
73 std::tuple<T*, size_t> front()
74 {
75 std::unique_lock<std::mutex> lk(m_mutex);
76 if (empty()) {
77 m_tail_moved.wait(lk, [&]{return m_quit || !empty();});
78 if (m_quit && empty()) _Unlikely_
79 return { nullptr, 0 };
80 }
81 size_t tail = wrap(m_head + m_size);
82 return { &m_data[m_head], m_head < tail ? m_size : CAPACITY - m_head };
83 }
84
90 void pop(_In_ size_t size)
91 {
92 {
93 const std::lock_guard<std::mutex> lg(m_mutex);
94#ifdef _DEBUG
95 size_t tail = wrap(m_head + m_size);
96 assert(size <= (m_head < tail ? m_size : CAPACITY - m_head));
97#endif
98 m_head = wrap(m_head + size);
99 m_size -= size;
100 }
101 m_head_moved.notify_one();
102 }
103
107 void quit()
108 {
109 {
110 const std::lock_guard<std::mutex> lg(m_mutex);
111 m_quit = true;
112 }
113 m_head_moved.notify_one();
114 m_tail_moved.notify_one();
115 }
116
120 void sync()
121 {
122 std::unique_lock<std::mutex> lk(m_mutex);
123 m_head_moved.wait(lk, [&]{return m_quit || empty();});
124 }
125
126 protected:
127 inline size_t wrap(_In_ size_t idx) const
128 {
129 // TODO: When CAPACITY is power of 2, use & ~(CAPACITY - 1) instead.
130 return idx % CAPACITY;
131 }
132
133 inline size_t space() const
134 {
135 return CAPACITY - m_size;
136 }
137
138 inline bool empty() const
139 {
140 return !m_size;
141 }
142
143 protected:
144 std::mutex m_mutex;
145 std::condition_variable m_head_moved, m_tail_moved;
146 size_t m_head, m_size;
147 bool m_quit;
148 T m_data[CAPACITY];
149 };
150}
Ring buffer.
Definition ring.hpp:24
void quit()
Cancells waiting sender and receiver.
Definition ring.hpp:107
std::tuple< T *, size_t > back()
Allocates the data after the ring tail. Use push() after the allocated data is populated.
Definition ring.hpp:38
void pop(size_t size)
Notifies the sender the data was consumed.
Definition ring.hpp:90
void sync()
Waits until the ring is flush.
Definition ring.hpp:120
std::tuple< T *, size_t > front()
Peeks the data at the ring head. Use pop() after the data was consumed.
Definition ring.hpp:73
void push(size_t size)
Notifies the receiver the data was populated.
Definition ring.hpp:55