diff --git a/cartographer/mapping/internal/3d/pose_graph_3d.cc b/cartographer/mapping/internal/3d/pose_graph_3d.cc index 196f276..3622ebc 100644 --- a/cartographer/mapping/internal/3d/pose_graph_3d.cc +++ b/cartographer/mapping/internal/3d/pose_graph_3d.cc @@ -51,7 +51,7 @@ PoseGraph3D::PoseGraph3D( PoseGraph3D::~PoseGraph3D() { WaitForAllComputations(); - common::MutexLocker locker(&mutex_); + common::MutexLocker locker(&work_queue_mutex_); CHECK(work_queue_ == nullptr); } @@ -153,7 +153,7 @@ NodeId PoseGraph3D::AddNode( void PoseGraph3D::AddWorkItem( const std::function& work_item) { { - common::MutexLocker locker(&mutex_); + common::MutexLocker locker(&work_queue_mutex_); if (work_queue_ != nullptr) { const auto now = std::chrono::steady_clock::now(); work_queue_->push_back({now, work_item}); @@ -164,7 +164,7 @@ void PoseGraph3D::AddWorkItem( } if (work_item() == WorkItem::Result::kRunOptimization) { { - common::MutexLocker locker(&mutex_); + common::MutexLocker locker(&work_queue_mutex_); work_queue_ = common::make_unique(); } constraint_builder_.WhenDone( @@ -489,7 +489,7 @@ void PoseGraph3D::HandleWorkQueue( while (process_work_queue) { std::function work_item; { - common::MutexLocker locker(&mutex_); + common::MutexLocker locker(&work_queue_mutex_); if (work_queue_->empty()) { work_queue_.reset(); return; @@ -509,40 +509,60 @@ void PoseGraph3D::HandleWorkQueue( } void PoseGraph3D::WaitForAllComputations() { - bool notification = false; - common::MutexLocker locker(&mutex_); + int num_trajectory_nodes; + { + common::MutexLocker locker(&mutex_); + num_trajectory_nodes = data_.num_trajectory_nodes; + } + const int num_finished_nodes_at_start = constraint_builder_.GetNumFinishedNodes(); - while (!locker.AwaitWithTimeout( - [this]() REQUIRES(mutex_) { - return ((constraint_builder_.GetNumFinishedNodes() == - data_.num_trajectory_nodes) && - !work_queue_); - }, - common::FromSeconds(1.))) { + + auto report_progress = [this, num_trajectory_nodes, + num_finished_nodes_at_start]() { // Log progress on nodes only when we are actually processing nodes. - if (data_.num_trajectory_nodes != num_finished_nodes_at_start) { + if (num_trajectory_nodes != num_finished_nodes_at_start) { std::ostringstream progress_info; progress_info << "Optimizing: " << std::fixed << std::setprecision(1) << 100. * (constraint_builder_.GetNumFinishedNodes() - num_finished_nodes_at_start) / - (data_.num_trajectory_nodes - - num_finished_nodes_at_start) + (num_trajectory_nodes - num_finished_nodes_at_start) << "%..."; std::cout << "\r\x1b[K" << progress_info.str() << std::flush; } + }; + + // First wait for the work queue to drain so that it's safe to schedule + // a WhenDone() callback. + { + common::MutexLocker locker(&work_queue_mutex_); + while (!locker.AwaitWithTimeout( + [this]() REQUIRES(work_queue_mutex_) { return work_queue_ == nullptr; }, + common::FromSeconds(1.))) { + report_progress(); + } } - std::cout << "\r\x1b[KOptimizing: Done. " << std::endl; + + // Now wait for any pending constraint computations to finish. + common::MutexLocker locker(&mutex_); + bool notification GUARDED_BY(mutex_) = false; constraint_builder_.WhenDone( [this, - ¬ification](const constraints::ConstraintBuilder3D::Result& result) { - common::MutexLocker locker(&mutex_); - data_.constraints.insert(data_.constraints.end(), result.begin(), - result.end()); - notification = true; - }); - locker.Await([¬ification]() { return notification; }); + ¬ification](const constraints::ConstraintBuilder3D::Result& result) + EXCLUDES(mutex_) { + common::MutexLocker locker(&mutex_); + data_.constraints.insert(data_.constraints.end(), result.begin(), + result.end()); + notification = true; + }); + while (!locker.AwaitWithTimeout([¬ification]() + REQUIRES(mutex_) { return notification; }, + common::FromSeconds(1.))) { + report_progress(); + } + CHECK_EQ(constraint_builder_.GetNumFinishedNodes(), num_trajectory_nodes); + std::cout << "\r\x1b[KOptimizing: Done. " << std::endl; } void PoseGraph3D::DeleteTrajectory(const int trajectory_id) { diff --git a/cartographer/mapping/internal/3d/pose_graph_3d.h b/cartographer/mapping/internal/3d/pose_graph_3d.h index 117c0d5..dc271cc 100644 --- a/cartographer/mapping/internal/3d/pose_graph_3d.h +++ b/cartographer/mapping/internal/3d/pose_graph_3d.h @@ -162,7 +162,7 @@ class PoseGraph3D : public PoseGraph { // Handles a new work item. void AddWorkItem(const std::function& work_item) - EXCLUDES(mutex_); + EXCLUDES(mutex_) EXCLUDES(work_queue_mutex_); // Adds connectivity and sampler for a trajectory if it does not exist. void AddTrajectoryIfNeeded(int trajectory_id) REQUIRES(mutex_); @@ -197,7 +197,7 @@ class PoseGraph3D : public PoseGraph { // Runs the optimization, executes the trimmers and processes the work queue. void HandleWorkQueue(const constraints::ConstraintBuilder3D::Result& result) - EXCLUDES(mutex_); + EXCLUDES(mutex_) EXCLUDES(work_queue_mutex_); // Runs the optimization. Callers have to make sure, that there is only one // optimization being run at a time. @@ -229,10 +229,11 @@ class PoseGraph3D : public PoseGraph { const proto::PoseGraphOptions options_; GlobalSlamOptimizationCallback global_slam_optimization_callback_; mutable common::Mutex mutex_; + common::Mutex work_queue_mutex_; // If it exists, further work items must be added to this queue, and will be // considered later. - std::unique_ptr work_queue_ GUARDED_BY(mutex_); + std::unique_ptr work_queue_ GUARDED_BY(work_queue_mutex_); // We globally localize a fraction of the nodes from each trajectory. std::unordered_map>