1 /*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <thread>
18 #include <iostream>
19
20 #include <gtest/gtest.h>
21 #include "utils/LockFreeQueue.h"
22
23 /**
24 * Tests
25 * =====
26 *
27 * READ/POP:
28 * - if queue is empty return false
29 * - if queue has elements return true
30 * - returns correct value once
31 * - returns correct value more than once
32 *
33 * WRITE/PUSH:
34 * - if queue is full return false
35 * - if queue has space return true
36 *
37 * WRAP:
38 * - read from full queue, then write one more element, then correct read
39 *
40 * PEEK:
41 * - peek once
42 * - peek twice
43 *
44 * SIZE:
45 * - correct zero
46 * - correct once
47 * - correct many
48 *
49 * MULTI-THREADED:
50 * - write and read concurrently, queue is empty at end and item order is maintained
51 *
52 * CANNOT TEST:
53 * - read/writes correct after wraparound (takes too long to run)
54 * - Constructing queue with non power of 2 capacity results in assert failure (needs death test)
55 * - Constructing queue with signed type results in assert failure (needs death test)
56 *
57 */
58
59 constexpr int kQueueCapacity = 2;
60
61 class TestLockFreeQueue : public ::testing::Test {
62
63 public:
64
65 int result1;
66 int result2;
67 int result3;
68 LockFreeQueue<int, kQueueCapacity> q;
69 };
70
TEST_F(TestLockFreeQueue,PopReturnsFalseWhenEmpty)71 TEST_F(TestLockFreeQueue, PopReturnsFalseWhenEmpty){
72 bool result = q.pop(result1);
73 ASSERT_EQ(result, false);
74 }
75
TEST_F(TestLockFreeQueue,PopReturnsTrueWhenNotEmpty)76 TEST_F(TestLockFreeQueue, PopReturnsTrueWhenNotEmpty){
77 q.push(123);
78 bool result = q.pop(result1);
79 ASSERT_EQ(result, true);
80 }
81
TEST_F(TestLockFreeQueue,PopReturnsOneCorrectValue)82 TEST_F(TestLockFreeQueue, PopReturnsOneCorrectValue){
83 q.push(123);
84 q.pop(result1);
85 ASSERT_EQ(result1, 123);
86 }
87
TEST_F(TestLockFreeQueue,PopReturnsManyCorrectValues)88 TEST_F(TestLockFreeQueue, PopReturnsManyCorrectValues){
89 q.push(123);
90 q.push(456);
91 q.pop(result1);
92 q.pop(result2);
93 ASSERT_EQ(result1, 123);
94 ASSERT_EQ(result2, 456);
95 }
96
TEST_F(TestLockFreeQueue,PushWhenFullReturnsFalse)97 TEST_F(TestLockFreeQueue, PushWhenFullReturnsFalse){
98 q.push(123);
99 q.push(123);
100 ASSERT_EQ(q.push(123), false);
101 }
102
TEST_F(TestLockFreeQueue,PushWhenSpaceAvailableReturnsTrue)103 TEST_F(TestLockFreeQueue, PushWhenSpaceAvailableReturnsTrue){
104 ASSERT_EQ(q.push(123), true);
105 }
106
TEST_F(TestLockFreeQueue,PushHandlesWrapCorrectly)107 TEST_F(TestLockFreeQueue, PushHandlesWrapCorrectly){
108 q.push(123);
109 q.push(234);
110 q.pop(result1);
111 q.push(999);
112 q.pop(result2);
113 ASSERT_EQ(result2, 234);
114 q.pop(result3);
115 ASSERT_EQ(result3, 999);
116 }
117
TEST_F(TestLockFreeQueue,PeekWhenEmptyReturnsFalse)118 TEST_F(TestLockFreeQueue, PeekWhenEmptyReturnsFalse){
119 ASSERT_EQ(q.peek(result1), false);
120 }
121
TEST_F(TestLockFreeQueue,PeekWhenNotEmptyReturnsTrue)122 TEST_F(TestLockFreeQueue, PeekWhenNotEmptyReturnsTrue){
123 q.push(123);
124 ASSERT_EQ(q.peek(result1), true);
125 }
126
TEST_F(TestLockFreeQueue,PeekReturnsCorrectValueOnce)127 TEST_F(TestLockFreeQueue, PeekReturnsCorrectValueOnce){
128 q.push(456);
129 q.peek(result1);
130 ASSERT_EQ(result1, 456);
131 }
132
TEST_F(TestLockFreeQueue,PeekReturnsCorrectValueTwice)133 TEST_F(TestLockFreeQueue, PeekReturnsCorrectValueTwice){
134 q.push(456);
135 q.peek(result1);
136 q.peek(result2);
137 ASSERT_EQ(result2, 456);
138 }
139
TEST_F(TestLockFreeQueue,SizeReturnsZeroAfterInit)140 TEST_F(TestLockFreeQueue, SizeReturnsZeroAfterInit){
141 ASSERT_EQ(q.size(), 0);
142 }
143
TEST_F(TestLockFreeQueue,SizeIsOneAfterPushOnce)144 TEST_F(TestLockFreeQueue, SizeIsOneAfterPushOnce){
145 q.push(321);
146 ASSERT_EQ(q.size(), 1);
147 }
148
TEST_F(TestLockFreeQueue,SizeIsCorrectAfterPushingMaxItems)149 TEST_F(TestLockFreeQueue, SizeIsCorrectAfterPushingMaxItems){
150 for (int i = 0; i < kQueueCapacity; ++i) { q.push(i); }
151 ASSERT_EQ(q.size(), kQueueCapacity);
152 }
153
TEST_F(TestLockFreeQueue,SizeCorrectAfterWriteCounterWraps)154 TEST_F(TestLockFreeQueue, SizeCorrectAfterWriteCounterWraps){
155
156 const uint32_t kCapacity = (uint32_t) 1 << 7;
157 LockFreeQueue<int, kCapacity, uint8_t> myQ;
158
159 for (int i = 0; i < UINT8_MAX; ++i) {
160 myQ.push(i);
161 myQ.pop(result1);
162 }
163
164 myQ.push(1);
165 ASSERT_EQ(myQ.size(), 1);
166 }
167
TEST_F(TestLockFreeQueue,MultiThreadedTest)168 TEST_F(TestLockFreeQueue, MultiThreadedTest){
169
170 // Push and pop from the queue concurrently
171 // Check that the queue is empty
172 // Check that the order of the read values matches the order of the written values
173
174 constexpr int kQueueCapacity = 2048;
175 LockFreeQueue<int, kQueueCapacity> myQ;
176 std::array<int, kQueueCapacity> sourceData;
177 std::array<int, kQueueCapacity> targetData { 0 };
178
179 // Populate the source data
180 for (int i = 0; i < kQueueCapacity; i++){
181 sourceData[i] = i;
182 }
183
184 std::thread writer([&myQ, &sourceData](){
185 for (int i = 0; i < kQueueCapacity; i++){
186 myQ.push(sourceData[i]);
187 }
188 });
189
190 std::thread reader([&myQ, &targetData](){
191 for (int i = 0; i < kQueueCapacity; i++){
192 // pop the latest element, or wait for it to become available
193 while (!myQ.pop(targetData[i])){
194 std::cout << "Waiting for element at index " << i << " to be written" << std::endl;
195 usleep(500);
196 }
197 }
198 });
199
200 writer.join();
201 reader.join();
202
203 int v;
204 ASSERT_FALSE(myQ.pop(v));
205
206 // Check that the target matches the source
207 for (int i = 0; i < kQueueCapacity; i++){
208 ASSERT_EQ(sourceData[i], targetData[i]) << "Elements at index " << i << " did not match";
209 }
210 }
211