#include #include #include "Job.hpp" #include #include namespace Slic3r { void GUI::Job::run(std::exception_ptr &eptr) { m_running.store(true); try { process(); } catch (...) { eptr = std::current_exception(); } m_running.store(false); // ensure to call the last status to finalize the job update_status(status_range(), ""); } void GUI::Job::update_status(int st, const wxString &msg) { auto evt = new wxThreadEvent(wxEVT_THREAD, m_thread_evt_id); evt->SetInt(st); evt->SetString(msg); wxQueueEvent(this, evt); } void GUI::Job::update_percent_finish() { m_progress->clear_percent(); } void GUI::Job::show_error_info(wxString msg, int code, wxString description, wxString extra) { m_progress->show_error_info(msg, code, description, extra); } GUI::Job::Job(std::shared_ptr pri) : m_progress(std::move(pri)) { m_thread_evt_id = wxNewId(); Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { if (m_finalizing) return; auto msg = evt.GetString(); if (!msg.empty() && !m_worker_error) m_progress->set_status_text(msg.ToUTF8().data()); if (m_finalized) return; m_progress->set_progress(evt.GetInt()); if (evt.GetInt() == status_range() || m_worker_error) { // set back the original range and cancel callback m_progress->set_range(m_range); // Make sure progress indicators get the last value of their range // to make sure they close, fade out, whathever m_progress->set_progress(m_range); m_progress->set_cancel_callback(); wxEndBusyCursor(); if (m_worker_error) { m_finalized = true; m_progress->set_status_text(""); m_progress->set_progress(m_range); on_exception(m_worker_error); } else { // This is an RAII solution to remember that finalization is // running. The run method calls update_status(status_range(), "") // at the end, which queues up a call to this handler in all cases. // If process also calls update_status with maxed out status arg // it will call this handler twice. It is not a problem unless // yield is called inside the finilize() method, which would // jump out of finalize and call this handler again. struct Finalizing { bool &flag; Finalizing (bool &f): flag(f) { flag = true; } ~Finalizing() { flag = false; } } fin(m_finalizing); finalize(); } // dont do finalization again for the same process m_finalized = true; } }, m_thread_evt_id); } void GUI::Job::start() { // Start the job. No effect if the job is already running if (!m_running.load()) { prepare(); // Save the current status indicatior range and push the new one m_range = m_progress->get_range(); m_progress->set_range(status_range()); // init cancellation flag and set the cancel callback m_canceled.store(false); m_progress->set_cancel_callback( [this]() { m_canceled.store(true); }); m_finalized = false; m_finalizing = false; // Changing cursor to busy wxBeginBusyCursor(); try { // Execute the job m_worker_error = nullptr; m_thread = create_thread([this] { this->run(m_worker_error); }); } catch (std::exception &) { update_status(status_range(), _(L("Error! Unable to create thread!"))); } // The state changes will be undone when the process hits the // last status value, in the status update handler (see ctor) } } bool GUI::Job::join(int timeout_ms) { if (!m_thread.joinable()) return true; if (timeout_ms <= 0) m_thread.join(); else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) return false; return true; } void GUI::ExclusiveJobGroup::start(size_t jid) { assert(jid < m_jobs.size()); stop_all(); m_jobs[jid]->start(); } void GUI::ExclusiveJobGroup::join_all(int wait_ms) { std::vector aborted(m_jobs.size(), false); for (size_t jid = 0; jid < m_jobs.size(); ++jid) aborted[jid] = m_jobs[jid]->join(wait_ms); if (!std::all_of(aborted.begin(), aborted.end(), [](bool t) { return t; })) BOOST_LOG_TRIVIAL(error) << "Could not abort a job!"; } bool GUI::ExclusiveJobGroup::is_any_running() const { return std::any_of(m_jobs.begin(), m_jobs.end(), [](const std::unique_ptr &j) { return j->is_running(); }); } }