SDDC_Driver
Loading...
Searching...
No Matches
ringbuffer.h
1/*
2 * This file is part of SDDC_Driver.
3 *
4 * =====================
5 * MIT License
6 * =====================
7 *
8 * Copyright (C) 2021 - Howard Su
9 * Copyright (C) 2025 - RenardSpark
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
17
18 * The above copyright notice and this permission notice shall be included in all
19 * copies or substantial portions of the Software.
20
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 *
29 * SPDX-License-Identifier: MIT
30 */
31
32#pragma once
33
34#include <thread>
35#include <mutex>
36#include <condition_variable>
37#include <atomic>
38#include <array>
39#include <vector>
40
41#include "../config.h"
42
43namespace {
44 const int default_count = 64;
45 const int spin_count = 100;
46 #define ALIGN (8)
47};
48
49template<typename T, size_t max_count = default_count> class ringbuffer {
50 typedef T* TPtr;
51
52public:
53 ringbuffer():
54 read_index(0),
55 write_index(0),
56 blocks_available(0),
57 emptyCount(0),
58 fullCount(0),
59 writeCount(0),
60 stopped(false)
61 {
62 }
63
65 {
66 TracePrintln("ringbuffer", "");
67
68 Stop();
69 }
70
71 int getFullCount() const { return fullCount; }
72
73 int getEmptyCount() const { return emptyCount; }
74
75 int getWriteCount() const { return writeCount; }
76
77 void Start()
78 {
79 std::unique_lock<std::mutex> lk(mutex);
80 write_index = read_index = 0;
81 stopped = false;
82 }
83
84 void Stop()
85 {
86 std::unique_lock<std::mutex> lk(mutex);
87 read_index = 0;
88 stopped = true;
89 write_index = max_count / 2;
90 nonfullCV.notify_all();
91 nonemptyCV.notify_all();
92 }
93
94 void setBlockSize(int size)
95 {
96 TracePrintln("ringbuffer", "");
97
98 if (block_size != size)
99 {
100 block_size = size;
101
102 int aligned_block_size = (block_size + ALIGN - 1) & (~(ALIGN - 1));
103
104 DebugPrintln("ringbuffer", "New raw buffer size : %ld", max_count * aligned_block_size);
105
106 for(auto it = buffers.begin(); it < buffers.end(); it++)
107 {
108 it->resize(aligned_block_size);
109 }
110 }
111 }
112
113 T* peekWritePtr(int offset)
114 {
115 return buffers[(write_index.load() + max_count + offset) % max_count].data();
116 }
117
118 T* peekReadPtr(int offset)
119 {
120 return buffers[(read_index.load() + max_count + offset) % max_count].data();
121 }
122
123 void push(std::vector<T> arr)
124 {
125 WaitUntilNotFull();
126
127 std::unique_lock<std::mutex> lk(mutex);
128
129 buffers[write_index] = arr;
130
131 write_index = (write_index + 1) % max_count;
132 blocks_available++;
133
134 if (blocks_available == 1)
135 {
136 nonemptyCV.notify_all();
137 }
138
139 writeCount++;
140 }
141
142 std::vector<T> pop()
143 {
144 WaitUntilNotEmpty();
145
146 std::unique_lock<std::mutex> lk(mutex);
147
148 std::vector<T> vec = buffers[read_index];
149
150 read_index = (read_index + 1) % max_count;
151 blocks_available--;
152
153 if (blocks_available == max_count - 1)
154 {
155 nonfullCV.notify_all();
156 }
157
158 return vec;
159 }
160
161 int getBlockSize() const { return block_size; }
162
163 void WaitUntilNotEmpty()
164 {
165 if (stopped) return;
166
167 // if not empty
168 for (int i = 0; i < spin_count; i++)
169 {
170 if (blocks_available > 0)
171 return;
172 }
173
174 if(blocks_available <= 0)
175 {
176 std::unique_lock<std::mutex> lk(mutex);
177
178 emptyCount++;
179 nonemptyCV.wait(lk, [this] {
180 return blocks_available > 0;
181 });
182 }
183 }
184
185 void WaitUntilNotFull()
186 {
187 if (stopped) return;
188
189 for (int i = 0; i < spin_count; i++)
190 {
191 if (blocks_available < max_count)
192 return;
193 }
194
195 if (blocks_available >= max_count)
196 {
197 std::unique_lock<std::mutex> lk(mutex);
198 fullCount++;
199 nonfullCV.wait(lk, [this] {
200 return blocks_available < max_count;
201 });
202 }
203 }
204
205 volatile std::atomic<size_t> read_index;
206 volatile std::atomic<size_t> write_index;
207 volatile std::atomic<size_t> blocks_available;
208
209private:
210 int emptyCount;
211 int fullCount;
212 int writeCount;
213
214 std::mutex mutex;
215 bool stopped;
216 std::condition_variable nonemptyCV;
217 std::condition_variable nonfullCV;
218
219 int block_size = 0;
220
221 std::array<std::vector<T>, max_count> buffers;
222};
Definition ringbuffer.h:49