| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "util/threading.h" |
|
|
| #include "util/logging.h" |
|
|
| namespace colmap { |
|
|
| Thread::Thread() |
| : started_(false), |
| stopped_(false), |
| paused_(false), |
| pausing_(false), |
| finished_(false), |
| setup_(false), |
| setup_valid_(false) { |
| RegisterCallback(STARTED_CALLBACK); |
| RegisterCallback(FINISHED_CALLBACK); |
| } |
|
|
| void Thread::Start() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| CHECK(!started_ || finished_); |
| Wait(); |
| timer_.Restart(); |
| thread_ = std::thread(&Thread::RunFunc, this); |
| started_ = true; |
| stopped_ = false; |
| paused_ = false; |
| pausing_ = false; |
| finished_ = false; |
| setup_ = false; |
| setup_valid_ = false; |
| } |
|
|
| void Thread::Stop() { |
| { |
| std::unique_lock<std::mutex> lock(mutex_); |
| stopped_ = true; |
| } |
| Resume(); |
| } |
|
|
| void Thread::Pause() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| paused_ = true; |
| } |
|
|
| void Thread::Resume() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| if (paused_) { |
| paused_ = false; |
| pause_condition_.notify_all(); |
| } |
| } |
|
|
| void Thread::Wait() { |
| if (thread_.joinable()) { |
| thread_.join(); |
| } |
| } |
|
|
| bool Thread::IsStarted() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| return started_; |
| } |
|
|
| bool Thread::IsStopped() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| return stopped_; |
| } |
|
|
| bool Thread::IsPaused() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| return paused_; |
| } |
|
|
| bool Thread::IsRunning() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| return started_ && !pausing_ && !finished_; |
| } |
|
|
| bool Thread::IsFinished() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| return finished_; |
| } |
|
|
| void Thread::AddCallback(const int id, const std::function<void()>& func) { |
| CHECK(func); |
| CHECK_GT(callbacks_.count(id), 0) << "Callback not registered"; |
| callbacks_.at(id).push_back(func); |
| } |
|
|
| void Thread::RegisterCallback(const int id) { |
| callbacks_.emplace(id, std::list<std::function<void()>>()); |
| } |
|
|
| void Thread::Callback(const int id) const { |
| CHECK_GT(callbacks_.count(id), 0) << "Callback not registered"; |
| for (const auto& callback : callbacks_.at(id)) { |
| callback(); |
| } |
| } |
|
|
| std::thread::id Thread::GetThreadId() const { |
| return std::this_thread::get_id(); |
| } |
|
|
| void Thread::SignalValidSetup() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| CHECK(!setup_); |
| setup_ = true; |
| setup_valid_ = true; |
| setup_condition_.notify_all(); |
| } |
|
|
| void Thread::SignalInvalidSetup() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| CHECK(!setup_); |
| setup_ = true; |
| setup_valid_ = false; |
| setup_condition_.notify_all(); |
| } |
|
|
| const class Timer& Thread::GetTimer() const { return timer_; } |
|
|
| void Thread::BlockIfPaused() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| if (paused_) { |
| pausing_ = true; |
| timer_.Pause(); |
| pause_condition_.wait(lock); |
| pausing_ = false; |
| timer_.Resume(); |
| } |
| } |
|
|
| bool Thread::CheckValidSetup() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| if (!setup_) { |
| setup_condition_.wait(lock); |
| } |
| return setup_valid_; |
| } |
|
|
| void Thread::RunFunc() { |
| Callback(STARTED_CALLBACK); |
| Run(); |
| { |
| std::unique_lock<std::mutex> lock(mutex_); |
| finished_ = true; |
| timer_.Pause(); |
| } |
| Callback(FINISHED_CALLBACK); |
| } |
|
|
| ThreadPool::ThreadPool(const int num_threads) |
| : stopped_(false), num_active_workers_(0) { |
| const int num_effective_threads = GetEffectiveNumThreads(num_threads); |
| for (int index = 0; index < num_effective_threads; ++index) { |
| std::function<void(void)> worker = |
| std::bind(&ThreadPool::WorkerFunc, this, index); |
| workers_.emplace_back(worker); |
| } |
| } |
|
|
| ThreadPool::~ThreadPool() { Stop(); } |
|
|
| void ThreadPool::Stop() { |
| { |
| std::unique_lock<std::mutex> lock(mutex_); |
|
|
| if (stopped_) { |
| return; |
| } |
|
|
| stopped_ = true; |
|
|
| std::queue<std::function<void()>> empty_tasks; |
| std::swap(tasks_, empty_tasks); |
| } |
|
|
| task_condition_.notify_all(); |
|
|
| for (auto& worker : workers_) { |
| worker.join(); |
| } |
|
|
| finished_condition_.notify_all(); |
| } |
|
|
| void ThreadPool::Wait() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| if (!tasks_.empty() || num_active_workers_ > 0) { |
| finished_condition_.wait( |
| lock, [this]() { return tasks_.empty() && num_active_workers_ == 0; }); |
| } |
| } |
|
|
| void ThreadPool::WorkerFunc(const int index) { |
| { |
| std::lock_guard<std::mutex> lock(mutex_); |
| thread_id_to_index_.emplace(GetThreadId(), index); |
| } |
|
|
| while (true) { |
| std::function<void()> task; |
| { |
| std::unique_lock<std::mutex> lock(mutex_); |
| task_condition_.wait(lock, |
| [this] { return stopped_ || !tasks_.empty(); }); |
| if (stopped_ && tasks_.empty()) { |
| return; |
| } |
| task = std::move(tasks_.front()); |
| tasks_.pop(); |
| num_active_workers_ += 1; |
| } |
|
|
| task(); |
|
|
| { |
| std::unique_lock<std::mutex> lock(mutex_); |
| num_active_workers_ -= 1; |
| } |
|
|
| finished_condition_.notify_all(); |
| } |
| } |
|
|
| std::thread::id ThreadPool::GetThreadId() const { |
| return std::this_thread::get_id(); |
| } |
|
|
| int ThreadPool::GetThreadIndex() { |
| std::unique_lock<std::mutex> lock(mutex_); |
| return thread_id_to_index_.at(GetThreadId()); |
| } |
|
|
| int GetEffectiveNumThreads(const int num_threads) { |
| int num_effective_threads = num_threads; |
| if (num_threads <= 0) { |
| num_effective_threads = std::thread::hardware_concurrency(); |
| } |
|
|
| if (num_effective_threads <= 0) { |
| num_effective_threads = 1; |
| } |
|
|
| return num_effective_threads; |
| } |
|
|
| } |
|
|