diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index e5fc80bc28..8145462903 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -904,10 +904,6 @@ int main(int argc, char** argv) { Logger::Notice("Using " + std::to_string(num_threads) + " threads"); } - if (!quiet && num_threads > 1) { - Logger::Status("Creating geometry..."); - } - if (vmap.count("log-file")) { Logger::SetOutput(quiet ? nullptr : &cout_, &log_fs); } else { @@ -1085,11 +1081,7 @@ int main(int argc, char** argv) { int old_progress = quiet ? 0 : -1; if (!quiet) { - if (num_threads == 1) { - Logger::Status("Creating geometry..."); - } else { - Logger::Status("Writing geometry..."); - } + Logger::Status("Creating geometry..."); } // The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next() diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index 00ec057b27..b16476af47 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -114,13 +114,20 @@ namespace IfcGeom { class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation { private: - std::atomic progress_; + std::atomic finished_ = false; + std::atomic progress_ = 0; std::vector tasks_; - std::vector all_processed_elements_; - std::vector all_processed_native_elements_; - typename std::vector::const_iterator task_result_iterator_; - typename std::vector::const_iterator native_task_result_iterator_; + std::list all_processed_elements_; + std::list all_processed_native_elements_; + + typename std::list::const_iterator task_result_iterator_; + typename std::list::const_iterator native_task_result_iterator_; + + std::mutex element_ready_mutex_; + bool task_result_ptr_initialized = false; + size_t async_elements_returned_ = 0; + MAKE_TYPE_NAME(IteratorImplementation_)(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I MAKE_TYPE_NAME(IteratorImplementation_)& operator=(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I @@ -240,6 +247,10 @@ namespace IfcGeom { initialization_outcome_ = !tasks_.empty(); init_future_ = std::async(std::launch::async, [this]() { process_concurrently(); }); + + // wait for the first element, because after init(), get() can be called. + // so the element conversion must succeed + initialization_outcome_ = wait_for_element(); } else { initialization_outcome_ = create(); } @@ -275,6 +286,23 @@ namespace IfcGeom { } } + size_t processed_ = 0; + + void process_finished_rep(geometry_conversion_task* rep) { + std::lock_guard lk(element_ready_mutex_); + + all_processed_elements_.insert(all_processed_elements_.end(), rep->elements.begin(), rep->elements.end()); + all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep->breps.begin(), rep->breps.end()); + + if (!task_result_ptr_initialized) { + task_result_iterator_ = all_processed_elements_.begin(); + native_task_result_iterator_ = all_processed_native_elements_.begin(); + task_result_ptr_initialized = true; + } + + progress_ = ++processed_ * 100 / tasks_.size(); + } + void process_concurrently() { size_t conc_threads = num_threads_; if (conc_threads > tasks_.size()) { @@ -287,13 +315,8 @@ namespace IfcGeom { kernel_pool.push_back(new MAKE_TYPE_NAME(Kernel)(kernel)); } - std::vector> threadpool; - - int old_progress = -1; - int processed = 0; - - Logger::ProgressBar(0); - + std::vector> threadpool; + for (auto& rep : tasks_) { MAKE_TYPE_NAME(Kernel)* K = nullptr; if (threadpool.size() < kernel_pool.size()) { @@ -302,18 +325,11 @@ namespace IfcGeom { while (threadpool.size() == conc_threads) { for (int i = 0; i < (int)threadpool.size(); i++) { - std::future &fu = threadpool[i]; + auto& fu = threadpool[i]; std::future_status status; status = fu.wait_for(std::chrono::seconds(0)); if (status == std::future_status::ready) { - fu.get(); - - processed += 1; - progress_ = processed * 50 / tasks_.size(); - if (progress_ != old_progress) { - Logger::ProgressBar(progress_); - old_progress = progress_; - } + process_finished_rep(fu.get()); std::swap(threadpool[i], threadpool.back()); threadpool.pop_back(); @@ -324,12 +340,13 @@ namespace IfcGeom { } // for } // while - std::future fu = std::async( + std::future fu = std::async( std::launch::async, [this]( IfcGeom::MAKE_TYPE_NAME(Kernel)* kernel, const IfcGeom::IteratorSettings& settings, geometry_conversion_task* rep) { - return this->create_element_(kernel, settings, rep); + this->create_element_(kernel, settings, rep); + return rep; }, K, std::ref(settings), @@ -338,24 +355,11 @@ namespace IfcGeom { threadpool.emplace_back(std::move(fu)); } - for (std::future &fu : threadpool) { - fu.get(); - - processed += 1; - progress_ = processed * 100 / tasks_.size(); - if (progress_ / 2 != old_progress) { - Logger::ProgressBar(progress_ / 2); - old_progress = progress_ / 2; - } + for (auto& fu : threadpool) { + process_finished_rep(fu.get()); } - for (auto& rep : tasks_) { - all_processed_elements_.insert(all_processed_elements_.end(), rep.elements.begin(), rep.elements.end()); - all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep.breps.begin(), rep.breps.end()); - } - - task_result_iterator_ = all_processed_elements_.begin(); - native_task_result_iterator_ = all_processed_native_elements_.begin(); + finished_ = true; Logger::Status("\rDone creating geometry (" + boost::lexical_cast(all_processed_elements_.size()) + " objects) "); @@ -874,6 +878,24 @@ namespace IfcGeom { } } + bool wait_for_element() { + while (true) { + size_t s; + { + std::lock_guard lk(element_ready_mutex_); + s = all_processed_elements_.size(); + } + if (s > async_elements_returned_) { + ++async_elements_returned_; + return true; + } else if (finished_) { + return false; + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + } + public: /// Returns what would be the product for the next shape representation /// @todo Double-check and test the impl. @@ -893,13 +915,14 @@ namespace IfcGeom { /// Use get() to retrieve the created geometry. IfcUtil::IfcBaseClass* next() { if (num_threads_ != 1) { + if (!wait_for_element()) { + return nullptr; + } + task_result_iterator_++; native_task_result_iterator_++; - if (task_result_iterator_ == all_processed_elements_.end()) { - return nullptr; - } else { - return (*task_result_iterator_)->product(); - } + + return (*task_result_iterator_)->product(); } else { // Increment the iterator over the list of products using the current // shape representation