blob: 430111a6d8ff29d16007f1a1e64a91b9be2c2093 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2018 Google Inc. All rights reserved.
3// 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
31// This include must come before any #ifndef check on Ceres compile options.
32#include "ceres/internal/port.h"
33
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080034#ifdef CERES_USE_CXX_THREADS
Austin Schuh70cc9552019-01-21 19:46:48 -080035
36#include <chrono>
37#include <thread>
38
39#include "ceres/concurrent_queue.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080040#include "gmock/gmock.h"
41#include "gtest/gtest.h"
42
43namespace ceres {
44namespace internal {
45
46// A basic test of push and pop.
47TEST(ConcurrentQueue, PushPop) {
48 ConcurrentQueue<int> queue;
49
50 const int num_to_add = 10;
51 for (int i = 0; i < num_to_add; ++i) {
52 queue.Push(i);
53 }
54
55 for (int i = 0; i < num_to_add; ++i) {
56 int value;
57 ASSERT_TRUE(queue.Pop(&value));
58 EXPECT_EQ(i, value);
59 }
60}
61
62// Push and pop elements from the queue after StopWaiters has been called.
63TEST(ConcurrentQueue, PushPopAfterStopWaiters) {
64 ConcurrentQueue<int> queue;
65
66 const int num_to_add = 10;
67 int value;
68
69 // Pop should return immediately with false with an empty queue.
70 ASSERT_FALSE(queue.Pop(&value));
71
72 for (int i = 0; i < num_to_add; ++i) {
73 queue.Push(i);
74 }
75
76 // Call stop waiters to ensure we can still Push and Pop from the queue.
77 queue.StopWaiters();
78
79 for (int i = 0; i < num_to_add; ++i) {
80 ASSERT_TRUE(queue.Pop(&value));
81 EXPECT_EQ(i, value);
82 }
83
84 // Pop should return immediately with false with an empty queue.
85 ASSERT_FALSE(queue.Pop(&value));
86
87 // Ensure we can still push onto the queue after StopWaiters has been called.
88 const int offset = 123;
89 for (int i = 0; i < num_to_add; ++i) {
90 queue.Push(i + offset);
91 }
92
93 for (int i = 0; i < num_to_add; ++i) {
94 int value;
95 ASSERT_TRUE(queue.Pop(&value));
96 EXPECT_EQ(i + offset, value);
97 }
98
99 // Pop should return immediately with false with an empty queue.
100 ASSERT_FALSE(queue.Pop(&value));
101
102 // Try calling StopWaiters again to ensure nothing changes.
103 queue.StopWaiters();
104
105 queue.Push(13456);
106 ASSERT_TRUE(queue.Pop(&value));
107 EXPECT_EQ(13456, value);
108}
109
110// Push and pop elements after StopWaiters and EnableWaiters has been called.
111TEST(ConcurrentQueue, PushPopStopAndStart) {
112 ConcurrentQueue<int> queue;
113
114 int value;
115
116 queue.Push(13456);
117 queue.Push(256);
118
119 queue.StopWaiters();
120
121 ASSERT_TRUE(queue.Pop(&value));
122 EXPECT_EQ(13456, value);
123
124 queue.EnableWaiters();
125
126 // Try adding another entry after enable has been called.
127 queue.Push(989);
128
129 // Ensure we can pop both elements off.
130 ASSERT_TRUE(queue.Pop(&value));
131 EXPECT_EQ(256, value);
132
133 ASSERT_TRUE(queue.Pop(&value));
134 EXPECT_EQ(989, value);
135
136 // Re-enable waiting.
137 queue.EnableWaiters();
138
139 // Pop should return immediately with false with an empty queue.
140 ASSERT_FALSE(queue.Pop(&value));
141}
142
143// A basic test for Wait.
144TEST(ConcurrentQueue, Wait) {
145 ConcurrentQueue<int> queue;
146
147 int value;
148
149 queue.Push(13456);
150
151 ASSERT_TRUE(queue.Wait(&value));
152 EXPECT_EQ(13456, value);
153
154 queue.StopWaiters();
155
156 // Ensure waiting returns immediately after StopWaiters.
157 EXPECT_FALSE(queue.Wait(&value));
158 EXPECT_FALSE(queue.Wait(&value));
159
160 EXPECT_FALSE(queue.Pop(&value));
161
162 // Calling StopWaiters multiple times does not change anything.
163 queue.StopWaiters();
164
165 EXPECT_FALSE(queue.Wait(&value));
166 EXPECT_FALSE(queue.Wait(&value));
167
168 queue.Push(989);
169 queue.Push(789);
170
171 ASSERT_TRUE(queue.Wait(&value));
172 EXPECT_EQ(989, value);
173
174 ASSERT_TRUE(queue.Wait(&value));
175 EXPECT_EQ(789, value);
176}
177
178// Ensure wait blocks until an element is pushed. Also ensure wait does not
179// block after StopWaiters is called and there is no value in the queue.
180// Finally, ensures EnableWaiters re-enables waiting.
181TEST(ConcurrentQueue, EnsureWaitBlocks) {
182 ConcurrentQueue<int> queue;
183
184 int value = 0;
185 bool valid_value = false;
186 bool waiting = false;
187 std::mutex mutex;
188
189 std::thread thread([&]() {
190 {
191 std::lock_guard<std::mutex> lock(mutex);
192 waiting = true;
193 }
194
195 int element = 87987;
196 bool valid = queue.Wait(&element);
197
198 {
199 std::lock_guard<std::mutex> lock(mutex);
200 waiting = false;
201 value = element;
202 valid_value = valid;
203 }
204 });
205
206 // Give the thread time to start and wait.
207 std::this_thread::sleep_for(std::chrono::milliseconds(500));
208
209 // Ensure nothing is has been popped off the queue
210 {
211 std::lock_guard<std::mutex> lock(mutex);
212 EXPECT_TRUE(waiting);
213 ASSERT_FALSE(valid_value);
214 ASSERT_EQ(0, value);
215 }
216
217 queue.Push(13456);
218
219 // Wait for the thread to pop the value.
220 thread.join();
221
222 EXPECT_TRUE(valid_value);
223 EXPECT_EQ(13456, value);
224}
225
226TEST(ConcurrentQueue, StopAndEnableWaiters) {
227 ConcurrentQueue<int> queue;
228
229 int value = 0;
230 bool valid_value = false;
231 bool waiting = false;
232 std::mutex mutex;
233
234 auto task = [&]() {
235 {
236 std::lock_guard<std::mutex> lock(mutex);
237 waiting = true;
238 }
239
240 int element = 87987;
241 bool valid = queue.Wait(&element);
242
243 {
244 std::lock_guard<std::mutex> lock(mutex);
245 waiting = false;
246 value = element;
247 valid_value = valid;
248 }
249 };
250
251 std::thread thread_1(task);
252
253 // Give the thread time to start and wait.
254 std::this_thread::sleep_for(std::chrono::milliseconds(500));
255
256 // Ensure the thread is waiting.
257 {
258 std::lock_guard<std::mutex> lock(mutex);
259 EXPECT_TRUE(waiting);
260 }
261
262 // Unblock the thread.
263 queue.StopWaiters();
264
265 thread_1.join();
266
267 // Ensure nothing has been popped off the queue.
268 EXPECT_FALSE(valid_value);
269 EXPECT_EQ(87987, value);
270
271 // Ensure another call to Wait returns immediately.
272 EXPECT_FALSE(queue.Wait(&value));
273
274 queue.EnableWaiters();
275
276 value = 0;
277 valid_value = false;
278 waiting = false;
279
280 // Start another task waiting for an element to be pushed.
281 std::thread thread_2(task);
282
283 // Give the thread time to start and wait.
284 std::this_thread::sleep_for(std::chrono::milliseconds(500));
285
286 // Ensure nothing is popped off the queue.
287 {
288 std::lock_guard<std::mutex> lock(mutex);
289 EXPECT_TRUE(waiting);
290 ASSERT_FALSE(valid_value);
291 ASSERT_EQ(0, value);
292 }
293
294 queue.Push(13456);
295
296 // Wait for the thread to pop the value.
297 thread_2.join();
298
299 EXPECT_TRUE(valid_value);
300 EXPECT_EQ(13456, value);
301}
302
303} // namespace internal
304} // namespace ceres
305
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800306#endif // CERES_USE_CXX_THREADS