diff options
author | bubnikv <bubnikv@gmail.com> | 2018-09-14 10:28:00 +0300 |
---|---|---|
committer | bubnikv <bubnikv@gmail.com> | 2018-09-14 10:28:00 +0300 |
commit | 9d9e4a0f7b2d9d3440c2a05bc65b2cab707d148a (patch) | |
tree | e57a7cb171afe5efa6381a73ad02dcbce3050826 /xs/src/libslic3r | |
parent | bb70ad609025a259f35f4f21e2fc9587c7a3fc2d (diff) |
WIP: Background processing.
Diffstat (limited to 'xs/src/libslic3r')
-rw-r--r-- | xs/src/libslic3r/GCode.cpp | 18 | ||||
-rw-r--r-- | xs/src/libslic3r/GCodeTimeEstimator.cpp | 4 | ||||
-rw-r--r-- | xs/src/libslic3r/Model.cpp | 1 | ||||
-rw-r--r-- | xs/src/libslic3r/Model.hpp | 1 | ||||
-rw-r--r-- | xs/src/libslic3r/Print.cpp | 23 | ||||
-rw-r--r-- | xs/src/libslic3r/Print.hpp | 19 | ||||
-rw-r--r-- | xs/src/libslic3r/PrintObject.cpp | 13 | ||||
-rw-r--r-- | xs/src/libslic3r/Utils.hpp | 8 | ||||
-rw-r--r-- | xs/src/libslic3r/utils.cpp | 82 |
9 files changed, 145 insertions, 24 deletions
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 0ade0de61..91c9d6b0f 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -418,6 +418,17 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ { PROFILE_CLEAR(); + if (print->is_step_done(psGCodeExport)) { + // Does the file exist? If so, we hope that it is still valid. + FILE *f = boost::nowide::fopen(path, "r"); + if (f != nullptr) { + ::fclose(f); + return; + } + } + + print->set_started(psGCodeExport); + BOOST_LOG_TRIVIAL(info) << "Exporting G-code..."; // Remove the old g-code if it exists. @@ -467,12 +478,13 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } - if (boost::nowide::rename(path_tmp.c_str(), path) != 0) + if (rename_file(path_tmp, path) != 0) throw std::runtime_error( std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "Is " + path_tmp + " locked?" + '\n'); BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished"; + print->set_done(psGCodeExport); // Write the profiler measurements to file PROFILE_UPDATE(); @@ -483,8 +495,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) { PROFILE_FUNC(); - print.set_started(psGCodeExport); - // resets time estimators m_normal_time_estimator.reset(); m_normal_time_estimator.set_dialect(print.config().gcode_flavor); @@ -1029,8 +1039,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // starts analizer calculations if (preview_data != nullptr) m_analyzer.calc_gcode_preview_data(*preview_data); - - print.set_done(psGCodeExport); } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index 7471367fe..f97265ee3 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -1,4 +1,5 @@ #include "GCodeTimeEstimator.hpp" +#include "Utils.hpp" #include <boost/bind.hpp> #include <cmath> @@ -367,8 +368,7 @@ namespace Slic3r { fclose(out); in.close(); - boost::nowide::remove(filename.c_str()); - if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0) + if (rename_file(path_tmp, filename) != 0) throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + "Is " + path_tmp + " locked?" + '\n'); diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 19c474cad..dc21ab4ae 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -849,6 +849,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects) return; } +// Called by Print::validate() from the UI thread. void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) { for (const ModelVolume* vol : this->volumes) diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 8a8af481c..8ca1d8ea9 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -129,6 +129,7 @@ public: void cut(coordf_t z, Model* model) const; void split(ModelObjectPtrs* new_objects); + // Called by Print::validate() from the UI thread. void check_instances_print_volume_state(const BoundingBoxf3& print_volume); // Print object statistics to console. diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index c8a50a6bb..a677d9e5a 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -72,6 +72,7 @@ void Print::reload_object(size_t /* idx */) // Returns true if the brim or skirt have been invalidated. bool Print::reload_model_instances() { + tbb::mutex::scoped_lock lock(m_mutex); bool invalidated = false; for (PrintObject *object : m_objects) invalidated |= object->reload_model_instances(); @@ -370,6 +371,7 @@ double Print::max_allowed_layer_height() const // and have explicit instance positions. void Print::add_model_object(ModelObject* model_object, int idx) { + tbb::mutex::scoped_lock lock(m_mutex); // Initialize a new print object and store it at the given position. PrintObject *object = new PrintObject(this, model_object, model_object->raw_bounding_box()); if (idx != -1) { @@ -378,6 +380,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) } else m_objects.emplace_back(object); // Invalidate all print steps. + //FIXME lock mutex! this->invalidate_all_steps(); for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { @@ -434,6 +437,8 @@ void Print::add_model_object(ModelObject* model_object, int idx) bool Print::apply_config(DynamicPrintConfig config) { + tbb::mutex::scoped_lock lock(m_mutex); + // we get a copy of the config object so we can modify it safely config.normalize(); @@ -564,13 +569,17 @@ std::string Print::validate() const BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(m_config.max_print_height))); // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. print_volume.min(2) = -1e10; - unsigned int printable_count = 0; - for (PrintObject *po : m_objects) { - po->model_object()->check_instances_print_volume_state(print_volume); - po->reload_model_instances(); - if (po->is_printable()) - ++printable_count; - } + unsigned int printable_count = 0; + { + // Lock due to the po->reload_model_instances() + tbb::mutex::scoped_lock lock(m_mutex); + for (PrintObject *po : m_objects) { + po->model_object()->check_instances_print_volume_state(print_volume); + po->reload_model_instances(); + if (po->is_printable()) + ++ printable_count; + } + } if (printable_count == 0) return L("All objects are outside of the print volume."); diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index d72c1fef5..106640c4d 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -88,6 +88,11 @@ public: bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback &cancel) { bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID; if (invalidated) { +#if 0 + if (mtx.state != mtx.HELD) { + printf("Not held!\n"); + } +#endif mtx.unlock(); cancel(); mtx.lock(); @@ -283,18 +288,14 @@ private: SupportLayerPtrs m_support_layers; PrintState<PrintObjectStep, posCount> m_state; - // Mutex used for synchronization of the worker thread with the UI thread: - // The mutex will be used to guard the worker thread against entering a stage - // while the data influencing the stage is modified. - tbb::mutex m_mutex; // TODO: call model_object->get_bounding_box() instead of accepting // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject() {} - void set_started(PrintObjectStep step) { m_state.set_started(step, m_mutex); } - void set_done(PrintObjectStep step) { m_state.set_done(step, m_mutex); } + void set_started(PrintObjectStep step); + void set_done(PrintObjectStep step); std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier); }; @@ -446,8 +447,8 @@ public: const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } protected: - void set_started(PrintStep step) { m_state.set_started(step, m_mutex); } - void set_done(PrintStep step) { m_state.set_done(step, m_mutex); } + void set_started(PrintStep step) { m_state.set_started(step, m_mutex); throw_if_canceled(); } + void set_done(PrintStep step) { m_state.set_done(step, m_mutex); throw_if_canceled(); } bool invalidate_step(PrintStep step); bool invalidate_all_steps() { return m_state.invalidate_all(m_mutex, m_cancel_callback); } @@ -473,7 +474,7 @@ private: // Mutex used for synchronization of the worker thread with the UI thread: // The mutex will be used to guard the worker thread against entering a stage // while the data influencing the stage is modified. - tbb::mutex m_mutex; + mutable tbb::mutex m_mutex; // Has the calculation been canceled? tbb::atomic<bool> m_canceled; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 96c831157..7882c58a8 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -59,8 +59,19 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding this->layer_height_profile = model_object->layer_height_profile; } +void PrintObject::set_started(PrintObjectStep step) +{ + m_state.set_started(step, m_print->m_mutex); +} + +void PrintObject::set_done(PrintObjectStep step) +{ + m_state.set_done(step, m_print->m_mutex); +} + bool PrintObject::add_copy(const Vec2d &point) { + tbb::mutex::scoped_lock lock(m_print->m_mutex); Points points = m_copies; points.push_back(Point::new_scale(point(0), point(1))); return this->set_copies(points); @@ -68,6 +79,7 @@ bool PrintObject::add_copy(const Vec2d &point) bool PrintObject::delete_last_copy() { + tbb::mutex::scoped_lock lock(m_print->m_mutex); Points points = m_copies; points.pop_back(); return this->set_copies(points); @@ -158,7 +170,6 @@ void PrintObject::make_perimeters() m_print->throw_if_canceled(); } this->typed_slices = false; -// m_state.invalidate(posPrepareInfill); } // compare each layer to the one below, and mark those slices needing diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index f1390b8a2..dd05891dd 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -43,6 +43,14 @@ extern local_encoded_string encode_path(const char *src); extern std::string decode_path(const char *src); extern std::string normalize_utf8_nfc(const char *src); +// Safely rename a file even if the target exists. +// On Windows, the file explorer (or anti-virus or whatever else) often locks the file +// for a short while, so the file may not be movable. Retry while we see recoverable errors. +extern int rename_file(const std::string &from, const std::string &to); + +// Copy a file, adjust the access attributes, so that the target is writable. +extern int copy_file(const std::string &from, const std::string &to); + // File path / name / extension splitting utilities, working with UTF-8, // to be published to Perl. namespace PerlUtils { diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 95aaf5453..4c2d65605 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -23,6 +23,7 @@ #include <boost/nowide/fstream.hpp> #include <boost/nowide/integration/filesystem.hpp> #include <boost/nowide/convert.hpp> +#include <boost/nowide/cstdio.hpp> namespace Slic3r { @@ -139,6 +140,87 @@ const std::string& data_dir() return g_data_dir; } + +// borrowed from LVVM lib/Support/Windows/Path.inc +int rename_file(const std::string &from, const std::string &to) +{ + int ec = 0; + +#ifdef _WIN32 + + // Convert to utf-16. + std::wstring wide_from = boost::nowide::widen(from); + std::wstring wide_to = boost::nowide::widen(to); + + // Retry while we see recoverable errors. + // System scanners (eg. indexer) might open the source file when it is written + // and closed. + bool TryReplace = true; + + // This loop may take more than 2000 x 1ms to finish. + for (int i = 0; i < 2000; ++ i) { + if (i > 0) + // Sleep 1ms + ::Sleep(1); + if (TryReplace) { + // Try ReplaceFile first, as it is able to associate a new data stream + // with the destination even if the destination file is currently open. + if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL)) + return 0; + DWORD ReplaceError = ::GetLastError(); + ec = -1; // ReplaceError + // If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or + // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW(). + if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT || + ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) { + TryReplace = false; + continue; + } + // If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry + // using ReplaceFileW(). + if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED) + continue; + // We get ERROR_FILE_NOT_FOUND if the destination file is missing. + // MoveFileEx can handle this case. + if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION) + break; + } + if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) + return 0; + DWORD MoveError = ::GetLastError(); + ec = -1; // MoveError + if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION) + break; + } + +#else + + boost::nowide::remove(from.c_str()); + ec = boost::nowide::rename(from.c_str(), to.c_str()); + +#endif + + return ec; +} + +int copy_file(const std::string &from, const std::string &to) +{ + const boost::filesystem::path source(from); + const boost::filesystem::path target(to); + static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644 + + // Make sure the file has correct permission both before and after we copy over it. + try { + if (boost::filesystem::exists(target)) + boost::filesystem::permissions(target, perms); + boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists); + boost::filesystem::permissions(target, perms); + } catch (std::exception & /* ex */) { + return -1; + } + return 0; +} + } // namespace Slic3r #include <xsinit.h> |