| // Ceres Solver - A fast non-linear least squares minimizer |
| // Copyright 2018 Google Inc. All rights reserved. |
| // http://ceres-solver.org/ |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // * Neither the name of Google Inc. nor the names of its contributors may be |
| // used to endorse or promote products derived from this software without |
| // specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| // POSSIBILITY OF SUCH DAMAGE. |
| // |
| // Author: vitus@google.com (Michael Vitus) |
| |
| // This include must come before any #ifndef check on Ceres compile options. |
| #include "ceres/internal/port.h" |
| |
| #ifdef CERES_USE_CXX_THREADS |
| |
| #include <chrono> |
| #include <thread> |
| |
| #include "ceres/concurrent_queue.h" |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| namespace ceres { |
| namespace internal { |
| |
| // A basic test of push and pop. |
| TEST(ConcurrentQueue, PushPop) { |
| ConcurrentQueue<int> queue; |
| |
| const int num_to_add = 10; |
| for (int i = 0; i < num_to_add; ++i) { |
| queue.Push(i); |
| } |
| |
| for (int i = 0; i < num_to_add; ++i) { |
| int value; |
| ASSERT_TRUE(queue.Pop(&value)); |
| EXPECT_EQ(i, value); |
| } |
| } |
| |
| // Push and pop elements from the queue after StopWaiters has been called. |
| TEST(ConcurrentQueue, PushPopAfterStopWaiters) { |
| ConcurrentQueue<int> queue; |
| |
| const int num_to_add = 10; |
| int value; |
| |
| // Pop should return immediately with false with an empty queue. |
| ASSERT_FALSE(queue.Pop(&value)); |
| |
| for (int i = 0; i < num_to_add; ++i) { |
| queue.Push(i); |
| } |
| |
| // Call stop waiters to ensure we can still Push and Pop from the queue. |
| queue.StopWaiters(); |
| |
| for (int i = 0; i < num_to_add; ++i) { |
| ASSERT_TRUE(queue.Pop(&value)); |
| EXPECT_EQ(i, value); |
| } |
| |
| // Pop should return immediately with false with an empty queue. |
| ASSERT_FALSE(queue.Pop(&value)); |
| |
| // Ensure we can still push onto the queue after StopWaiters has been called. |
| const int offset = 123; |
| for (int i = 0; i < num_to_add; ++i) { |
| queue.Push(i + offset); |
| } |
| |
| for (int i = 0; i < num_to_add; ++i) { |
| int value; |
| ASSERT_TRUE(queue.Pop(&value)); |
| EXPECT_EQ(i + offset, value); |
| } |
| |
| // Pop should return immediately with false with an empty queue. |
| ASSERT_FALSE(queue.Pop(&value)); |
| |
| // Try calling StopWaiters again to ensure nothing changes. |
| queue.StopWaiters(); |
| |
| queue.Push(13456); |
| ASSERT_TRUE(queue.Pop(&value)); |
| EXPECT_EQ(13456, value); |
| } |
| |
| // Push and pop elements after StopWaiters and EnableWaiters has been called. |
| TEST(ConcurrentQueue, PushPopStopAndStart) { |
| ConcurrentQueue<int> queue; |
| |
| int value; |
| |
| queue.Push(13456); |
| queue.Push(256); |
| |
| queue.StopWaiters(); |
| |
| ASSERT_TRUE(queue.Pop(&value)); |
| EXPECT_EQ(13456, value); |
| |
| queue.EnableWaiters(); |
| |
| // Try adding another entry after enable has been called. |
| queue.Push(989); |
| |
| // Ensure we can pop both elements off. |
| ASSERT_TRUE(queue.Pop(&value)); |
| EXPECT_EQ(256, value); |
| |
| ASSERT_TRUE(queue.Pop(&value)); |
| EXPECT_EQ(989, value); |
| |
| // Re-enable waiting. |
| queue.EnableWaiters(); |
| |
| // Pop should return immediately with false with an empty queue. |
| ASSERT_FALSE(queue.Pop(&value)); |
| } |
| |
| // A basic test for Wait. |
| TEST(ConcurrentQueue, Wait) { |
| ConcurrentQueue<int> queue; |
| |
| int value; |
| |
| queue.Push(13456); |
| |
| ASSERT_TRUE(queue.Wait(&value)); |
| EXPECT_EQ(13456, value); |
| |
| queue.StopWaiters(); |
| |
| // Ensure waiting returns immediately after StopWaiters. |
| EXPECT_FALSE(queue.Wait(&value)); |
| EXPECT_FALSE(queue.Wait(&value)); |
| |
| EXPECT_FALSE(queue.Pop(&value)); |
| |
| // Calling StopWaiters multiple times does not change anything. |
| queue.StopWaiters(); |
| |
| EXPECT_FALSE(queue.Wait(&value)); |
| EXPECT_FALSE(queue.Wait(&value)); |
| |
| queue.Push(989); |
| queue.Push(789); |
| |
| ASSERT_TRUE(queue.Wait(&value)); |
| EXPECT_EQ(989, value); |
| |
| ASSERT_TRUE(queue.Wait(&value)); |
| EXPECT_EQ(789, value); |
| } |
| |
| // Ensure wait blocks until an element is pushed. Also ensure wait does not |
| // block after StopWaiters is called and there is no value in the queue. |
| // Finally, ensures EnableWaiters re-enables waiting. |
| TEST(ConcurrentQueue, EnsureWaitBlocks) { |
| ConcurrentQueue<int> queue; |
| |
| int value = 0; |
| bool valid_value = false; |
| bool waiting = false; |
| std::mutex mutex; |
| |
| std::thread thread([&]() { |
| { |
| std::lock_guard<std::mutex> lock(mutex); |
| waiting = true; |
| } |
| |
| int element = 87987; |
| bool valid = queue.Wait(&element); |
| |
| { |
| std::lock_guard<std::mutex> lock(mutex); |
| waiting = false; |
| value = element; |
| valid_value = valid; |
| } |
| }); |
| |
| // Give the thread time to start and wait. |
| std::this_thread::sleep_for(std::chrono::milliseconds(500)); |
| |
| // Ensure nothing is has been popped off the queue |
| { |
| std::lock_guard<std::mutex> lock(mutex); |
| EXPECT_TRUE(waiting); |
| ASSERT_FALSE(valid_value); |
| ASSERT_EQ(0, value); |
| } |
| |
| queue.Push(13456); |
| |
| // Wait for the thread to pop the value. |
| thread.join(); |
| |
| EXPECT_TRUE(valid_value); |
| EXPECT_EQ(13456, value); |
| } |
| |
| TEST(ConcurrentQueue, StopAndEnableWaiters) { |
| ConcurrentQueue<int> queue; |
| |
| int value = 0; |
| bool valid_value = false; |
| bool waiting = false; |
| std::mutex mutex; |
| |
| auto task = [&]() { |
| { |
| std::lock_guard<std::mutex> lock(mutex); |
| waiting = true; |
| } |
| |
| int element = 87987; |
| bool valid = queue.Wait(&element); |
| |
| { |
| std::lock_guard<std::mutex> lock(mutex); |
| waiting = false; |
| value = element; |
| valid_value = valid; |
| } |
| }; |
| |
| std::thread thread_1(task); |
| |
| // Give the thread time to start and wait. |
| std::this_thread::sleep_for(std::chrono::milliseconds(500)); |
| |
| // Ensure the thread is waiting. |
| { |
| std::lock_guard<std::mutex> lock(mutex); |
| EXPECT_TRUE(waiting); |
| } |
| |
| // Unblock the thread. |
| queue.StopWaiters(); |
| |
| thread_1.join(); |
| |
| // Ensure nothing has been popped off the queue. |
| EXPECT_FALSE(valid_value); |
| EXPECT_EQ(87987, value); |
| |
| // Ensure another call to Wait returns immediately. |
| EXPECT_FALSE(queue.Wait(&value)); |
| |
| queue.EnableWaiters(); |
| |
| value = 0; |
| valid_value = false; |
| waiting = false; |
| |
| // Start another task waiting for an element to be pushed. |
| std::thread thread_2(task); |
| |
| // Give the thread time to start and wait. |
| std::this_thread::sleep_for(std::chrono::milliseconds(500)); |
| |
| // Ensure nothing is popped off the queue. |
| { |
| std::lock_guard<std::mutex> lock(mutex); |
| EXPECT_TRUE(waiting); |
| ASSERT_FALSE(valid_value); |
| ASSERT_EQ(0, value); |
| } |
| |
| queue.Push(13456); |
| |
| // Wait for the thread to pop the value. |
| thread_2.join(); |
| |
| EXPECT_TRUE(valid_value); |
| EXPECT_EQ(13456, value); |
| } |
| |
| } // namespace internal |
| } // namespace ceres |
| |
| #endif // CERES_USE_CXX_THREADS |