Fix a bunch of race conditions between arrange and bg processing, fix #1770

This commit is contained in:
Vojtech Kral 2019-04-17 14:56:00 +02:00
parent 0c1e223414
commit b57452d090
2 changed files with 43 additions and 27 deletions

View file

@ -34,7 +34,7 @@
#include "../Utils/PresetUpdater.hpp" #include "../Utils/PresetUpdater.hpp"
#include "../Utils/PrintHost.hpp" #include "../Utils/PrintHost.hpp"
#include "ConfigWizard_private.hpp" #include "ConfigWizard.hpp"
#include "slic3r/Config/Snapshot.hpp" #include "slic3r/Config/Snapshot.hpp"
#include "ConfigSnapshotDialog.hpp" #include "ConfigSnapshotDialog.hpp"
#include "FirmwareDialog.hpp" #include "FirmwareDialog.hpp"
@ -230,7 +230,7 @@ bool GUI_App::on_init_inner()
// and after MainFrame is created & shown. // and after MainFrame is created & shown.
// The extra CallAfter() is needed because of Mac, where this is the only way // The extra CallAfter() is needed because of Mac, where this is the only way
// to popup a modal dialog on start without screwing combo boxes. // to popup a modal dialog on start without screwing combo boxes.
// This is ugly but I honestly found not better way to do it. // This is ugly but I honestly found no better way to do it.
// Neither wxShowEvent nor wxWindowCreateEvent work reliably. // Neither wxShowEvent nor wxWindowCreateEvent work reliably.
static bool once = true; static bool once = true;
if (once) { if (once) {

View file

@ -1169,8 +1169,8 @@ struct Plater::priv
wxString project_filename; wxString project_filename;
BackgroundSlicingProcess background_process; BackgroundSlicingProcess background_process;
std::atomic<bool> arranging; bool arranging;
std::atomic<bool> rotoptimizing; bool rotoptimizing;
bool delayed_scene_refresh; bool delayed_scene_refresh;
std::string delayed_error_message; std::string delayed_error_message;
@ -1333,8 +1333,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
, view_toolbar(GLToolbar::Radio) , view_toolbar(GLToolbar::Radio)
#endif // ENABLE_SVG_ICONS #endif // ENABLE_SVG_ICONS
{ {
arranging.store(false); arranging = false;
rotoptimizing.store(false); rotoptimizing = false;
background_process.set_fff_print(&fff_print); background_process.set_fff_print(&fff_print);
background_process.set_sla_print(&sla_print); background_process.set_sla_print(&sla_print);
background_process.set_gcode_preview_data(&gcode_preview_data); background_process.set_gcode_preview_data(&gcode_preview_data);
@ -2025,15 +2025,14 @@ void Plater::priv::mirror(Axis axis)
void Plater::priv::arrange() void Plater::priv::arrange()
{ {
// don't do anything if currently arranging. Then this is a re-entrance if (arranging) { return; }
if(arranging.load()) return; arranging = true;
Slic3r::ScopeGuard arranging_guard([this]() { arranging = false; });
// Guard the arrange process
arranging.store(true);
wxBusyCursor wait; wxBusyCursor wait;
this->background_process.stop(); this->background_process.stop();
unsigned count = 0; unsigned count = 0;
for(auto obj : model.objects) count += obj->instances.size(); for(auto obj : model.objects) count += obj->instances.size();
@ -2049,14 +2048,14 @@ void Plater::priv::arrange()
statusbar()->set_progress(count - st); statusbar()->set_progress(count - st);
statusbar()->set_status_text(msg); statusbar()->set_status_text(msg);
// ok, this is dangerous, but we are protected by the atomic flag // ok, this is dangerous, but we are protected by the flag
// 'arranging' and the arrange button is also disabled. // 'arranging' and the arrange button is also disabled.
// This call is needed for the cancel button to work. // This call is needed for the cancel button to work.
wxYieldIfNeeded(); wxYieldIfNeeded();
}; };
statusbar()->set_cancel_callback([this, statusfn](){ statusbar()->set_cancel_callback([this, statusfn](){
arranging.store(false); arranging = false;
statusfn(0, L("Arranging canceled")); statusfn(0, L("Arranging canceled"));
}); });
@ -2092,7 +2091,7 @@ void Plater::priv::arrange()
hint, hint,
false, // create many piles not just one pile false, // create many piles not just one pile
[statusfn](unsigned st) { statusfn(st, arrangestr); }, [statusfn](unsigned st) { statusfn(st, arrangestr); },
[this] () { return !arranging.load(); }); [this] () { return !arranging; });
} catch(std::exception& /*e*/) { } catch(std::exception& /*e*/) {
GUI::show_error(this->q, L("Could not arrange model objects! " GUI::show_error(this->q, L("Could not arrange model objects! "
"Some geometries may be invalid.")); "Some geometries may be invalid."));
@ -2101,7 +2100,6 @@ void Plater::priv::arrange()
statusfn(0, L("Arranging done.")); statusfn(0, L("Arranging done."));
statusbar()->set_range(prev_range); statusbar()->set_range(prev_range);
statusbar()->set_cancel_callback(); // remove cancel button statusbar()->set_cancel_callback(); // remove cancel button
arranging.store(false);
// Do a full refresh of scene tree, including regenerating all the GLVolumes. // Do a full refresh of scene tree, including regenerating all the GLVolumes.
//FIXME The update function shall just reload the modified matrices. //FIXME The update function shall just reload the modified matrices.
@ -2116,11 +2114,12 @@ void Plater::priv::sla_optimize_rotation() {
// running we should probably disable explicit slicing and background // running we should probably disable explicit slicing and background
// processing // processing
if(rotoptimizing.load()) return; if (rotoptimizing) { return; }
rotoptimizing.store(true); rotoptimizing = true;
Slic3r::ScopeGuard rotoptimizing_guard([this]() { rotoptimizing = false; });
int obj_idx = get_selected_object_idx(); int obj_idx = get_selected_object_idx();
if(obj_idx < 0) { rotoptimizing.store(false); return; } if (obj_idx < 0) { return; }
ModelObject * o = model.objects[size_t(obj_idx)]; ModelObject * o = model.objects[size_t(obj_idx)];
@ -2138,14 +2137,14 @@ void Plater::priv::sla_optimize_rotation() {
}; };
statusbar()->set_cancel_callback([this, stfn](){ statusbar()->set_cancel_callback([this, stfn](){
rotoptimizing.store(false); rotoptimizing = false;
stfn(0, L("Orientation search canceled")); stfn(0, L("Orientation search canceled"));
}); });
auto r = sla::find_best_rotation( auto r = sla::find_best_rotation(
*o, .005f, *o, .005f,
[stfn](unsigned s) { stfn(s, L("Searching for optimal orientation")); }, [stfn](unsigned s) { stfn(s, L("Searching for optimal orientation")); },
[this](){ return !rotoptimizing.load(); } [this](){ return !rotoptimizing; }
); );
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape"); const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
@ -2158,7 +2157,7 @@ void Plater::priv::sla_optimize_rotation() {
double mindist = 6.0; // FIXME double mindist = 6.0; // FIXME
double offs = mindist / 2.0 - EPSILON; double offs = mindist / 2.0 - EPSILON;
if(rotoptimizing.load()) // wasn't canceled if(rotoptimizing) // wasn't canceled
for(ModelInstance * oi : o->instances) { for(ModelInstance * oi : o->instances) {
oi->set_rotation({r[X], r[Y], r[Z]}); oi->set_rotation({r[X], r[Y], r[Z]});
@ -2208,7 +2207,6 @@ void Plater::priv::sla_optimize_rotation() {
stfn(0, L("Orientation found.")); stfn(0, L("Orientation found."));
statusbar()->set_range(prev_range); statusbar()->set_range(prev_range);
statusbar()->set_cancel_callback(); statusbar()->set_cancel_callback();
rotoptimizing.store(false);
update(true); update(true);
} }
@ -2386,6 +2384,11 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
// Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState. // Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState.
bool Plater::priv::restart_background_process(unsigned int state) bool Plater::priv::restart_background_process(unsigned int state)
{ {
if (arranging || rotoptimizing) {
// Avoid a race condition
return false;
}
if ( ! this->background_process.empty() && if ( ! this->background_process.empty() &&
(state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 &&
( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) || ( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) ||
@ -2612,6 +2615,11 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
{ {
if (evt.status.percent >= -1) { if (evt.status.percent >= -1) {
if (arranging || rotoptimizing) {
// Avoid a race condition
return;
}
this->statusbar()->set_progress(evt.status.percent); this->statusbar()->set_progress(evt.status.percent);
this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8("")); this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8(""));
} }
@ -3069,12 +3077,20 @@ bool Plater::priv::can_delete_all() const
bool Plater::priv::can_increase_instances() const bool Plater::priv::can_increase_instances() const
{ {
if (arranging || rotoptimizing) {
return false;
}
int obj_idx = get_selected_object_idx(); int obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); return (0 <= obj_idx) && (obj_idx < (int)model.objects.size());
} }
bool Plater::priv::can_decrease_instances() const bool Plater::priv::can_decrease_instances() const
{ {
if (arranging || rotoptimizing) {
return false;
}
int obj_idx = get_selected_object_idx(); int obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1);
} }
@ -3091,7 +3107,7 @@ bool Plater::priv::can_split_to_volumes() const
bool Plater::priv::can_arrange() const bool Plater::priv::can_arrange() const
{ {
return !model.objects.empty() && !arranging.load(); return !model.objects.empty() && !arranging;
} }
bool Plater::priv::can_layers_editing() const bool Plater::priv::can_layers_editing() const
@ -3232,9 +3248,9 @@ void Plater::remove_selected()
void Plater::increase_instances(size_t num) void Plater::increase_instances(size_t num)
{ {
if (! can_increase_instances()) { return; }
int obj_idx = p->get_selected_object_idx(); int obj_idx = p->get_selected_object_idx();
if (obj_idx == -1)
return;
ModelObject* model_object = p->model.objects[obj_idx]; ModelObject* model_object = p->model.objects[obj_idx];
ModelInstance* model_instance = model_object->instances.back(); ModelInstance* model_instance = model_object->instances.back();
@ -3266,9 +3282,9 @@ void Plater::increase_instances(size_t num)
void Plater::decrease_instances(size_t num) void Plater::decrease_instances(size_t num)
{ {
if (! can_decrease_instances()) { return; }
int obj_idx = p->get_selected_object_idx(); int obj_idx = p->get_selected_object_idx();
if (obj_idx == -1)
return;
ModelObject* model_object = p->model.objects[obj_idx]; ModelObject* model_object = p->model.objects[obj_idx];
if (model_object->instances.size() > num) { if (model_object->instances.size() > num) {