123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- // Ceres Solver - A fast non-linear least squares minimizer
- // Copyright 2023 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)
- #include "ceres/thread_pool.h"
- #include <chrono>
- #include <condition_variable>
- #include <mutex>
- #include <thread>
- #include "ceres/internal/config.h"
- #include "glog/logging.h"
- #include "gmock/gmock.h"
- #include "gtest/gtest.h"
- namespace ceres::internal {
- // Adds a number of tasks to the thread pool and ensures they all run.
- TEST(ThreadPool, AddTask) {
- int value = 0;
- const int num_tasks = 100;
- {
- ThreadPool thread_pool(2);
- std::condition_variable condition;
- std::mutex mutex;
- for (int i = 0; i < num_tasks; ++i) {
- thread_pool.AddTask([&]() {
- std::lock_guard<std::mutex> lock(mutex);
- ++value;
- condition.notify_all();
- });
- }
- std::unique_lock<std::mutex> lock(mutex);
- condition.wait(lock, [&]() { return value == num_tasks; });
- }
- EXPECT_EQ(num_tasks, value);
- }
- // Adds a number of tasks to the queue and resizes the thread pool while the
- // threads are executing their work.
- TEST(ThreadPool, ResizingDuringExecution) {
- int value = 0;
- const int num_tasks = 100;
- // Run this test in a scope to delete the thread pool and all of the threads
- // are stopped.
- {
- ThreadPool thread_pool(/*num_threads=*/2);
- std::condition_variable condition;
- std::mutex mutex;
- // Acquire a lock on the mutex to prevent the threads from finishing their
- // execution so we can test resizing the thread pool while the workers are
- // executing a task.
- std::unique_lock<std::mutex> lock(mutex);
- // The same task for all of the workers to execute.
- auto task = [&]() {
- // This will block until the mutex is released inside the condition
- // variable.
- std::lock_guard<std::mutex> lock(mutex);
- ++value;
- condition.notify_all();
- };
- // Add the initial set of tasks to run.
- for (int i = 0; i < num_tasks / 2; ++i) {
- thread_pool.AddTask(task);
- }
- // Resize the thread pool while tasks are executing.
- thread_pool.Resize(/*num_threads=*/3);
- // Add more tasks to the thread pool to guarantee these are also completed.
- for (int i = 0; i < num_tasks / 2; ++i) {
- thread_pool.AddTask(task);
- }
- // Unlock the mutex to unblock all of the threads and wait until all of the
- // tasks are completed.
- condition.wait(lock, [&]() { return value == num_tasks; });
- }
- EXPECT_EQ(num_tasks, value);
- }
- // Tests the destructor will wait until all running tasks are finished before
- // destructing the thread pool.
- TEST(ThreadPool, Destructor) {
- // Ensure the hardware supports more than 1 thread to ensure the test will
- // pass.
- const int num_hardware_threads = std::thread::hardware_concurrency();
- if (num_hardware_threads <= 1) {
- LOG(ERROR)
- << "Test not supported, the hardware does not support threading.";
- return;
- }
- std::condition_variable condition;
- std::mutex mutex;
- // Lock the mutex to ensure the tasks are blocked.
- std::unique_lock<std::mutex> master_lock(mutex);
- int value = 0;
- // Create a thread that will instantiate and delete the thread pool. This is
- // required because we need to block on the thread pool being deleted and
- // signal the tasks to finish.
- std::thread thread([&]() {
- ThreadPool thread_pool(/*num_threads=*/2);
- for (int i = 0; i < 100; ++i) {
- thread_pool.AddTask([&]() {
- // This will block until the mutex is released inside the condition
- // variable.
- std::lock_guard<std::mutex> lock(mutex);
- ++value;
- condition.notify_all();
- });
- }
- // The thread pool should be deleted.
- });
- // Give the thread pool time to start, add all the tasks, and then delete
- // itself.
- std::this_thread::sleep_for(std::chrono::milliseconds(500));
- // Unlock the tasks.
- master_lock.unlock();
- // Wait for the thread to complete.
- thread.join();
- EXPECT_EQ(100, value);
- }
- TEST(ThreadPool, Resize) {
- // Ensure the hardware supports more than 1 thread to ensure the test will
- // pass.
- const int num_hardware_threads = std::thread::hardware_concurrency();
- if (num_hardware_threads <= 1) {
- LOG(ERROR)
- << "Test not supported, the hardware does not support threading.";
- return;
- }
- ThreadPool thread_pool(1);
- EXPECT_EQ(1, thread_pool.Size());
- thread_pool.Resize(2);
- EXPECT_EQ(2, thread_pool.Size());
- // Try reducing the thread pool size and verify it stays the same size.
- thread_pool.Resize(1);
- EXPECT_EQ(2, thread_pool.Size());
- }
- } // namespace ceres::internal
|