-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathLFQ.h
102 lines (82 loc) · 3.08 KB
/
LFQ.h
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
93
94
95
96
97
98
99
100
/**
* VDX7 - Virtual DX7 synthesizer emulation
* Copyright (C) 2023 [email protected]
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
**/
#pragma once
#include <atomic>
#include <cstddef>
//
// Lock-free queue, adapted from:
// https://www.codeproject.com/Articles/43510/Lock-Free-Single-Producer-Single-Consumer-Circular
//
template<typename Element, size_t Size>
class CircularFifo {
public:
enum { Capacity = Size+1 };
CircularFifo() : _tail(0), _head(0){}
virtual ~CircularFifo() {}
bool push(const Element& item); // pushByMove?
bool pop(Element& item);
bool wasEmpty() const;
bool wasFull() const;
bool isLockFree() const;
private:
size_t increment(size_t idx) const;
std::atomic <size_t> _tail; // tail(input) index
Element _array[Capacity];
std::atomic<size_t> _head; // head(output) index
};
template<typename Element, size_t Size>
bool CircularFifo<Element, Size>::push(const Element& item) {
const auto current_tail = _tail.load(std::memory_order_relaxed);
const auto next_tail = increment(current_tail);
if(next_tail != _head.load(std::memory_order_acquire)) {
_array[current_tail] = item;
_tail.store(next_tail, std::memory_order_release);
return true;
}
return false; // full queue
}
// Pop by Consumer can only update the head (load with relaxed, store with release)
// the tail must be accessed with at least acquire
template<typename Element, size_t Size>
bool CircularFifo<Element, Size>::pop(Element& item) {
const auto current_head = _head.load(std::memory_order_relaxed);
if(current_head == _tail.load(std::memory_order_acquire))
return false; // empty queue
item = _array[current_head];
_head.store(increment(current_head), std::memory_order_release);
return true;
}
template<typename Element, size_t Size>
bool CircularFifo<Element, Size>::wasEmpty() const {
// snapshot with acceptance of that this comparison operation is not atomic
return (_head.load() == _tail.load());
}
// snapshot with acceptance that this comparison is not atomic
template<typename Element, size_t Size>
bool CircularFifo<Element, Size>::wasFull() const {
const auto next_tail = increment(_tail.load()); // acquire, we dont know who call
return (next_tail == _head.load());
}
template<typename Element, size_t Size>
bool CircularFifo<Element, Size>::isLockFree() const {
return (_tail.is_lock_free() && _head.is_lock_free());
}
template<typename Element, size_t Size>
size_t CircularFifo<Element, Size>::increment(size_t idx) const {
return (idx + 1) % Capacity;
}