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 "assert.hpp"
9#include "compat.hpp"
10#include <condition_variable>
11#include <mutex>
12#include <tuple>
13
14#if defined(__GNUC__)
15#pragma GCC diagnostic push
16#pragma GCC diagnostic ignored "-Wunknown-pragmas"
17#endif
18
19 namespace stdex
20{
27 template <class T, size_t N_cap>
28 class ring
29 {
30 public:
31#pragma warning(suppress:26495) // Don't bother to initialize m_data
32 ring() :
33 m_head(0),
34 m_size(0),
35 m_quit(false)
36 {}
37
43 std::tuple<T*, size_t> back()
44 {
45 std::unique_lock<std::mutex> lk(m_mutex);
46 if (!space()) {
47 m_head_moved.wait(lk, [&]{return m_quit || space();});
48 if (m_quit) _Unlikely_
49 return { nullptr, 0 };
50 }
51 size_t tail = wrap(m_head + m_size);
52 return { &m_data[tail], m_head <= tail ? N_cap - tail : m_head - tail };
53 }
54
60 void push(_In_ size_t size)
61 {
62 {
63 const std::lock_guard<std::mutex> lg(m_mutex);
64#ifndef NDEBUG
65 size_t tail = wrap(m_head + m_size);
66 stdex_assert(size <= (m_head <= tail ? N_cap - tail : m_head - tail));
67#endif
68 m_size += size;
69 }
70 m_tail_moved.notify_one();
71 }
72
78 std::tuple<T*, size_t> front()
79 {
80 std::unique_lock<std::mutex> lk(m_mutex);
81 if (empty()) {
82 m_tail_moved.wait(lk, [&]{return m_quit || !empty();});
83 if (m_quit && empty()) _Unlikely_
84 return { nullptr, 0 };
85 }
86 size_t tail = wrap(m_head + m_size);
87 return { &m_data[m_head], m_head < tail ? m_size : N_cap - m_head };
88 }
89
95 void pop(_In_ size_t size)
96 {
97 {
98 const std::lock_guard<std::mutex> lg(m_mutex);
99#ifndef NDEBUG
100 size_t tail = wrap(m_head + m_size);
101 stdex_assert(size <= (m_head < tail ? m_size : N_cap - m_head));
102#endif
103 m_head = wrap(m_head + size);
104 m_size -= size;
105 }
106 m_head_moved.notify_one();
107 }
108
112 void quit()
113 {
114 {
115 const std::lock_guard<std::mutex> lg(m_mutex);
116 m_quit = true;
117 }
118 m_head_moved.notify_one();
119 m_tail_moved.notify_one();
120 }
121
125 void sync()
126 {
127 std::unique_lock<std::mutex> lk(m_mutex);
128 m_head_moved.wait(lk, [&]{return m_quit || empty();});
129 }
130
131 protected:
132 size_t wrap(_In_ size_t idx) const
133 {
134 // TODO: When N_cap is power of 2, use & ~(N_cap - 1) instead.
135 return idx % N_cap;
136 }
137
138 size_t space() const
139 {
140 return N_cap - m_size;
141 }
142
143 bool empty() const
144 {
145 return !m_size;
146 }
147
148 protected:
149 std::mutex m_mutex;
150 std::condition_variable m_head_moved, m_tail_moved;
151 size_t m_head, m_size;
152 bool m_quit;
153 T m_data[N_cap];
154 };
155}
156
157#if defined(__GNUC__)
158#pragma GCC diagnostic pop
159#endif
stdex::ring
Ring buffer.
Definition ring.hpp:29
stdex::ring::pop
void pop(size_t size)
Notifies the sender the data was consumed.
Definition ring.hpp:95
stdex::ring::back
std::tuple< T *, size_t > back()
Allocates the data after the ring tail. Use push() after the allocated data is populated.
Definition ring.hpp:43
stdex::ring::quit
void quit()
Cancells waiting sender and receiver.
Definition ring.hpp:112
stdex::ring::front
std::tuple< T *, size_t > front()
Peeks the data at the ring head. Use pop() after the data was consumed.
Definition ring.hpp:78
stdex::ring::push
void push(size_t size)
Notifies the receiver the data was populated.
Definition ring.hpp:60
stdex::ring::sync
void sync()
Waits until the ring is flush.
Definition ring.hpp:125