Create Stopable job

This commit is contained in:
Filip Sykala 2021-11-24 10:20:37 +01:00
parent 205f8cf86d
commit bba9dab8c8
5 changed files with 198 additions and 157 deletions

View file

@ -208,7 +208,7 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent)
}
GLGizmoEmboss::~GLGizmoEmboss() {}
GLGizmoEmboss::~GLGizmoEmboss() { m_job->stop(); }
void GLGizmoEmboss::set_fine_position()
{
@ -333,6 +333,8 @@ void GLGizmoEmboss::on_set_state()
return;
}
m_volume = nullptr;
m_job->stop();
m_job->join(); // free thread resource
remove_notification_not_valid_font();
} else if (GLGizmoBase::m_state == GLGizmoBase::On) {
if (!m_is_initialized) initialize();
@ -478,7 +480,7 @@ bool GLGizmoEmboss::process()
data->volume_name = create_volume_name();
data->volume_ptr = m_volume;
data->object_idx = m_parent.get_selection().get_object_idx();
m_job->re_run(std::move(data));
m_job->run(std::move(data));
// notification is removed befor object is changed by job
remove_notification_not_valid_font();

View file

@ -13,7 +13,6 @@
using namespace Slic3r;
using namespace GUI;
namespace Priv {
static void process(std::unique_ptr<EmbossData> input, StopCondition is_stop);
@ -28,6 +27,10 @@ EmbossJob::EmbossJob() : StopableJob<EmbossData>(Priv::process) {}
void Priv::process(std::unique_ptr<EmbossData> input, StopCondition is_stop)
{
// Changing cursor to busy
wxBeginBusyCursor();
ScopeGuard sg([]() { wxEndBusyCursor(); });
// only for sure
assert(input != nullptr);

View file

@ -1,11 +1,7 @@
#ifndef slic3r_EmbossJob_hpp_
#define slic3r_EmbossJob_hpp_
#include <memory>
#include <thread>
#include <mutex>
#include "Job.hpp"
#include "StopableJob.hpp"
#include "libslic3r/Emboss.hpp"
namespace Slic3r {
@ -14,155 +10,6 @@ class ModelVolume;
namespace Slic3r::GUI {
// inspired by Job.hpp
// All public function can be call only from UI thread
// Mechanism to stack processing and do only last one
template<typename TIn>
class ReRunJob
{
std::mutex m_next_mutex;
std::unique_ptr<TIn> m_input_next = nullptr;
std::thread m_thread;
// indicate checking of new input.
std::atomic<bool> m_running{false};
using Func = std::function<void(std::unique_ptr<TIn>)>;
Func m_func;
public:
ReRunJob(Func func) : m_func(func) {}
virtual ~ReRunJob() { join(); }
void re_run(std::unique_ptr<TIn> input)
{
if (input == nullptr) return;
{
// keep access to next input
std::lock_guard lg(m_next_mutex);
if (is_running()) {
// when runnig
m_input_next = std::move(input);
return; // on end of run will be used new input
}
m_running.store(true);
}
if (m_thread.joinable()) m_thread.join();
// at this moment is not running --> stoped
assert(m_input_next == nullptr);
try { // Execute the job
m_thread = std::thread(
[this](std::unique_ptr<TIn> input) {
do {
m_func(std::move(input));
std::lock_guard lg(m_next_mutex);
// it is not in while condition because of lock guard
if (m_input_next == nullptr) {
m_running.store(false);
return;
}
input = std::move(m_input_next);
m_input_next = nullptr;
} while (true);
},
std::move(input));
} catch (std::exception &) {}
}
bool is_running() const { return m_running.load(); }
void join()
{
if (m_thread.joinable()) m_thread.join();
assert(!is_running());
}
};
using StopCondition = std::function<bool(void)>;
// add stop ability
template<typename TIn> class StopableJob
{
std::mutex m_mutex;
std::unique_ptr<TIn> m_input_next = nullptr;
std::thread m_thread;
// indicate checking of new input.
bool m_running = false;
bool m_stop = false;
using Func = std::function<void(std::unique_ptr<TIn>, StopCondition)>;
Func m_func;
public:
StopableJob(Func func) : m_func(func) {}
virtual ~StopableJob() {
stop();
join();
}
void re_run(std::unique_ptr<TIn> input)
{
if (input == nullptr) return;
{
// keep access to next input
std::lock_guard lg(m_mutex);
if (m_running) {
// when runnig
m_stop = true;
m_input_next = std::move(input);
return; // on end of run will be used new input
}
m_running = true;
m_stop = false;
}
if (m_thread.joinable()) m_thread.join();
// at this moment is not running --> stoped
assert(m_input_next == nullptr);
try { // Execute the job
m_thread = std::thread(
[this](std::unique_ptr<TIn> input) {
do {
m_func(std::move(input), [this]() { return is_stoping(); });
std::lock_guard lg(m_mutex);
m_stop = false;
// this is not while (end)condition because of lock guard
if (m_input_next == nullptr) {
m_running = false;
return;
}
input = std::move(m_input_next);
m_input_next = nullptr;
} while (true);
},
std::move(input));
} catch (std::exception &) {}
}
bool is_running()
{
std::lock_guard lg(m_mutex);
return m_running;
}
bool is_stoping()
{
std::lock_guard lg(m_mutex);
return m_stop;
}
void stop() {
std::lock_guard lg(m_mutex);
m_input_next = nullptr;
m_stop = true;
}
// blocking until stop,
void join(int timeout_ms = 0)
{
if (m_thread.joinable()) m_thread.join();
assert(!m_running);
}
};
struct EmbossData
{
// Pointer on Data of font (glyph shapes)

View file

@ -0,0 +1,83 @@
#ifndef slic3r_ReRunJob_hpp_
#define slic3r_ReRunJob_hpp_
#include <memory>
#include <atomic>
#include <mutex>
#include <thread>
namespace Slic3r::GUI {
// inspired by Job.hpp
// All public function can be call only from UI thread
// Mechanism to stack processing unique input and do only the last one
template<typename TIn>
class ReRunJob
{
std::mutex m_next_mutex;
std::unique_ptr<TIn> m_input_next = nullptr;
std::thread m_thread;
// indicate checking of new input.
std::atomic<bool> m_running{false};
using Func = std::function<void(std::unique_ptr<TIn>)>;
Func m_func;
public:
ReRunJob(Func func) : m_func(func) {}
virtual ~ReRunJob() {
try {
// thread join could throw exception
// https://en.cppreference.com/w/cpp/thread/thread/join
join();
} catch (std::system_error err) {}
}
void re_run(std::unique_ptr<TIn> input)
{
if (input == nullptr) return;
{
// keep access to next input
std::lock_guard lg(m_next_mutex);
if (is_running()) {
// when runnig
m_input_next = std::move(input);
return; // on end of run will be used new input
}
m_running.store(true);
}
if (m_thread.joinable()) m_thread.join();
// at this moment is not running --> stoped
assert(m_input_next == nullptr);
try { // Execute the job
m_thread = std::thread(
[this](std::unique_ptr<TIn> input) {
do {
m_func(std::move(input));
std::lock_guard lg(m_next_mutex);
// it is not in while condition because of lock guard
if (m_input_next == nullptr) {
m_running.store(false);
return;
}
input = std::move(m_input_next);
m_input_next = nullptr;
} while (true);
},
std::move(input));
} catch (std::exception &) {}
}
bool is_running() const { return m_running.load(); }
void join()
{
if (m_thread.joinable()) m_thread.join();
assert(!is_running());
}
};
} // namespace Slic3r::GUI
#endif // slic3r_ReRunJob_hpp_

View file

@ -0,0 +1,106 @@
#ifndef slic3r_StopableJob_hpp_
#define slic3r_StopableJob_hpp_
#include <memory>
#include <thread>
#include <mutex>
namespace Slic3r::GUI {
// inspired by Job.hpp
// All public function can be call only from UI thread
// Mechanism to stack processing and do only last one
// Ability to stop processing developer must add check into m_func
using StopCondition = std::function<bool(void)>;
template<typename TIn> class StopableJob
{
std::mutex m_mutex;
std::unique_ptr<TIn> m_input_next = nullptr;
std::thread m_thread;
// when running == true, than check of new input in thread.
bool m_running = false;
// faster interupt inside func, developer must add StopCondifion call
bool m_stop = false;
using Func = std::function<void(std::unique_ptr<TIn>, StopCondition)>;
Func m_func;
public:
StopableJob(Func func) : m_func(func) {}
virtual ~StopableJob() {
stop();
try {
// thread join could throw exception
// https://en.cppreference.com/w/cpp/thread/thread/join
join();
} catch (std::system_error err) {}
}
void run(std::unique_ptr<TIn> input)
{
if (input == nullptr) return;
{
// keep access to next input
std::lock_guard lg(m_mutex);
if (m_running) {
// when runnig
m_stop = true;
m_input_next = std::move(input);
return; // on end of run will be used new input
}
m_running = true;
m_stop = false;
}
if (m_thread.joinable()) m_thread.join();
// at this moment is not running --> stoped
assert(m_input_next == nullptr);
try { // Execute the job
m_thread = std::thread(
[this](std::unique_ptr<TIn> input) {
do {
m_func(std::move(input), [this]() { return is_stoping(); });
std::lock_guard lg(m_mutex);
m_stop = false;
// this is not while (end)condition because of lock guard
if (m_input_next == nullptr) {
m_running = false;
return;
}
input = std::move(m_input_next);
m_input_next = nullptr;
} while (true);
},
std::move(input));
} catch (std::exception &) {}
}
bool is_running()
{
std::lock_guard lg(m_mutex);
return m_running;
}
bool is_stoping()
{
std::lock_guard lg(m_mutex);
return m_stop;
}
void stop() {
std::lock_guard lg(m_mutex);
if (!m_running) return;
m_input_next = nullptr;
m_stop = true;
}
// Be Carefull, blocking until join
// call stop when you not sure
void join(int timeout_ms = 0)
{
if (m_thread.joinable()) m_thread.join();
assert(!m_running);
}
};
} // namespace Slic3r::GUI
#endif // slic3r_StopableJob_hpp_