-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRingBuffer_v2.hpp
More file actions
92 lines (76 loc) · 2.38 KB
/
RingBuffer_v2.hpp
File metadata and controls
92 lines (76 loc) · 2.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#pragma once
#include <atomic>
#include <memory>
#include "defs.hpp"
#include <iostream>
namespace v2 {
template <typename T>
struct Block
{
alignas(cache_line_size) std::atomic<u32> version;
alignas(cache_line_size) T data;
};
template <typename T>
class RingBuffer
{
alignas(cache_line_size) u64 _blockIdx{0};
alignas(cache_line_size) std::unique_ptr<Block<T>[]> _blocks;
size_t _capacity;
public:
auto advance(u64 idx) -> u64 {
return (idx == _capacity - 1) ? 0 : (idx + 1);
}
RingBuffer(size_t size)
: _capacity(size)
{
_blocks = std::make_unique<Block<T>[]>(size);
for (size_t i = 0; i < size; i++)
{
_blocks[i].version.store(0, std::memory_order_relaxed);
}
}
auto getIdx() const -> u64 {
return _blockIdx;
}
void write(T item) {
/* If it is the first write, just write the data, and after the write is complete,
* increment the version to 1. The odd version means a read is allowed.
* If it is a rewrite, then the version is already odd, so before writing,
* increment the version so it is even (no reads allowed). Perform the write.
* Once writing is done, increment the version again to make it odd (reading is allowed).
*/
auto blockIdx = advance(_blockIdx);
auto & block = _blocks[blockIdx];
auto version = block.version.load(std::memory_order_acquire);
// if ((version & 2) - 1 != 0) {
if (version % 2) { // TODO: use bitwse comparison
version++; // make even
block.version.store(version, std::memory_order_release);
}
block.data = item;
// store the new odd version
version++;
block.version.store(version, std::memory_order_release);
// upgrade the counter
_blockIdx = blockIdx;
}
bool read(u64 blockIdx, T &item) const {
auto & block = _blocks[blockIdx];
auto version = block.version.load(std::memory_order_acquire);
// read only when version is odd
if (version % 2 == 0) { // TODO: use bitwse comparison
// if ((version & 2) - 1 == 0 ) { // TODO: use bitwse comparison
return false;
}
// read the data
item = block.data;
// indicate that the block is read but don't change evenness
// TODO: That might not be necessary
// block.version.store(version + 2, std::memory_order_release);
return true;
}
size_t size() const {
return _capacity;
}
};
} // end namespace v2