test_ring_buffer.cpp (6010B)
1 /* 2 * Copyright © 2016 Mozilla Foundation 3 * 4 * This program is made available under an ISC-style license. See the 5 * accompanying file LICENSE for details. 6 */ 7 8 #ifndef NOMINMAX 9 #define NOMINMAX 10 #endif 11 12 #include "cubeb_ringbuffer.h" 13 #include "gtest/gtest.h" 14 #include <chrono> 15 #include <iostream> 16 #include <thread> 17 18 /* Generate a monotonically increasing sequence of numbers. */ 19 template <typename T> class sequence_generator { 20 public: 21 sequence_generator(size_t channels) : channels(channels) {} 22 void get(T * elements, size_t frames) 23 { 24 for (size_t i = 0; i < frames; i++) { 25 for (size_t c = 0; c < channels; c++) { 26 elements[i * channels + c] = static_cast<T>(index_); 27 } 28 index_++; 29 } 30 } 31 void rewind(size_t frames) { index_ -= frames; } 32 33 private: 34 size_t index_ = 0; 35 size_t channels = 0; 36 }; 37 38 /* Checks that a sequence is monotonically increasing. */ 39 template <typename T> class sequence_verifier { 40 public: 41 sequence_verifier(size_t channels) : channels(channels) {} 42 void check(T * elements, size_t frames) 43 { 44 for (size_t i = 0; i < frames; i++) { 45 for (size_t c = 0; c < channels; c++) { 46 if (elements[i * channels + c] != static_cast<T>(index_)) { 47 std::cerr << "Element " << i << " is different. Expected " 48 << static_cast<T>(index_) << ", got " << elements[i] 49 << ". (channel count: " << channels << ")." << std::endl; 50 ASSERT_TRUE(false); 51 } 52 } 53 index_++; 54 } 55 } 56 57 private: 58 size_t index_ = 0; 59 size_t channels = 0; 60 }; 61 62 template <typename T> 63 void 64 test_ring(lock_free_audio_ring_buffer<T> & buf, int channels, 65 int capacity_frames) 66 { 67 std::unique_ptr<T[]> seq(new T[capacity_frames * channels]); 68 sequence_generator<T> gen(channels); 69 sequence_verifier<T> checker(channels); 70 71 int iterations = 1002; 72 73 const int block_size = 128; 74 75 while (iterations--) { 76 gen.get(seq.get(), block_size); 77 int rv = buf.enqueue(seq.get(), block_size); 78 ASSERT_EQ(rv, block_size); 79 PodZero(seq.get(), block_size); 80 rv = buf.dequeue(seq.get(), block_size); 81 ASSERT_EQ(rv, block_size); 82 checker.check(seq.get(), block_size); 83 } 84 } 85 86 template <typename T> 87 void 88 test_ring_multi(lock_free_audio_ring_buffer<T> & buf, int channels, 89 int capacity_frames) 90 { 91 sequence_verifier<T> checker(channels); 92 std::unique_ptr<T[]> out_buffer(new T[capacity_frames * channels]); 93 94 const int block_size = 128; 95 96 std::thread t([=, &buf] { 97 int iterations = 1002; 98 std::unique_ptr<T[]> in_buffer(new T[capacity_frames * channels]); 99 sequence_generator<T> gen(channels); 100 101 while (iterations--) { 102 std::this_thread::yield(); 103 gen.get(in_buffer.get(), block_size); 104 int rv = buf.enqueue(in_buffer.get(), block_size); 105 ASSERT_TRUE(rv <= block_size); 106 if (rv != block_size) { 107 gen.rewind(block_size - rv); 108 } 109 } 110 }); 111 112 int remaining = 1002; 113 114 while (remaining--) { 115 std::this_thread::yield(); 116 int rv = buf.dequeue(out_buffer.get(), block_size); 117 ASSERT_TRUE(rv <= block_size); 118 checker.check(out_buffer.get(), rv); 119 } 120 121 t.join(); 122 } 123 124 template <typename T> 125 void 126 basic_api_test(T & ring) 127 { 128 ASSERT_EQ(ring.capacity(), 128); 129 130 ASSERT_EQ(ring.available_read(), 0); 131 ASSERT_EQ(ring.available_write(), 128); 132 133 int rv = ring.enqueue_default(63); 134 135 ASSERT_TRUE(rv == 63); 136 ASSERT_EQ(ring.available_read(), 63); 137 ASSERT_EQ(ring.available_write(), 65); 138 139 rv = ring.enqueue_default(65); 140 141 ASSERT_EQ(rv, 65); 142 ASSERT_EQ(ring.available_read(), 128); 143 ASSERT_EQ(ring.available_write(), 0); 144 145 rv = ring.dequeue(nullptr, 63); 146 147 ASSERT_EQ(ring.available_read(), 65); 148 ASSERT_EQ(ring.available_write(), 63); 149 150 rv = ring.dequeue(nullptr, 65); 151 152 ASSERT_EQ(ring.available_read(), 0); 153 ASSERT_EQ(ring.available_write(), 128); 154 } 155 156 void 157 test_reset_api() 158 { 159 const size_t ring_buffer_size = 128; 160 const size_t enqueue_size = ring_buffer_size / 2; 161 162 lock_free_queue<float> ring(ring_buffer_size); 163 std::thread t([=, &ring] { 164 std::unique_ptr<float[]> in_buffer(new float[enqueue_size]); 165 ring.enqueue(in_buffer.get(), enqueue_size); 166 }); 167 168 t.join(); 169 170 ring.reset_thread_ids(); 171 172 // Enqueue with a different thread. We have reset the thread ID 173 // in the ring buffer, this should work. 174 std::thread t2([=, &ring] { 175 std::unique_ptr<float[]> in_buffer(new float[enqueue_size]); 176 ring.enqueue(in_buffer.get(), enqueue_size); 177 }); 178 179 t2.join(); 180 181 ASSERT_TRUE(true); 182 } 183 184 TEST(cubeb, ring_buffer) 185 { 186 /* Basic API test. */ 187 const int min_channels = 1; 188 const int max_channels = 10; 189 const int min_capacity = 199; 190 const int max_capacity = 1277; 191 const int capacity_increment = 27; 192 193 lock_free_queue<float> q1(128); 194 basic_api_test(q1); 195 lock_free_queue<short> q2(128); 196 basic_api_test(q2); 197 198 for (size_t channels = min_channels; channels < max_channels; channels++) { 199 lock_free_audio_ring_buffer<float> q3(channels, 128); 200 basic_api_test(q3); 201 lock_free_audio_ring_buffer<short> q4(channels, 128); 202 basic_api_test(q4); 203 } 204 205 /* Single thread testing. */ 206 /* Test mono to 9.1 */ 207 for (size_t channels = min_channels; channels < max_channels; channels++) { 208 /* Use non power-of-two numbers to catch edge-cases. */ 209 for (size_t capacity_frames = min_capacity; capacity_frames < max_capacity; 210 capacity_frames += capacity_increment) { 211 lock_free_audio_ring_buffer<float> ring(channels, capacity_frames); 212 test_ring(ring, channels, capacity_frames); 213 } 214 } 215 216 /* Multi thread testing */ 217 for (size_t channels = min_channels; channels < max_channels; channels++) { 218 /* Use non power-of-two numbers to catch edge-cases. */ 219 for (size_t capacity_frames = min_capacity; capacity_frames < max_capacity; 220 capacity_frames += capacity_increment) { 221 lock_free_audio_ring_buffer<short> ring(channels, capacity_frames); 222 test_ring_multi(ring, channels, capacity_frames); 223 } 224 } 225 226 test_reset_api(); 227 } 228 229 #undef NOMINMAX