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