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 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "cubeb_ringbuffer.h"
#include "gtest/gtest.h"
#include <chrono>
#include <iostream>
#include <thread>
/* Generate a monotonically increasing sequence of numbers. */
template <typename T> class sequence_generator {
public:
sequence_generator(size_t channels) : channels(channels) {}
void get(T * elements, size_t frames)
{
for (size_t i = 0; i < frames; i++) {
for (size_t c = 0; c < channels; c++) {
elements[i * channels + c] = static_cast<T>(index_);
}
index_++;
}
}
void rewind(size_t frames) { index_ -= frames; }
private:
size_t index_ = 0;
size_t channels = 0;
};
/* Checks that a sequence is monotonically increasing. */
template <typename T> class sequence_verifier {
public:
sequence_verifier(size_t channels) : channels(channels) {}
void check(T * elements, size_t frames)
{
for (size_t i = 0; i < frames; i++) {
for (size_t c = 0; c < channels; c++) {
if (elements[i * channels + c] != static_cast<T>(index_)) {
std::cerr << "Element " << i << " is different. Expected "
<< static_cast<T>(index_) << ", got " << elements[i]
<< ". (channel count: " << channels << ")." << std::endl;
ASSERT_TRUE(false);
}
}
index_++;
}
}
private:
size_t index_ = 0;
size_t channels = 0;
};
template <typename T>
void
test_ring(lock_free_audio_ring_buffer<T> & buf, int channels,
int capacity_frames)
{
std::unique_ptr<T[]> seq(new T[capacity_frames * channels]);
sequence_generator<T> gen(channels);
sequence_verifier<T> checker(channels);
int iterations = 1002;
const int block_size = 128;
while (iterations--) {
gen.get(seq.get(), block_size);
int rv = buf.enqueue(seq.get(), block_size);
ASSERT_EQ(rv, block_size);
PodZero(seq.get(), block_size);
rv = buf.dequeue(seq.get(), block_size);
ASSERT_EQ(rv, block_size);
checker.check(seq.get(), block_size);
}
}
template <typename T>
void
test_ring_multi(lock_free_audio_ring_buffer<T> & buf, int channels,
int capacity_frames)
{
sequence_verifier<T> checker(channels);
std::unique_ptr<T[]> out_buffer(new T[capacity_frames * channels]);
const int block_size = 128;
std::thread t([=, &buf] {
int iterations = 1002;
std::unique_ptr<T[]> in_buffer(new T[capacity_frames * channels]);
sequence_generator<T> gen(channels);
while (iterations--) {
std::this_thread::yield();
gen.get(in_buffer.get(), block_size);
int rv = buf.enqueue(in_buffer.get(), block_size);
ASSERT_TRUE(rv <= block_size);
if (rv != block_size) {
gen.rewind(block_size - rv);
}
}
});
int remaining = 1002;
while (remaining--) {
std::this_thread::yield();
int rv = buf.dequeue(out_buffer.get(), block_size);
ASSERT_TRUE(rv <= block_size);
checker.check(out_buffer.get(), rv);
}
t.join();
}
template <typename T>
void
basic_api_test(T & ring)
{
ASSERT_EQ(ring.capacity(), 128);
ASSERT_EQ(ring.available_read(), 0);
ASSERT_EQ(ring.available_write(), 128);
int rv = ring.enqueue_default(63);
ASSERT_TRUE(rv == 63);
ASSERT_EQ(ring.available_read(), 63);
ASSERT_EQ(ring.available_write(), 65);
rv = ring.enqueue_default(65);
ASSERT_EQ(rv, 65);
ASSERT_EQ(ring.available_read(), 128);
ASSERT_EQ(ring.available_write(), 0);
rv = ring.dequeue(nullptr, 63);
ASSERT_EQ(ring.available_read(), 65);
ASSERT_EQ(ring.available_write(), 63);
rv = ring.dequeue(nullptr, 65);
ASSERT_EQ(ring.available_read(), 0);
ASSERT_EQ(ring.available_write(), 128);
}
void
test_reset_api()
{
const size_t ring_buffer_size = 128;
const size_t enqueue_size = ring_buffer_size / 2;
lock_free_queue<float> ring(ring_buffer_size);
std::thread t([=, &ring] {
std::unique_ptr<float[]> in_buffer(new float[enqueue_size]);
ring.enqueue(in_buffer.get(), enqueue_size);
});
t.join();
ring.reset_thread_ids();
// Enqueue with a different thread. We have reset the thread ID
// in the ring buffer, this should work.
std::thread t2([=, &ring] {
std::unique_ptr<float[]> in_buffer(new float[enqueue_size]);
ring.enqueue(in_buffer.get(), enqueue_size);
});
t2.join();
ASSERT_TRUE(true);
}
TEST(cubeb, ring_buffer)
{
/* Basic API test. */
const int min_channels = 1;
const int max_channels = 10;
const int min_capacity = 199;
const int max_capacity = 1277;
const int capacity_increment = 27;
lock_free_queue<float> q1(128);
basic_api_test(q1);
lock_free_queue<short> q2(128);
basic_api_test(q2);
for (size_t channels = min_channels; channels < max_channels; channels++) {
lock_free_audio_ring_buffer<float> q3(channels, 128);
basic_api_test(q3);
lock_free_audio_ring_buffer<short> q4(channels, 128);
basic_api_test(q4);
}
/* Single thread testing. */
/* Test mono to 9.1 */
for (size_t channels = min_channels; channels < max_channels; channels++) {
/* Use non power-of-two numbers to catch edge-cases. */
for (size_t capacity_frames = min_capacity; capacity_frames < max_capacity;
capacity_frames += capacity_increment) {
lock_free_audio_ring_buffer<float> ring(channels, capacity_frames);
test_ring(ring, channels, capacity_frames);
}
}
/* Multi thread testing */
for (size_t channels = min_channels; channels < max_channels; channels++) {
/* Use non power-of-two numbers to catch edge-cases. */
for (size_t capacity_frames = min_capacity; capacity_frames < max_capacity;
capacity_frames += capacity_increment) {
lock_free_audio_ring_buffer<short> ring(channels, capacity_frames);
test_ring_multi(ring, channels, capacity_frames);
}
}
test_reset_api();
}
#undef NOMINMAX
|