Fix deadlock when canceling the slicing while creating thumbnails

This commit is contained in:
remi durand 2021-04-29 14:57:52 +02:00
parent 7ec55736dc
commit ab83dcf305
8 changed files with 52 additions and 16 deletions

View File

@ -169,7 +169,7 @@ enum PrinterTechnology : uint8_t
// Laser engraving // Laser engraving
ptLaser = 1 << 4, ptLaser = 1 << 4,
// Any technology, useful for parameters compatible with both ptFFF and ptSLA // Any technology, useful for parameters compatible with both ptFFF and ptSLA
ptAny = 1+2+4, ptAny = ptFFF | ptSLA | ptSLS | ptMill | ptLaser,
// Unknown, useful for command line processing // Unknown, useful for command line processing
ptUnknown = 1 << 7 ptUnknown = 1 << 7
}; };

View File

@ -849,10 +849,16 @@ namespace DoExport {
for (const Vec2d &size : sizes) for (const Vec2d &size : sizes)
if (size.x() > 0 && size.y() > 0) if (size.x() > 0 && size.y() > 0)
good_sizes.push_back(size); good_sizes.push_back(size);
if (good_sizes.empty()) return;
//Create the thumbnails
const size_t max_row_length = 78; const size_t max_row_length = 78;
ThumbnailsList thumbnails; ThumbnailsList thumbnails;
thumbnail_cb(thumbnails, good_sizes, true, true, thumbnails_with_bed, true); // note that it needs the gui thread, so can create deadlock if job is canceled.
bool can_create_thumbnail = thumbnail_cb(thumbnails, good_sizes, true, true, thumbnails_with_bed, true);
throw_if_canceled();
if (!can_create_thumbnail) return;
for (const ThumbnailData& data : thumbnails) for (const ThumbnailData& data : thumbnails)
{ {
if (data.is_valid()) if (data.is_valid())

View File

@ -20,7 +20,7 @@ struct ThumbnailData
}; };
typedef std::vector<ThumbnailData> ThumbnailsList; typedef std::vector<ThumbnailData> ThumbnailsList;
typedef std::function<void(ThumbnailsList & thumbnails, const Vec2ds & sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)> ThumbnailsGeneratorCallback; typedef std::function<bool(ThumbnailsList & thumbnails, const Vec2ds & sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)> ThumbnailsGeneratorCallback;
} // namespace Slic3r } // namespace Slic3r

View File

@ -1206,7 +1206,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
GLCanvas3D::~GLCanvas3D() GLCanvas3D::~GLCanvas3D()
{ {
reset_volumes(); reset_volumes(true);
} }
void GLCanvas3D::post_event(wxEvent &&event) void GLCanvas3D::post_event(wxEvent &&event)
@ -1303,7 +1303,7 @@ unsigned int GLCanvas3D::get_volumes_count() const
return (unsigned int)m_volumes.volumes.size(); return (unsigned int)m_volumes.volumes.size();
} }
void GLCanvas3D::reset_volumes() void GLCanvas3D::reset_volumes(bool is_destroying)
{ {
if (!m_initialized) if (!m_initialized)
return; return;
@ -1313,10 +1313,11 @@ void GLCanvas3D::reset_volumes()
_set_current(); _set_current();
m_selection.clear(); m_selection.clear(is_destroying);
m_volumes.clear(); m_volumes.clear();
m_dirty = true; m_dirty = true;
if(!is_destroying)
_set_warning_texture(WarningTexture::ObjectOutside, false); _set_warning_texture(WarningTexture::ObjectOutside, false);
} }

View File

@ -558,7 +558,7 @@ public:
unsigned int get_volumes_count() const; unsigned int get_volumes_count() const;
const GLVolumeCollection& get_volumes() const { return m_volumes; } const GLVolumeCollection& get_volumes() const { return m_volumes; }
void reset_volumes(); void reset_volumes(bool is_destroying = false);
int check_volumes_outside_state() const; int check_volumes_outside_state() const;
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }

View File

@ -2001,14 +2001,43 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
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_result(&gcode_result); background_process.set_gcode_result(&gcode_result);
background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)->bool
{ {
std::packaged_task<void(ThumbnailsList&, const Vec2ds&, bool, bool, bool, bool)> task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) { auto task = std::make_shared<std::packaged_task<void(ThumbnailsList&, const Vec2ds&, bool, bool, bool, bool)>>([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) {
generate_thumbnails(thumbnails, sizes, printable_only, parts_only, show_bed, transparent_background); generate_thumbnails(thumbnails, sizes, printable_only, parts_only, show_bed, transparent_background);
}); });
std::future<void> result = task.get_future();
wxTheApp->CallAfter([&]() { task(thumbnails, sizes, printable_only, parts_only, show_bed, transparent_background); }); std::future<void> future_result = task->get_future();
result.wait(); std::shared_ptr<std::mutex> protect_bool = std::make_shared<std::mutex>();
std::shared_ptr<bool> is_started = std::make_shared<bool>(false);
std::shared_ptr<bool> cancel = std::make_shared<bool>(false);
wxTheApp->CallAfter([task, protect_bool, is_started, cancel, &thumbnails, &sizes, &printable_only, &parts_only, &show_bed, &transparent_background]()
{
{
std::lock_guard<std::mutex> lock(*protect_bool);
if (*cancel)
return;
*is_started = true;
}
(*task)(thumbnails, sizes, printable_only, parts_only, show_bed, transparent_background);
});
// can deadlock if background processing is cancelled / locked
// have to cancel the process if we're exiting here as the parameters will be deleted.
// if the process is already started, then we have to wait its end. and there is no deadlock with generate_thumbnails
// 2 seconds is plenty to
std::future_status result = future_result.wait_for(std::chrono::seconds(2));
if (result == std::future_status::ready)
return true;
{
std::lock_guard<std::mutex> lock(*protect_bool);
if (*is_started) {
future_result.wait();
result = std::future_status::ready;
} else {
*cancel = true;
}
}
return result == std::future_status::ready;
}); });
background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED); background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED);
background_process.set_finished_event(EVT_PROCESS_COMPLETED); background_process.set_finished_event(EVT_PROCESS_COMPLETED);

View File

@ -447,7 +447,7 @@ void Selection::set_deserialized(EMode mode, const std::vector<std::pair<size_t,
this->set_bounding_boxes_dirty(); this->set_bounding_boxes_dirty();
} }
void Selection::clear() void Selection::clear(bool is_destroying)
{ {
if (!m_valid) if (!m_valid)
return; return;
@ -466,7 +466,7 @@ void Selection::clear()
this->set_bounding_boxes_dirty(); this->set_bounding_boxes_dirty();
// this happens while the application is closing // this happens while the application is closing
if (wxGetApp().obj_manipul() == nullptr) if (is_destroying || wxGetApp().obj_manipul() == nullptr)
return; return;
// resets the cache in the sidebar // resets the cache in the sidebar

View File

@ -268,7 +268,7 @@ public:
// Update the selection based on the map from old indices to new indices after m_volumes changed. // Update the selection based on the map from old indices to new indices after m_volumes changed.
// If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances. // If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances.
void volumes_changed(const std::vector<size_t> &map_volume_old_to_new); void volumes_changed(const std::vector<size_t> &map_volume_old_to_new);
void clear(); void clear(bool is_destroying = false);
bool is_empty() const { return m_type == Empty; } bool is_empty() const { return m_type == Empty; }
bool is_wipe_tower() const { return m_type == WipeTower; } bool is_wipe_tower() const { return m_type == WipeTower; }