PrusaSlicer-NonPlainar/src/slic3r/GUI/Jobs/BoostThreadWorker.hpp
2022-01-11 11:26:26 +01:00

140 lines
4.9 KiB
C++

#ifndef BOOSTTHREADWORKER_HPP
#define BOOSTTHREADWORKER_HPP
#include <boost/variant.hpp>
#include "Worker.hpp"
#include <libslic3r/Thread.hpp>
#include <boost/log/trivial.hpp>
#include "ThreadSafeQueue.hpp"
namespace Slic3r { namespace GUI {
// An implementation of the Worker interface which uses the boost::thread
// API and two thread safe message queues to communicate with the main thread
// back and forth. The queue from the main thread to the worker thread holds the
// job entries that will be performed on the worker. The other queue holds messages
// from the worker to the main thread. These messages include status updates,
// finishing operation and arbitrary functiors that need to be performed
// on the main thread during the jobs execution, like displaying intermediate
// results.
class BoostThreadWorker : public Worker, private Job::Ctl
{
struct JobEntry // Goes into worker and also out of worker as a finalize msg
{
std::unique_ptr<Job> job;
bool canceled = false;
std::exception_ptr eptr = nullptr;
};
// A message data for status updates. Only goes from worker to main thread.
struct StatusInfo { int status; std::string msg; };
// An arbitrary callback to be called on the main thread. Only from worker
// to main thread.
struct MainThreadCallData
{
std::function<void()> fn;
std::promise<void> promise;
};
struct EmptyMessage {};
class WorkerMessage
{
public:
enum MsgType { Empty, Status, Finalize, MainThreadCall };
private:
boost::variant<EmptyMessage, StatusInfo, JobEntry, MainThreadCallData> m_data;
public:
WorkerMessage() = default;
WorkerMessage(int s, std::string txt)
: m_data{StatusInfo{s, std::move(txt)}}
{}
WorkerMessage(JobEntry &&entry) : m_data{std::move(entry)} {}
WorkerMessage(MainThreadCallData fn) : m_data{std::move(fn)} {}
int get_type () const { return m_data.which(); }
void deliver(BoostThreadWorker &runner);
};
using JobQueue = ThreadSafeQueueSPSC<JobEntry>;
using MessageQueue = ThreadSafeQueueSPSC<WorkerMessage>;
boost::thread m_thread;
std::atomic<bool> m_running{false}, m_canceled{false};
std::shared_ptr<ProgressIndicator> m_progress;
JobQueue m_input_queue; // from main thread to worker
MessageQueue m_output_queue; // form worker to main thread
std::string m_name;
void run();
bool join(int timeout_ms = 0);
protected:
// Implement Job::Ctl interface:
void update_status(int st, const std::string &msg = "") override;
bool was_canceled() const override { return m_canceled.load(); }
std::future<void> call_on_main_thread(std::function<void()> fn) override;
public:
explicit BoostThreadWorker(std::shared_ptr<ProgressIndicator> pri,
boost::thread::attributes & attr,
const char * name = "");
explicit BoostThreadWorker(std::shared_ptr<ProgressIndicator> pri,
boost::thread::attributes && attr,
const char * name = "")
: BoostThreadWorker{std::move(pri), attr, name}
{}
explicit BoostThreadWorker(std::shared_ptr<ProgressIndicator> pri,
const char * name = "")
: BoostThreadWorker{std::move(pri), {}, name}
{}
~BoostThreadWorker();
BoostThreadWorker(const BoostThreadWorker &) = delete;
BoostThreadWorker(BoostThreadWorker &&) = delete;
BoostThreadWorker &operator=(const BoostThreadWorker &) = delete;
BoostThreadWorker &operator=(BoostThreadWorker &&) = delete;
bool push(std::unique_ptr<Job> job) override;
bool is_idle() const override
{
// The assumption is that jobs can only be queued from a single main
// thread from which this method is also called. And the output
// messages are also processed only in this calling thread. In that
// case, if the input queue is empty, it will remain so during this
// function call. If the worker thread is also not running and the
// output queue is already processed, we can safely say that the
// worker is dormant.
return m_input_queue.empty() && !m_running.load() && m_output_queue.empty();
}
void cancel() override { m_canceled.store(true); }
void cancel_all() override { m_input_queue.clear(); cancel(); }
ProgressIndicator * get_pri() { return m_progress.get(); }
const ProgressIndicator * get_pri() const { return m_progress.get(); }
void process_events() override;
bool wait_for_current_job(unsigned timeout_ms = 0) override;
bool wait_for_idle(unsigned timeout_ms = 0) override;
};
}} // namespace Slic3r::GUI
#endif // BOOSTTHREADWORKER_HPP