jaulib v1.3.0
Jau Support Library (C++, Java, ..)
test_lfringbuffer11.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#include <cassert>
25#include <cinttypes>
26#include <cstring>
27#include <memory>
28#include <thread>
29#include <pthread.h>
30
32
33#include <jau/ringbuffer.hpp>
34
35using namespace jau;
36using namespace jau::fractions_i64_literals;
37
38typedef uint8_t IntegralType;
39typedef uint8_t TrivialType;
40constexpr const TrivialType TrivialTypeNullElem(0xff);
42
43constexpr static const IntegralType integral_modulus = 254;
44
45// Test examples.
47 private:
48
49 TrivialTypeRingbuffer createEmpty(jau::nsize_t initialCapacity) {
50 TrivialTypeRingbuffer rb(initialCapacity);
51 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
52 return rb;
53 }
54 TrivialTypeRingbuffer createFull(const std::vector<TrivialType> & source) {
55 TrivialTypeRingbuffer rb(source);
56 REQUIRE_MSG("full "+rb.toString(), rb.isFull());
57 return rb;
58 }
59
60 std::vector<TrivialType> createIntArray(const jau::nsize_t capacity, const IntegralType startValue) {
61 std::vector<TrivialType> array(capacity);
62 for(jau::nsize_t i=0; i<capacity; i++) {
63 array[i] = TrivialType( ( startValue + (IntegralType)i ) % integral_modulus );
64 }
65 return array;
66 }
67
68 void getThreadType01(const std::string& msg, TrivialTypeRingbuffer* rb, jau::nsize_t len) {
69 // std::thread::id this_id = std::this_thread::get_id();
70 // pthread_t this_id = pthread_self();
71
72 // INFO_STR, INFO: Not thread safe yet
73 // INFO_STR(msg+": Created / " + rb->toString());
74 for(jau::nsize_t i=0; i<len; i++) {
75 TrivialType svI = 0;
76 REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString()+", elem "+std::to_string(svI), rb->getBlocking(svI, fractions_i64::zero));
77 // INFO_STR("Got "+std::to_string(svI->intValue())+" / " + rb->toString());
78 }
79 // INFO_STR(msg+": Dies / " + rb->toString());
80 (void)msg;
81 }
82
83 void getRangeThreadType02(const std::string& msg, TrivialTypeRingbuffer* rb, jau::nsize_t len) {
84 // std::thread::id this_id = std::this_thread::get_id();
85 // pthread_t this_id = pthread_self();
86
87 // INFO_STR, INFO: Not thread safe yet
88 // INFO_STR(msg+": Created / " + rb->toString());
89 std::vector<TrivialType> array(len);
90 REQUIRE_MSG("get-range of "+std::to_string(array.size())+" elem in "+rb->toString(), len==rb->getBlocking( &(*array.begin()), len, len, 0_s) );
91
92 for(jau::nsize_t i=0; i<len; i++) {
93 TrivialType svI = array[i];
94 REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString()+", elem "+std::to_string(svI), svI!=TrivialTypeNullElem);
95 // INFO_STR("Got "+std::to_string(svI.intValue())+" / " + rb->toString());
96 }
97 // INFO_STR(msg+": Dies / " + rb->toString());
98 (void)msg;
99 }
100
101 void putThreadType01(const std::string& msg, TrivialTypeRingbuffer* rb, jau::nsize_t len, IntegralType startValue) {
102 // std::thread::id this_id = std::this_thread::get_id();
103 // pthread_t this_id = pthread_self();
104
105 // INFO_STR(msg+": Created / " + rb->toString());
106 for(jau::nsize_t i=0; i<len; i++) {
107 IntegralType vI( ( startValue + (IntegralType)i ) % integral_modulus );
108 // INFO_STR("Putting "+std::to_string(vI)+" ... / " + rb->toString());
109 REQUIRE( true == rb->putBlocking( vI, 0_s ) );
110 }
111 // INFO_STR(msg+": Dies / " + rb->toString());
112 (void)msg;
113 }
114
115 void putRangeThreadType02(const std::string& msg, TrivialTypeRingbuffer* rb, jau::nsize_t len, IntegralType startValue) {
116 // std::thread::id this_id = std::this_thread::get_id();
117 // pthread_t this_id = pthread_self();
118
119 // INFO_STR(msg+": Created / " + rb->toString());
120 std::vector<TrivialType> data = createIntArray(len, startValue);
121 REQUIRE_MSG("put-range of "+std::to_string(data.size())+" elem in "+rb->toString(), rb->put( &(*data.begin()), &(*data.end()) ) );
122
123 // INFO_STR(msg+": Dies / " + rb->toString());
124 (void)msg;
125 }
126
127 public:
128
130 INFO_STR("\n\ntest01a_Read1Write1\n");
131 jau::nsize_t capacity = 100;
132 TrivialTypeRingbuffer rb = createEmpty(capacity);
133
134 fprintf(stderr, "test01a_Read1Write1: %s\n", rb.get_info().c_str());
135 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
136 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
137
138 std::thread getThread01(&TestRingbuffer11::getThreadType01, this, "test01a.get01", &rb, capacity); // @suppress("Invalid arguments")
139 std::thread putThread01(&TestRingbuffer11::putThreadType01, this, "test01a.put01", &rb, capacity, 0); // @suppress("Invalid arguments")
140 putThread01.join();
141 getThread01.join();
142
143 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
144 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
145 }
146
148 INFO_STR("\n\ntest01b_Read1Write1_Range\n");
149 jau::nsize_t capacity = 100;
150 TrivialTypeRingbuffer rb = createEmpty(capacity);
151
152 fprintf(stderr, "test01b_Read1Write1_Range: %s\n", rb.get_info().c_str());
153 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
154 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
155
156 std::thread getThread01(&TestRingbuffer11::getRangeThreadType02, this, "test01b.getR01", &rb, capacity); // @suppress("Invalid arguments")
157 std::thread putThread01(&TestRingbuffer11::putRangeThreadType02, this, "test01b.putR01", &rb, capacity, 0); // @suppress("Invalid arguments")
158 putThread01.join();
159 getThread01.join();
160
161 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
162 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
163 }
164
166 INFO_STR("\n\ntest02a_Read4Write1\n");
167 jau::nsize_t capacity = 400;
168 TrivialTypeRingbuffer rb = createEmpty(capacity);
169 rb.setMultiPCEnabled(true);
170
171 fprintf(stderr, "test02a_Read4Write1: %s\n", rb.get_info().c_str());
172 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
173 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
174
175 std::thread getThread01(&TestRingbuffer11::getThreadType01, this, "test02a.get01", &rb, capacity/4); // @suppress("Invalid arguments")
176 std::thread getThread02(&TestRingbuffer11::getThreadType01, this, "test02a.get02", &rb, capacity/4); // @suppress("Invalid arguments")
177 std::thread putThread01(&TestRingbuffer11::putThreadType01, this, "test02a.put01", &rb, capacity, 0); // @suppress("Invalid arguments")
178 std::thread getThread03(&TestRingbuffer11::getThreadType01, this, "test02a.get03", &rb, capacity/4); // @suppress("Invalid arguments")
179 std::thread getThread04(&TestRingbuffer11::getThreadType01, this, "test02a.get04", &rb, capacity/4); // @suppress("Invalid arguments")
180 putThread01.join();
181 getThread01.join();
182 getThread02.join();
183 getThread03.join();
184 getThread04.join();
185
186 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
187 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
188 }
189
191 INFO_STR("\n\ntest02b_Read4Write1_Range\n");
192 jau::nsize_t capacity = 400;
193 TrivialTypeRingbuffer rb = createEmpty(capacity);
194 rb.setMultiPCEnabled(true);
195
196 fprintf(stderr, "test02b_Read4Write1_Range: %s\n", rb.get_info().c_str());
197 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
198 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
199
200 std::thread getThread01(&TestRingbuffer11::getRangeThreadType02, this, "test02b.getR01", &rb, capacity/4); // @suppress("Invalid arguments")
201 std::thread getThread02(&TestRingbuffer11::getRangeThreadType02, this, "test02b.getR02", &rb, capacity/4); // @suppress("Invalid arguments")
202 std::thread putThread01(&TestRingbuffer11::putRangeThreadType02, this, "test02b.putR01", &rb, capacity, 0); // @suppress("Invalid arguments")
203 std::thread getThread03(&TestRingbuffer11::getRangeThreadType02, this, "test02b.getR03", &rb, capacity/4); // @suppress("Invalid arguments")
204 std::thread getThread04(&TestRingbuffer11::getRangeThreadType02, this, "test02b.getR04", &rb, capacity/4); // @suppress("Invalid arguments")
205 putThread01.join();
206 getThread01.join();
207 getThread02.join();
208 getThread03.join();
209 getThread04.join();
210
211 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
212 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
213 }
214
216 INFO_STR("\n\ntest03a_Read8Write2\n");
217 jau::nsize_t capacity = 800;
218 TrivialTypeRingbuffer rb = createEmpty(capacity);
219 rb.setMultiPCEnabled(true);
220
221 fprintf(stderr, "test03a_Read8Write2: %s\n", rb.get_info().c_str());
222 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
223 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
224
225 std::thread getThread01(&TestRingbuffer11::getThreadType01, this, "test03a.get01", &rb, capacity/8); // @suppress("Invalid arguments")
226 std::thread getThread02(&TestRingbuffer11::getThreadType01, this, "test03a.get02", &rb, capacity/8); // @suppress("Invalid arguments")
227 std::thread putThread01(&TestRingbuffer11::putThreadType01, this, "test03a.put01", &rb, capacity/2, 0); // @suppress("Invalid arguments")
228 std::thread getThread03(&TestRingbuffer11::getThreadType01, this, "test03a.get03", &rb, capacity/8); // @suppress("Invalid arguments")
229 std::thread getThread04(&TestRingbuffer11::getThreadType01, this, "test03a.get04", &rb, capacity/8); // @suppress("Invalid arguments")
230
231 std::thread getThread05(&TestRingbuffer11::getThreadType01, this, "test03a.get05", &rb, capacity/8); // @suppress("Invalid arguments")
232 std::thread getThread06(&TestRingbuffer11::getThreadType01, this, "test03a.get06", &rb, capacity/8); // @suppress("Invalid arguments")
233 std::thread putThread02(&TestRingbuffer11::putThreadType01, this, "test03a.put02", &rb, capacity/2, 400); // @suppress("Invalid arguments")
234 std::thread getThread07(&TestRingbuffer11::getThreadType01, this, "test03a.get07", &rb, capacity/8); // @suppress("Invalid arguments")
235 std::thread getThread08(&TestRingbuffer11::getThreadType01, this, "test03a.get08", &rb, capacity/8); // @suppress("Invalid arguments")
236
237 putThread01.join();
238 putThread02.join();
239 getThread01.join();
240 getThread02.join();
241 getThread03.join();
242 getThread04.join();
243 getThread05.join();
244 getThread06.join();
245 getThread07.join();
246 getThread08.join();
247
248 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
249 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
250 }
251
253 INFO_STR("\n\ntest03b_Read8Write2_Range\n");
254 jau::nsize_t capacity = 800;
255 TrivialTypeRingbuffer rb = createEmpty(capacity);
256 rb.setMultiPCEnabled(true);
257
258 fprintf(stderr, "test03b_Read8Write2_Range: %s\n", rb.get_info().c_str());
259 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
260 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
261
262 std::thread getThread01(&TestRingbuffer11::getRangeThreadType02, this, "test03b.getR01", &rb, capacity/8); // @suppress("Invalid arguments")
263 std::thread getThread02(&TestRingbuffer11::getRangeThreadType02, this, "test03b.getR02", &rb, capacity/8); // @suppress("Invalid arguments")
264 std::thread putThread01(&TestRingbuffer11::putRangeThreadType02, this, "test03b.putR01", &rb, capacity/2, 0); // @suppress("Invalid arguments")
265 std::thread getThread03(&TestRingbuffer11::getRangeThreadType02, this, "test03b.getR03", &rb, capacity/8); // @suppress("Invalid arguments")
266 std::thread getThread04(&TestRingbuffer11::getRangeThreadType02, this, "test03b.getR04", &rb, capacity/8); // @suppress("Invalid arguments")
267
268 std::thread getThread05(&TestRingbuffer11::getRangeThreadType02, this, "test03b.getR05", &rb, capacity/8); // @suppress("Invalid arguments")
269 std::thread getThread06(&TestRingbuffer11::getRangeThreadType02, this, "test03b.getR06", &rb, capacity/8); // @suppress("Invalid arguments")
270 std::thread putThread02(&TestRingbuffer11::putRangeThreadType02, this, "test03b.putR02", &rb, capacity/2, 400); // @suppress("Invalid arguments")
271 std::thread getThread07(&TestRingbuffer11::getRangeThreadType02, this, "test03b.getR07", &rb, capacity/8); // @suppress("Invalid arguments")
272 std::thread getThread08(&TestRingbuffer11::getRangeThreadType02, this, "test03b.getR08", &rb, capacity/8); // @suppress("Invalid arguments")
273
274 putThread01.join();
275 putThread02.join();
276 getThread01.join();
277 getThread02.join();
278 getThread03.join();
279 getThread04.join();
280 getThread05.join();
281 getThread06.join();
282 getThread07.join();
283 getThread08.join();
284
285 REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty());
286 REQUIRE_MSG("empty size "+rb.toString(), 0 == rb.size());
287 }
288
293
297
301 }
302
303 void test_range() {
307
311
315 }
316};
317
318METHOD_AS_TEST_CASE( TestRingbuffer11::test_sequential, "Test TestRingbuffer 11- test_sequential");
319METHOD_AS_TEST_CASE( TestRingbuffer11::test_range, "Test TestRingbuffer 11- test_range");
320
#define INFO_STR(msg)
Definition: catch2_ext.hpp:75
#define REQUIRE_MSG(MSG,...)
Definition: catch2_ext.hpp:58
Ring buffer implementation, a.k.a circular buffer, exposing lock-free get*(..) and put*(....
Definition: ringbuffer.hpp:182
bool putBlocking(Value_type &&e, const fraction_i64 &timeout, bool &timeout_occurred) noexcept
Enqueues the given element by moving it into this ringbuffer storage.
bool put(Value_type &&e) noexcept
Enqueues the given element by moving it into this ringbuffer storage.
Size_type size() const noexcept
Returns the number of elements in this ring buffer.
bool getBlocking(Value_type &result, const fraction_i64 &timeout, bool &timeout_occurred) noexcept
Dequeues the oldest enqueued element.
constexpr_non_literal_var void setMultiPCEnabled(const bool v)
Enable or disable capability to handle multiple producer and consumer, see ringbuffer_multi_pc and ri...
bool isFull() const noexcept
Returns true if this ring buffer is full, otherwise false.
bool isEmpty() const noexcept
Returns true if this ring buffer is empty, otherwise false.
std::string toString() const noexcept
Returns a short string representation incl.
std::string get_info() const noexcept
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
constexpr const jau::fraction_i64 zero(0l, 1lu)
zero is 0/1
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
static constexpr const IntegralType integral_modulus
uint8_t TrivialType
constexpr const TrivialType TrivialTypeNullElem(0xff)
ringbuffer< TrivialType, jau::nsize_t > TrivialTypeRingbuffer
METHOD_AS_TEST_CASE(TestRingbuffer11::test_sequential, "Test TestRingbuffer 11- test_sequential")
uint8_t IntegralType