blob: db1446fffbae519f819ecb55a8e1b19abb848d71 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
Austin Schuh3de38b02024-06-25 18:25:10 -07002// Copyright 2023 Google Inc. All rights reserved.
Austin Schuh70cc9552019-01-21 19:46:48 -08003// http://ceres-solver.org/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors may be
14// used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//
29// Author: vitus@google.com (Michael Vitus)
30
Austin Schuh3de38b02024-06-25 18:25:10 -070031#include "ceres/concurrent_queue.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080032
33#include <chrono>
34#include <thread>
35
Austin Schuh3de38b02024-06-25 18:25:10 -070036#include "ceres/internal/config.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080037#include "gmock/gmock.h"
38#include "gtest/gtest.h"
39
Austin Schuh3de38b02024-06-25 18:25:10 -070040namespace ceres::internal {
Austin Schuh70cc9552019-01-21 19:46:48 -080041
42// A basic test of push and pop.
43TEST(ConcurrentQueue, PushPop) {
44 ConcurrentQueue<int> queue;
45
46 const int num_to_add = 10;
47 for (int i = 0; i < num_to_add; ++i) {
48 queue.Push(i);
49 }
50
51 for (int i = 0; i < num_to_add; ++i) {
52 int value;
53 ASSERT_TRUE(queue.Pop(&value));
54 EXPECT_EQ(i, value);
55 }
56}
57
58// Push and pop elements from the queue after StopWaiters has been called.
59TEST(ConcurrentQueue, PushPopAfterStopWaiters) {
60 ConcurrentQueue<int> queue;
61
62 const int num_to_add = 10;
63 int value;
64
65 // Pop should return immediately with false with an empty queue.
66 ASSERT_FALSE(queue.Pop(&value));
67
68 for (int i = 0; i < num_to_add; ++i) {
69 queue.Push(i);
70 }
71
72 // Call stop waiters to ensure we can still Push and Pop from the queue.
73 queue.StopWaiters();
74
75 for (int i = 0; i < num_to_add; ++i) {
76 ASSERT_TRUE(queue.Pop(&value));
77 EXPECT_EQ(i, value);
78 }
79
80 // Pop should return immediately with false with an empty queue.
81 ASSERT_FALSE(queue.Pop(&value));
82
83 // Ensure we can still push onto the queue after StopWaiters has been called.
84 const int offset = 123;
85 for (int i = 0; i < num_to_add; ++i) {
86 queue.Push(i + offset);
87 }
88
89 for (int i = 0; i < num_to_add; ++i) {
90 int value;
91 ASSERT_TRUE(queue.Pop(&value));
92 EXPECT_EQ(i + offset, value);
93 }
94
95 // Pop should return immediately with false with an empty queue.
96 ASSERT_FALSE(queue.Pop(&value));
97
98 // Try calling StopWaiters again to ensure nothing changes.
99 queue.StopWaiters();
100
101 queue.Push(13456);
102 ASSERT_TRUE(queue.Pop(&value));
103 EXPECT_EQ(13456, value);
104}
105
106// Push and pop elements after StopWaiters and EnableWaiters has been called.
107TEST(ConcurrentQueue, PushPopStopAndStart) {
108 ConcurrentQueue<int> queue;
109
110 int value;
111
112 queue.Push(13456);
113 queue.Push(256);
114
115 queue.StopWaiters();
116
117 ASSERT_TRUE(queue.Pop(&value));
118 EXPECT_EQ(13456, value);
119
120 queue.EnableWaiters();
121
122 // Try adding another entry after enable has been called.
123 queue.Push(989);
124
125 // Ensure we can pop both elements off.
126 ASSERT_TRUE(queue.Pop(&value));
127 EXPECT_EQ(256, value);
128
129 ASSERT_TRUE(queue.Pop(&value));
130 EXPECT_EQ(989, value);
131
132 // Re-enable waiting.
133 queue.EnableWaiters();
134
135 // Pop should return immediately with false with an empty queue.
136 ASSERT_FALSE(queue.Pop(&value));
137}
138
139// A basic test for Wait.
140TEST(ConcurrentQueue, Wait) {
141 ConcurrentQueue<int> queue;
142
143 int value;
144
145 queue.Push(13456);
146
147 ASSERT_TRUE(queue.Wait(&value));
148 EXPECT_EQ(13456, value);
149
150 queue.StopWaiters();
151
152 // Ensure waiting returns immediately after StopWaiters.
153 EXPECT_FALSE(queue.Wait(&value));
154 EXPECT_FALSE(queue.Wait(&value));
155
156 EXPECT_FALSE(queue.Pop(&value));
157
158 // Calling StopWaiters multiple times does not change anything.
159 queue.StopWaiters();
160
161 EXPECT_FALSE(queue.Wait(&value));
162 EXPECT_FALSE(queue.Wait(&value));
163
164 queue.Push(989);
165 queue.Push(789);
166
167 ASSERT_TRUE(queue.Wait(&value));
168 EXPECT_EQ(989, value);
169
170 ASSERT_TRUE(queue.Wait(&value));
171 EXPECT_EQ(789, value);
172}
173
174// Ensure wait blocks until an element is pushed. Also ensure wait does not
175// block after StopWaiters is called and there is no value in the queue.
176// Finally, ensures EnableWaiters re-enables waiting.
177TEST(ConcurrentQueue, EnsureWaitBlocks) {
178 ConcurrentQueue<int> queue;
179
180 int value = 0;
181 bool valid_value = false;
182 bool waiting = false;
183 std::mutex mutex;
184
185 std::thread thread([&]() {
186 {
187 std::lock_guard<std::mutex> lock(mutex);
188 waiting = true;
189 }
190
191 int element = 87987;
192 bool valid = queue.Wait(&element);
193
194 {
195 std::lock_guard<std::mutex> lock(mutex);
196 waiting = false;
197 value = element;
198 valid_value = valid;
199 }
200 });
201
202 // Give the thread time to start and wait.
203 std::this_thread::sleep_for(std::chrono::milliseconds(500));
204
205 // Ensure nothing is has been popped off the queue
206 {
207 std::lock_guard<std::mutex> lock(mutex);
208 EXPECT_TRUE(waiting);
209 ASSERT_FALSE(valid_value);
210 ASSERT_EQ(0, value);
211 }
212
213 queue.Push(13456);
214
215 // Wait for the thread to pop the value.
216 thread.join();
217
218 EXPECT_TRUE(valid_value);
219 EXPECT_EQ(13456, value);
220}
221
222TEST(ConcurrentQueue, StopAndEnableWaiters) {
223 ConcurrentQueue<int> queue;
224
225 int value = 0;
226 bool valid_value = false;
227 bool waiting = false;
228 std::mutex mutex;
229
230 auto task = [&]() {
231 {
232 std::lock_guard<std::mutex> lock(mutex);
233 waiting = true;
234 }
235
236 int element = 87987;
237 bool valid = queue.Wait(&element);
238
239 {
240 std::lock_guard<std::mutex> lock(mutex);
241 waiting = false;
242 value = element;
243 valid_value = valid;
244 }
245 };
246
247 std::thread thread_1(task);
248
249 // Give the thread time to start and wait.
250 std::this_thread::sleep_for(std::chrono::milliseconds(500));
251
252 // Ensure the thread is waiting.
253 {
254 std::lock_guard<std::mutex> lock(mutex);
255 EXPECT_TRUE(waiting);
256 }
257
258 // Unblock the thread.
259 queue.StopWaiters();
260
261 thread_1.join();
262
263 // Ensure nothing has been popped off the queue.
264 EXPECT_FALSE(valid_value);
265 EXPECT_EQ(87987, value);
266
267 // Ensure another call to Wait returns immediately.
268 EXPECT_FALSE(queue.Wait(&value));
269
270 queue.EnableWaiters();
271
272 value = 0;
273 valid_value = false;
274 waiting = false;
275
276 // Start another task waiting for an element to be pushed.
277 std::thread thread_2(task);
278
279 // Give the thread time to start and wait.
280 std::this_thread::sleep_for(std::chrono::milliseconds(500));
281
282 // Ensure nothing is popped off the queue.
283 {
284 std::lock_guard<std::mutex> lock(mutex);
285 EXPECT_TRUE(waiting);
286 ASSERT_FALSE(valid_value);
287 ASSERT_EQ(0, value);
288 }
289
290 queue.Push(13456);
291
292 // Wait for the thread to pop the value.
293 thread_2.join();
294
295 EXPECT_TRUE(valid_value);
296 EXPECT_EQ(13456, value);
297}
298
Austin Schuh3de38b02024-06-25 18:25:10 -0700299} // namespace ceres::internal