#ifndef PRUSALSICER_WORKER_HPP #define PRUSALSICER_WORKER_HPP #include #include "JobNew.hpp" namespace Slic3r { namespace GUI { // #define EXECUTE_UPDATE_ON_MAIN_THREAD // debug execution on main thread // An interface of a worker that runs jobs on a dedicated worker thread, one // after the other. It is assumed that every method of this class is called // from the same main thread. #ifdef EXECUTE_UPDATE_ON_MAIN_THREAD namespace { // Run Job on main thread (blocking) - ONLY DEBUG static inline bool execute_job(std::shared_ptr j) { struct MyCtl : public JobNew::Ctl { void update_status(int st, const std::string &msg = "") override{}; bool was_canceled() const override { return false; } std::future call_on_main_thread(std::function fn) override { return std::future{}; } } ctl; j->process(ctl); wxGetApp().plater()->CallAfter([j]() { std::exception_ptr e_ptr = nullptr; j->finalize(false, e_ptr); }); return true; } } // namespace #endif class Worker { public: // Queue up a new job after the current one. This call does not block. // Returns false if the job gets discarded. virtual bool push(std::unique_ptr job) = 0; // Returns true if no job is running, the job queue is empty and no job // message is left to be processed. This means that nothing is left to // finalize or take care of in the main thread. virtual bool is_idle() const = 0; // Ask the current job gracefully to cancel. This call is not blocking and // the job may or may not cancel eventually, depending on its // implementation. Note that it is not trivial to kill a thread forcefully // and we don't need that. virtual void cancel() = 0; // This method will delete the queued jobs and cancel the current one. virtual void cancel_all() = 0; // Needs to be called continuously to process events (like status update // or finalizing of jobs) in the main thread. This can be done e.g. in a // wxIdle handler. virtual void process_events() = 0; // Wait until the current job finishes. Timeout will only be considered // if not zero. Returns false if timeout is reached but the job has not // finished. virtual bool wait_for_current_job(unsigned timeout_ms = 0) = 0; // Wait until the whole job queue finishes. Timeout will only be considered // if not zero. Returns false only if timeout is reached but the worker has // not reached the idle state. virtual bool wait_for_idle(unsigned timeout_ms = 0) = 0; // The destructor shall properly close the worker thread. virtual ~Worker() = default; }; template constexpr bool IsProcessFn = std::is_invocable_v; template constexpr bool IsFinishFn = std::is_invocable_v; // Helper function to use the worker with arbitrary functors. template>, class = std::enable_if_t> > bool queue_job(Worker &w, ProcessFn fn, FinishFn finishfn) { struct LambdaJob: JobNew { ProcessFn fn; FinishFn finishfn; LambdaJob(ProcessFn pfn, FinishFn ffn) : fn{std::move(pfn)}, finishfn{std::move(ffn)} {} void process(Ctl &ctl) override { fn(ctl); } void finalize(bool canceled, std::exception_ptr &eptr) override { finishfn(canceled, eptr); } }; auto j = std::make_unique(std::move(fn), std::move(finishfn)); return w.push(std::move(j)); } template>> bool queue_job(Worker &w, ProcessFn fn) { return queue_job(w, std::move(fn), [](bool, std::exception_ptr &) {}); } inline bool queue_job(Worker &w, std::unique_ptr j) { return w.push(std::move(j)); } // Replace the current job queue with a new job. The signature is the same // as for queue_job(). This cancels all jobs and // will not wait. The new job will begin after the queue cancels properly. // Note that this can be called from the UI thread and will not block it if // the jobs take longer to cancel. template bool replace_job(Worker &w, Args&& ...args) { w.cancel_all(); return queue_job(w, std::forward(args)...); } // Cancel the current job and wait for it to actually be stopped. inline bool stop_current_job(Worker &w, unsigned timeout_ms = 0) { w.cancel(); return w.wait_for_current_job(timeout_ms); } // Cancel all pending jobs including current one and wait until the worker // becomes idle. inline bool stop_queue(Worker &w, unsigned timeout_ms = 0) { w.cancel_all(); return w.wait_for_idle(timeout_ms); } }} // namespace Slic3r::GUI #endif // WORKER_HPP