diff --git a/src/libslic3r/Exception.hpp b/src/libslic3r/Exception.hpp index fababa47d..1b8ad50a7 100644 --- a/src/libslic3r/Exception.hpp +++ b/src/libslic3r/Exception.hpp @@ -15,6 +15,7 @@ class Exception : public std::runtime_error { using std::runtime_error::runtime_ SLIC3R_DERIVE_EXCEPTION(CriticalException, Exception); SLIC3R_DERIVE_EXCEPTION(RuntimeError, CriticalException); SLIC3R_DERIVE_EXCEPTION(LogicError, CriticalException); +SLIC3R_DERIVE_EXCEPTION(HardCrash, CriticalException); SLIC3R_DERIVE_EXCEPTION(InvalidArgument, LogicError); SLIC3R_DERIVE_EXCEPTION(OutOfRange, LogicError); SLIC3R_DERIVE_EXCEPTION(IOError, CriticalException); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 8b3b37513..482900c50 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -74,11 +74,15 @@ std::pair SlicingProcessCompletedEvent::format_error_message( bool monospace = false; try { this->rethrow_exception(); - } catch (const std::bad_alloc& ex) { + } catch (const std::bad_alloc &ex) { wxString errmsg = GUI::from_u8((boost::format(_utf8(L("%s has encountered an error. It was likely caused by running out of memory. " "If you are sure you have enough RAM on your system, this may also be a bug and we would " "be glad if you reported it."))) % SLIC3R_APP_NAME).str()); error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what()); + } catch (const HardCrash &ex) { + error = GUI::format("PrusaSlicer has encountered a fatal error: \"%1%\"", ex.what()) + "\n\n" + + _u8L("Please save your project and restart PrusaSlicer. " + "We would be glad if you reported the issue."); } catch (PlaceholderParserError &ex) { error = ex.what(); monospace = true; @@ -277,19 +281,11 @@ void BackgroundSlicingProcess::thread_proc() m_state = STATE_RUNNING; lck.unlock(); std::exception_ptr exception; - try { - assert(m_print != nullptr); - switch(m_print->technology()) { - case ptFFF: this->process_fff(); break; - case ptSLA: this->process_sla(); break; - default: m_print->process(); break; - } - } catch (CanceledException & /* ex */) { - // Canceled, this is all right. - assert(m_print->canceled()); - } catch (...) { - exception = std::current_exception(); - } +#ifdef _WIN32 + this->call_process_seh_throw(exception); +#else + this->call_process(exception); +#endif m_print->finalize(); lck.lock(); m_state = m_print->canceled() ? STATE_CANCELED : STATE_FINISHED; @@ -312,7 +308,118 @@ void BackgroundSlicingProcess::thread_proc() // End of the background processing thread. The UI thread should join m_thread now. } -void BackgroundSlicingProcess::thread_proc_safe() +#ifdef _WIN32 +// Only these SEH exceptions will be catched and turned into Slic3r::HardCrash C++ exceptions. +static bool is_win32_seh_harware_exception(unsigned long ex) throw() { + return + ex == STATUS_ACCESS_VIOLATION || + ex == STATUS_DATATYPE_MISALIGNMENT || + ex == STATUS_FLOAT_DIVIDE_BY_ZERO || + ex == STATUS_FLOAT_OVERFLOW || + ex == STATUS_FLOAT_UNDERFLOW || +#ifdef STATUS_FLOATING_RESEVERED_OPERAND + ex == STATUS_FLOATING_RESEVERED_OPERAND || +#endif // STATUS_FLOATING_RESEVERED_OPERAND + ex == STATUS_ILLEGAL_INSTRUCTION || + ex == STATUS_PRIVILEGED_INSTRUCTION || + ex == STATUS_INTEGER_DIVIDE_BY_ZERO || + ex == STATUS_INTEGER_OVERFLOW || + ex == STATUS_STACK_OVERFLOW; +} + +// Rethrow some SEH exceptions as Slic3r::HardCrash C++ exceptions. +static void rethrow_seh_exception(unsigned long win32_seh_catched) +{ + if (win32_seh_catched) { + // Rethrow SEH exception as Slicer::HardCrash. + if (win32_seh_catched == STATUS_ACCESS_VIOLATION || win32_seh_catched == STATUS_DATATYPE_MISALIGNMENT) + throw Slic3r::HardCrash(_u8L("Access violation")); + if (win32_seh_catched == STATUS_ILLEGAL_INSTRUCTION || win32_seh_catched == STATUS_PRIVILEGED_INSTRUCTION) + throw Slic3r::HardCrash(_u8L("Illegal instruction")); + if (win32_seh_catched == STATUS_FLOAT_DIVIDE_BY_ZERO || win32_seh_catched == STATUS_INTEGER_DIVIDE_BY_ZERO) + throw Slic3r::HardCrash(_u8L("Divide by zero")); + if (win32_seh_catched == STATUS_FLOAT_OVERFLOW || win32_seh_catched == STATUS_INTEGER_OVERFLOW) + throw Slic3r::HardCrash(_u8L("Overflow")); + if (win32_seh_catched == STATUS_FLOAT_UNDERFLOW) + throw Slic3r::HardCrash(_u8L("Underflow")); +#ifdef STATUS_FLOATING_RESEVERED_OPERAND + if (win32_seh_catched == STATUS_FLOATING_RESEVERED_OPERAND) + throw Slic3r::HardCrash(_u8L("Floating reserved operand")); +#endif // STATUS_FLOATING_RESEVERED_OPERAND + if (win32_seh_catched == STATUS_STACK_OVERFLOW) + throw Slic3r::HardCrash(_u8L("Stack overflow")); + } +} + +// Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function. +unsigned long BackgroundSlicingProcess::call_process_seh(std::exception_ptr &ex) throw() +{ + unsigned long win32_seh_catched = 0; + __try { + this->call_process(ex); + } __except (is_win32_seh_harware_exception(GetExceptionCode())) { + win32_seh_catched = GetExceptionCode(); + } + return win32_seh_catched; +} +void BackgroundSlicingProcess::call_process_seh_throw(std::exception_ptr &ex) throw() +{ + unsigned long win32_seh_catched = this->call_process_seh(ex); + if (win32_seh_catched) { + // Rethrow SEH exception as Slicer::HardCrash. + try { + rethrow_seh_exception(win32_seh_catched); + } catch (...) { + ex = std::current_exception(); + } + } +} +#endif // _WIN32 + +void BackgroundSlicingProcess::call_process(std::exception_ptr &ex) throw() +{ + try { + assert(m_print != nullptr); + switch (m_print->technology()) { + case ptFFF: this->process_fff(); break; + case ptSLA: this->process_sla(); break; + default: m_print->process(); break; + } + } catch (CanceledException& /* ex */) { + // Canceled, this is all right. + assert(m_print->canceled()); + ex = std::current_exception(); + } catch (...) { + ex = std::current_exception(); + } +} + +#ifdef _WIN32 +unsigned long BackgroundSlicingProcess::thread_proc_safe_seh() throw() +{ + unsigned long win32_seh_catched = 0; + __try { + this->thread_proc_safe(); + } __except (is_win32_seh_harware_exception(GetExceptionCode())) { + win32_seh_catched = GetExceptionCode(); + } + return win32_seh_catched; +} +void BackgroundSlicingProcess::thread_proc_safe_seh_throw() throw() +{ + unsigned long win32_seh_catched = this->thread_proc_safe_seh(); + if (win32_seh_catched) { + // Rethrow SEH exception as Slicer::HardCrash. + try { + rethrow_seh_exception(win32_seh_catched); + } catch (...) { + wxTheApp->OnUnhandledException(); + } + } +} +#endif // _WIN32 + +void BackgroundSlicingProcess::thread_proc_safe() throw() { try { this->thread_proc(); @@ -349,7 +456,13 @@ bool BackgroundSlicingProcess::start() if (m_state == STATE_INITIAL) { // The worker thread is not running yet. Start it. assert(! m_thread.joinable()); - m_thread = create_thread([this]{this->thread_proc_safe();}); + m_thread = create_thread([this]{ +#ifdef _WIN32 + this->thread_proc_safe_seh_throw(); +#else // _WIN32 + this->thread_proc_safe(); +#endif // _WIN32 + }); // Wait until the worker thread is ready to execute the background processing task. m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); } diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 12bf6fe02..6f5cd8852 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -174,7 +174,16 @@ public: private: void thread_proc(); - void thread_proc_safe(); + // Calls thread_proc(), catches all C++ exceptions and shows them using wxApp::OnUnhandledException(). + void thread_proc_safe() throw(); +#ifdef _WIN32 + // Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function. + // Catch a SEH exception and return its ID or zero if no SEH exception has been catched. + unsigned long thread_proc_safe_seh() throw(); + // Calls thread_proc_safe_seh(), rethrows a Slic3r::HardCrash exception based on SEH exception + // returned by thread_proc_safe_seh() and lets wxApp::OnUnhandledException() display it. + void thread_proc_safe_seh_throw() throw(); +#endif // _WIN32 void join_background_thread(); // To be called by Print::apply() through the Print::m_cancel_callback to stop the background // processing before changing any data of running or finalized milestones. @@ -187,6 +196,20 @@ private: // Temporary: for mimicking the fff file export behavior with the raster output void process_sla(); + // Call Print::process() and catch all exceptions into ex, thus no exception could be thrown + // by this method. This exception behavior is required to combine C++ exceptions with Win32 SEH exceptions + // on the same thread. + void call_process(std::exception_ptr &ex) throw(); + +#ifdef _WIN32 + // Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function. + // Catch a SEH exception and return its ID or zero if no SEH exception has been catched. + unsigned long call_process_seh(std::exception_ptr &ex) throw(); + // Calls call_process_seh(), rethrows a Slic3r::HardCrash exception based on SEH exception + // returned by call_process_seh(). + void call_process_seh_throw(std::exception_ptr &ex) throw(); +#endif // _WIN32 + // Currently active print. It is one of m_fff_print and m_sla_print. PrintBase *m_print = nullptr; // Non-owned pointers to Print instances.