diff options
Diffstat (limited to 'src/core/thread/Task.cpp')
-rw-r--r-- | src/core/thread/Task.cpp | 370 |
1 files changed, 199 insertions, 171 deletions
diff --git a/src/core/thread/Task.cpp b/src/core/thread/Task.cpp index 7173b69e..dc0cfe94 100644 --- a/src/core/thread/Task.cpp +++ b/src/core/thread/Task.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -19,209 +19,237 @@ * The initial version of the source code is inherited from * the gpg4usb project, which is under GPL-3.0-or-later. * - * The source code version of this software was modified and released - * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021. + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later * */ #include "core/thread/Task.h" -#include <boost/uuid/uuid.hpp> -#include <boost/uuid/uuid_generators.hpp> -#include <boost/uuid/uuid_io.hpp> -#include <functional> -#include <string> -#include <utility> +#include <qscopedpointer.h> -#include "core/thread/TaskRunner.h" +#include "utils/MemoryUtils.h" -const std::string GpgFrontend::Thread::Task::DEFAULT_TASK_NAME = "default-task"; +namespace GpgFrontend::Thread { -GpgFrontend::Thread::Task::Task(std::string name) - : uuid_(generate_uuid()), name_(name) { - SPDLOG_TRACE("task {}/ created", GetFullID()); - init(); -} +class Task::Impl { + public: + Impl(Task *parent, QString name) + : parent_(parent), uuid_(generate_uuid()), name_(std::move(name)) { + GF_CORE_LOG_TRACE("task {} created", GetFullID()); + init(); + } -GpgFrontend::Thread::Task::Task(TaskRunnable runnable, std::string name, - DataObjectPtr data_object, bool sequency) - : uuid_(generate_uuid()), - name_(name), - runnable_(std::move(runnable)), - callback_(std::move([](int, const std::shared_ptr<DataObject> &) {})), - callback_thread_(QThread::currentThread()), - data_object_(data_object), - sequency_(sequency) { - SPDLOG_TRACE("task {} created with runnable, callback_thread_: {}", - GetFullID(), static_cast<void *>(callback_thread_)); - init(); -} + Impl(Task *parent, TaskRunnable runnable, QString name, + DataObjectPtr data_object) + : parent_(parent), + uuid_(generate_uuid()), + name_(std::move(name)), + runnable_(std::move(runnable)), + callback_([](int, const DataObjectPtr &) {}), + callback_thread_(QThread::currentThread()), + data_object_(std::move(data_object)) { + GF_CORE_LOG_TRACE("task {} created with runnable, callback_thread_: {}", + GetFullID(), static_cast<void *>(callback_thread_)); + init(); + } -GpgFrontend::Thread::Task::Task(TaskRunnable runnable, std::string name, - DataObjectPtr data_object, - TaskCallback callback, bool sequency) - : uuid_(generate_uuid()), - name_(name), - runnable_(std::move(runnable)), - callback_(std::move(callback)), - callback_thread_(QThread::currentThread()), - data_object_(data_object), - sequency_(sequency) { - init(); - SPDLOG_TRACE( - "task {} created with runnable and callback, callback_thread_: {}", - GetFullID(), static_cast<void *>(callback_thread_)); -} + Impl(Task *parent, TaskRunnable runnable, QString name, + DataObjectPtr data_object, TaskCallback callback) + : parent_(parent), + uuid_(generate_uuid()), + name_(std::move(name)), + runnable_(std::move(runnable)), + callback_(std::move(callback)), + callback_thread_(QThread::currentThread()), + data_object_(std::move(data_object)) { + GF_CORE_LOG_TRACE( + "task {} created with runnable and callback, callback_thread_: {}", + GetFullID(), static_cast<void *>(callback_thread_)); + init(); + } -GpgFrontend::Thread::Task::~Task() { - SPDLOG_TRACE("task {} destroyed", GetFullID()); -} + ~Impl() { GF_CORE_LOG_TRACE("task {} destroyed", GetFullID()); } + + /** + * @brief + * + * @return QString + */ + [[nodiscard]] auto GetFullID() const -> QString { + return uuid_ + "/" + name_; + } + + /** + * @brief + * + * @return QString + */ + [[nodiscard]] auto GetUUID() const -> QString { return uuid_; } + + /** + * @brief + * + * @return int + */ + auto Run() -> int { + GF_CORE_LOG_TRACE("task {} is in classical runnable and callback mode", + GetFullID()); + + if (runnable_) return runnable_(data_object_); + + GF_CORE_LOG_WARN("no runnable in task, do callback operation, task: {}", + GetFullID()); + return 0; + } + + /** + * @brief Set the Finish After Run object + * + * @param finish_after_run + */ + void HoldOnLifeCycle(bool hold_on) { parent_->setAutoDelete(!hold_on); } + + /** + * @brief + * + * @param rtn + */ + void SetRTN(int rtn) { this->rtn_ = rtn; } + + /** + * @brief + * + * @return auto + */ + [[nodiscard]] auto GetRTN() const { return this->rtn_; } + + private: + Task *const parent_; + const QString uuid_; + const QString name_; + TaskRunnable runnable_; ///< + TaskCallback callback_; ///< + int rtn_ = -99; ///< + QThread *callback_thread_ = nullptr; ///< + DataObjectPtr data_object_ = nullptr; ///< + + void init() { + GF_CORE_LOG_TRACE("task {} created, parent: {}, impl: {}", name_, + static_cast<void *>(parent_), static_cast<void *>(this)); + + // + HoldOnLifeCycle(false); + + // + connect(parent_, &Task::SignalRun, parent_, &Task::slot_exception_safe_run); + + auto *callback_thread = callback_thread_ != nullptr + ? callback_thread_ + : QCoreApplication::instance()->thread(); + // + connect(parent_, &Task::SignalTaskShouldEnd, callback_thread, + [this](int rtn) { + // set task returning code + SetRTN(rtn); + try { + if (callback_) { + GF_CORE_LOG_TRACE( + "task callback {} is starting with runnerable rtn: {}", + GetFullID(), rtn); + + callback_(rtn_, data_object_); + GF_CORE_LOG_TRACE("task callback {} finished, rtn: {}", + GetFullID(), rtn); + } + } catch (...) { + GF_CORE_LOG_ERROR("task {} callback caught exception, rtn: {}", + GetFullID(), rtn); + } + emit parent_->SignalTaskEnd(); + }); + + // + connect(parent_, &Task::SignalTaskEnd, parent_, &Task::deleteLater); + } + + /** + * @brief + * + * @return QString + */ + static auto generate_uuid() -> QString { + return QUuid::createUuid().toString(); + } +}; + +Task::Task(QString name) : p_(new Impl(this, name)) {} + +Task::Task(TaskRunnable runnable, QString name, DataObjectPtr data_object) + : p_(SecureCreateUniqueObject<Impl>(this, runnable, name, data_object)) {} + +Task::Task(TaskRunnable runnable, QString name, DataObjectPtr data_object, + TaskCallback callback) + : p_(SecureCreateUniqueObject<Impl>(this, runnable, name, data_object, + callback)) {} + +Task::~Task() = default; /** * @brief * - * @return std::string + * @return QString */ -std::string GpgFrontend::Thread::Task::GetFullID() const { - return uuid_ + "/" + name_; -} - -std::string GpgFrontend::Thread::Task::GetUUID() const { return uuid_; } +QString Task::GetFullID() const { return p_->GetFullID(); } -bool GpgFrontend::Thread::Task::GetSequency() const { return sequency_; } +QString Task::GetUUID() const { return p_->GetUUID(); } -void GpgFrontend::Thread::Task::SetFinishAfterRun( - bool run_callback_after_runnable_finished) { - this->run_callback_after_runnable_finished_ = - run_callback_after_runnable_finished; -} +void Task::HoldOnLifeCycle(bool hold_on) { p_->HoldOnLifeCycle(hold_on); } -void GpgFrontend::Thread::Task::SetRTN(int rtn) { this->rtn_ = rtn; } - -void GpgFrontend::Thread::Task::init() { - // after runnable finished, running callback - connect(this, &Task::SignalTaskRunnableEnd, this, - &Task::slot_task_run_callback); -} +void Task::setRTN(int rtn) { p_->SetRTN(rtn); } -void GpgFrontend::Thread::Task::slot_task_run_callback(int rtn) { - SPDLOG_TRACE("task runnable {} finished, rtn: {}", GetFullID(), rtn); - // set return value - this->SetRTN(rtn); +void Task::SafelyRun() { emit SignalRun(); } - try { - if (callback_) { - if (callback_thread_ == QThread::currentThread()) { - SPDLOG_DEBUG("callback thread is the same thread"); - if (!QMetaObject::invokeMethod(callback_thread_, - [callback = callback_, rtn = rtn_, - data_object = data_object_, this]() { - callback(rtn, data_object); - // do cleaning work - emit SignalTaskEnd(); - })) { - SPDLOG_ERROR("failed to invoke callback"); - } - // just finished, let callack thread to raise SignalTaskEnd - return; - } else { - // waiting for callback to finish - if (!QMetaObject::invokeMethod( - callback_thread_, - [callback = callback_, rtn = rtn_, - data_object = data_object_]() { callback(rtn, data_object); }, - Qt::BlockingQueuedConnection)) { - SPDLOG_ERROR("failed to invoke callback"); - } - } - } - } catch (std::exception &e) { - SPDLOG_ERROR("exception caught: {}", e.what()); - } catch (...) { - SPDLOG_ERROR("unknown exception caught"); - } +int Task::Run() { return p_->Run(); } - // raise signal, announcing this task come to an end - SPDLOG_DEBUG("task {}, starting calling signal SignalTaskEnd", GetFullID()); - emit SignalTaskEnd(); +void Task::run() { + GF_CORE_LOG_TRACE("interface run() of task {} was called by thread: {}", + GetFullID(), QThread::currentThread()->currentThreadId()); + this->SafelyRun(); } -void GpgFrontend::Thread::Task::run() { - SPDLOG_TRACE("task {} starting", GetFullID()); +Task::TaskHandler::TaskHandler(Task *task) : task_(task) {} - // build runnable package for running - auto runnable_package = [=, id = GetFullID()]() { - SPDLOG_DEBUG("task {} runnable start runing", id); - // Run() will set rtn by itself - Run(); - // raise signal to anounce after runnable returned - if (run_callback_after_runnable_finished_) emit SignalTaskRunnableEnd(rtn_); - }; - - if (thread() != QThread::currentThread()) { - SPDLOG_DEBUG("task running thread is not object living thread"); - // if running sequently - if (sequency_) { - // running in another thread, blocking until returned - if (!QMetaObject::invokeMethod(thread(), runnable_package, - Qt::BlockingQueuedConnection)) { - SPDLOG_ERROR("qt invoke method failed"); - } - } else { - // running in another thread, non-blocking - if (!QMetaObject::invokeMethod(thread(), runnable_package)) { - SPDLOG_ERROR("qt invoke method failed"); - } - } - } else { - if (!QMetaObject::invokeMethod(this, runnable_package)) { - SPDLOG_ERROR("qt invoke method failed"); - } - } +void Task::TaskHandler::Start() { + if (task_ != nullptr) task_->SafelyRun(); } -void GpgFrontend::Thread::Task::SlotRun() { run(); } - -void GpgFrontend::Thread::Task::Run() { - if (runnable_) { - SetRTN(runnable_(data_object_)); - } else { - SPDLOG_WARN("no runnable in task, do callback operation"); - } +void Task::TaskHandler::Cancel() { + if (task_ != nullptr) emit task_->SignalTaskEnd(); } -GpgFrontend::Thread::Task::DataObject::Destructor * -GpgFrontend::Thread::Task::DataObject::get_heap_ptr(size_t bytes_size) { - Destructor *dstr_ptr = new Destructor(); - dstr_ptr->p_obj = malloc(bytes_size); - return dstr_ptr; +auto Task::TaskHandler::GetTask() -> Task * { + if (task_ != nullptr) return task_; + return nullptr; } -GpgFrontend::Thread::Task::DataObject::~DataObject() { - if (!data_objects_.empty()) - SPDLOG_WARN("data_objects_ is not empty", - "address:", static_cast<void *>(this)); - while (!data_objects_.empty()) { - free_heap_ptr(data_objects_.top()); - data_objects_.pop(); - } -} +void Task::slot_exception_safe_run() noexcept { + auto rtn = p_->GetRTN(); + try { + GF_CORE_LOG_TRACE("task runnable {} is starting...", GetFullID()); -size_t GpgFrontend::Thread::Task::DataObject::GetObjectSize() { - return data_objects_.size(); -} + // Run() will set rtn by itself + rtn = this->Run(); -void GpgFrontend::Thread::Task::DataObject::free_heap_ptr(Destructor *ptr) { - SPDLOG_TRACE("p_obj: {} data object: {}", - static_cast<const void *>(ptr->p_obj), - static_cast<void *>(this)); - if (ptr->destroy != nullptr) { - ptr->destroy(ptr->p_obj); + GF_CORE_LOG_TRACE("task runnable {} finished, rtn: {}", GetFullID()); + } catch (...) { + GF_CORE_LOG_ERROR("exception was caught at task: {}", GetFullID()); } - free(const_cast<void *>(ptr->p_obj)); - delete ptr; -} -std::string GpgFrontend::Thread::Task::generate_uuid() { - return boost::uuids::to_string(boost::uuids::random_generator()()); + // raise signal to anounce after runnable returned + if (this->autoDelete()) emit this->SignalTaskShouldEnd(rtn); } +auto Task::GetRTN() { return p_->GetRTN(); } +} // namespace GpgFrontend::Thread
\ No newline at end of file |