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