diff options
Diffstat (limited to 'src/core/thread')
-rw-r--r-- | src/core/thread/CtxCheckTask.cpp | 54 | ||||
-rw-r--r-- | src/core/thread/CtxCheckTask.h | 63 | ||||
-rw-r--r-- | src/core/thread/FileReadTask.cpp | 60 | ||||
-rw-r--r-- | src/core/thread/FileReadTask.h | 22 | ||||
-rw-r--r-- | src/core/thread/Task.cpp | 370 | ||||
-rw-r--r-- | src/core/thread/Task.h | 223 | ||||
-rw-r--r-- | src/core/thread/TaskRunner.cpp | 212 | ||||
-rw-r--r-- | src/core/thread/TaskRunner.h | 83 | ||||
-rw-r--r-- | src/core/thread/TaskRunnerGetter.cpp | 44 | ||||
-rw-r--r-- | src/core/thread/TaskRunnerGetter.h | 32 | ||||
-rw-r--r-- | src/core/thread/ThreadingModel.h | 9 |
11 files changed, 507 insertions, 665 deletions
diff --git a/src/core/thread/CtxCheckTask.cpp b/src/core/thread/CtxCheckTask.cpp deleted file mode 100644 index 9735fcaa..00000000 --- a/src/core/thread/CtxCheckTask.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (C) 2021 Saturneric - * - * This file is part of GpgFrontend. - * - * GpgFrontend is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GpgFrontend is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. - * - * 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. - * - */ - -#include "core/thread/CtxCheckTask.h" - -#include "core/GpgContext.h" -#include "core/GpgCoreInit.h" -#include "core/common/CoreCommonUtil.h" -#include "core/function/gpg/GpgKeyGetter.h" -#include "thread/Task.h" - -GpgFrontend::Thread::CtxCheckTask::CtxCheckTask() : Task("ctx_check_task") { - connect(this, &CtxCheckTask::SignalGnupgNotInstall, - CoreCommonUtil::GetInstance(), - &CoreCommonUtil::SignalGnupgNotInstall); -} - -void GpgFrontend::Thread::CtxCheckTask::Run() { - // Init GpgFrontend Core - init_gpgfrontend_core(); - - // Create & Check Gnupg Context Status - if (!GpgContext::GetInstance().good()) { - emit SignalGnupgNotInstall(); - } - // Try flushing key cache - else - GpgFrontend::GpgKeyGetter::GetInstance().FlushKeyCache(); - - SPDLOG_DEBUG("ctx check task runnable done"); -} diff --git a/src/core/thread/CtxCheckTask.h b/src/core/thread/CtxCheckTask.h deleted file mode 100644 index 06ddfd82..00000000 --- a/src/core/thread/CtxCheckTask.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (C) 2021 Saturneric - * - * This file is part of GpgFrontend. - * - * GpgFrontend is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GpgFrontend is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. - * - * 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. - * - */ - -#ifndef GPGFRONTEND_CTXCHECKTRHEAD_H -#define GPGFRONTEND_CTXCHECKTRHEAD_H - -#include "core/GpgFrontendCore.h" -#include "core/thread/Task.h" - -namespace GpgFrontend::Thread { -/** - * @brief - * - */ -class GPGFRONTEND_CORE_EXPORT CtxCheckTask : public Task { - Q_OBJECT - public: - /** - * @brief Construct a new Ctx Check Thread object - * - */ - CtxCheckTask(); - - signals: - /** - * @brief - * - */ - void SignalGnupgNotInstall(); - - protected: - /** - * @brief - * - */ - void Run() override; -}; -} // namespace GpgFrontend::Thread - -#endif // GPGFRONTEND_CTXCHECKTRHEAD_H diff --git a/src/core/thread/FileReadTask.cpp b/src/core/thread/FileReadTask.cpp index 73954d28..49a3f540 100644 --- a/src/core/thread/FileReadTask.cpp +++ b/src/core/thread/FileReadTask.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -19,8 +19,10 @@ * 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 * */ @@ -28,58 +30,52 @@ namespace GpgFrontend::UI { -FileReadTask::FileReadTask(std::string path) : Task("file_read_task") { - connect(this, &FileReadTask::SignalFileBytesReadNext, this, - &FileReadTask::read_bytes); +constexpr size_t kBufferSize = 8192; -#ifdef WINDOWS - std::filesystem::path read_file_path( - QString::fromStdString(path).toStdU16String()); -#else - std::filesystem::path read_file_path( - QString::fromStdString(path).toStdString()); -#endif - read_file_path_ = read_file_path; +FileReadTask::FileReadTask(QString path) + : Task("file_read_task"), read_file_path_(std::move(path)) { + HoldOnLifeCycle(true); + connect(this, &FileReadTask::SignalFileBytesReadNext, this, + &FileReadTask::slot_read_bytes); } -void FileReadTask::Run() { - SetFinishAfterRun(false); - - if (is_regular_file(read_file_path_)) { - SPDLOG_DEBUG("read open file: {}", read_file_path_.u8string()); +auto FileReadTask::Run() -> int { + if (QFileInfo(read_file_path_).isFile()) { + GF_CORE_LOG_DEBUG("read open file: {}", read_file_path_); - target_file_.setFileName( - QString::fromStdString(read_file_path_.u8string())); + target_file_.setFileName(read_file_path_); target_file_.open(QIODevice::ReadOnly); if (!(target_file_.isOpen() && target_file_.isReadable())) { - SPDLOG_ERROR("file not open or not readable"); + GF_CORE_LOG_ERROR("file not open or not readable"); if (target_file_.isOpen()) target_file_.close(); - return; + return -1; } - SPDLOG_DEBUG("started reading: {}", read_file_path_.u8string()); - read_bytes(); + GF_CORE_LOG_DEBUG("started reading: {}", read_file_path_); + slot_read_bytes(); } else { emit SignalFileBytesReadEnd(); } + return 0; } -void FileReadTask::read_bytes() { +void FileReadTask::slot_read_bytes() { QByteArray read_buffer; - if (!target_file_.atEnd() && - (read_buffer = target_file_.read(buffer_size_)).size() > 0) { - SPDLOG_DEBUG("read bytes: {}", read_buffer.size()); + if (QByteArray read_buffer; + !target_file_.atEnd() && + (read_buffer = target_file_.read(kBufferSize)).size() > 0) { + GF_CORE_LOG_DEBUG("io thread read bytes: {}", read_buffer.size()); emit SignalFileBytesRead(std::move(read_buffer)); } else { - SPDLOG_DEBUG("read bytes end"); + GF_CORE_LOG_DEBUG("io thread read bytes end"); emit SignalFileBytesReadEnd(); // announce finish task - emit SignalTaskRunnableEnd(0); + emit SignalTaskShouldEnd(0); } } FileReadTask::~FileReadTask() { - SPDLOG_DEBUG("close file: {}", read_file_path_.u8string()); + GF_CORE_LOG_DEBUG("close file: {}", read_file_path_); if (target_file_.isOpen()) target_file_.close(); } diff --git a/src/core/thread/FileReadTask.h b/src/core/thread/FileReadTask.h index d4e61cbe..22be33ef 100644 --- a/src/core/thread/FileReadTask.h +++ b/src/core/thread/FileReadTask.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -19,13 +19,14 @@ * 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 * */ -#ifndef GPGFRONTEND_FILEREADTHREAD_H -#define GPGFRONTEND_FILEREADTHREAD_H +#pragma once #include "core/GpgFrontendCore.h" #include "core/thread/Task.h" @@ -39,11 +40,11 @@ namespace GpgFrontend::UI { class GPGFRONTEND_CORE_EXPORT FileReadTask : public GpgFrontend::Thread::Task { Q_OBJECT public: - explicit FileReadTask(std::string path); + explicit FileReadTask(QString path); virtual ~FileReadTask() override; - void Run() override; + int Run() override; signals: void SignalFileBytesRead(QByteArray bytes); @@ -51,15 +52,12 @@ class GPGFRONTEND_CORE_EXPORT FileReadTask : public GpgFrontend::Thread::Task { void SignalFileBytesReadNext(); private: - std::filesystem::path read_file_path_; + QString read_file_path_; QFile target_file_; - const size_t buffer_size_ = 4096; QEventLoop looper; private slots: - void read_bytes(); + void slot_read_bytes(); }; } // namespace GpgFrontend::UI - -#endif // GPGFRONTEND_FILEREADTHREAD_H 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 diff --git a/src/core/thread/Task.h b/src/core/thread/Task.h index ce354697..97a14949 100644 --- a/src/core/thread/Task.h +++ b/src/core/thread/Task.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -20,23 +20,17 @@ * the gpg4usb project, which is under GPL-3.0-or-later. * * All the source code of GpgFrontend was modified and released by - * Saturneric<[email protected]> starting on May 12, 2021. + * Saturneric <[email protected]> starting on May 12, 2021. * * SPDX-License-Identifier: GPL-3.0-or-later * */ -#ifndef GPGFRONTEND_TASK_H -#define GPGFRONTEND_TASK_H - -#include <functional> -#include <memory> -#include <stack> -#include <string> -#include <type_traits> -#include <utility> +#pragma once #include "core/GpgFrontendCore.h" +#include "core/function/SecureMemoryAllocator.h" +#include "core/model/DataObject.h" namespace GpgFrontend::Thread { @@ -45,253 +39,136 @@ class TaskRunner; class GPGFRONTEND_CORE_EXPORT Task : public QObject, public QRunnable { Q_OBJECT public: - class DataObject; - using DataObjectPtr = std::shared_ptr<DataObject>; ///< + friend class TaskRunner; + using TaskRunnable = std::function<int(DataObjectPtr)>; ///< using TaskCallback = std::function<void(int, DataObjectPtr)>; ///< - static const std::string DEFAULT_TASK_NAME; - - friend class TaskRunner; - - /** - * @brief DataObject to be passed to the callback function. - * - */ - class GPGFRONTEND_CORE_EXPORT DataObject { + class TaskHandler { public: - struct Destructor { - const void *p_obj; - void (*destroy)(const void *); - }; - - /** - * @brief Get the Objects Size - * - * @return size_t - */ - size_t GetObjectSize(); - - /** - * @brief - * - * @tparam T - * @param ptr - */ - template <typename T> - void AppendObject(T &&obj) { - SPDLOG_TRACE("append object: {}", static_cast<void *>(this)); - auto *obj_dstr = this->get_heap_ptr(sizeof(T)); - new ((void *)obj_dstr->p_obj) T(std::forward<T>(obj)); + explicit TaskHandler(Task*); - if (std::is_class_v<T>) { - auto destructor = [](const void *x) { - static_cast<const T *>(x)->~T(); - }; - obj_dstr->destroy = destructor; - } else { - obj_dstr->destroy = nullptr; - } + void Start(); - data_objects_.push(obj_dstr); - } + void Cancel(); - /** - * @brief - * - * @tparam T - * @param ptr - */ - template <typename T> - void AppendObject(T *obj) { - SPDLOG_TRACE("called: {}", static_cast<void *>(this)); - auto *obj_dstr = this->get_heap_ptr(sizeof(T)); - auto *ptr_heap = new ((void *)obj_dstr->p_obj) T(std::move(*obj)); - if (std::is_class_v<T>) { - SPDLOG_TRACE("is class"); - auto destructor = [](const void *x) { - static_cast<const T *>(x)->~T(); - }; - obj_dstr->destroy = destructor; - } else { - obj_dstr->destroy = nullptr; - } - data_objects_.push(std::move(obj_dstr)); - } - - /** - * @brief - * - * @tparam T - * @return std::shared_ptr<T> - */ - template <typename T> - T PopObject() { - SPDLOG_TRACE("pop object: {}", static_cast<void *>(this)); - if (data_objects_.empty()) throw std::runtime_error("No object to pop"); - auto *obj_dstr = data_objects_.top(); - auto *heap_ptr = (T *)obj_dstr->p_obj; - auto obj = std::move(*(T *)(heap_ptr)); - this->free_heap_ptr(obj_dstr); - data_objects_.pop(); - return obj; - } - - /** - * @brief Destroy the Data Object object - * - */ - ~DataObject(); + auto GetTask() -> Task*; private: - std::stack<Destructor *> data_objects_; ///< - - /** - * @brief Get the heap ptr object - * - * @param bytes_size - * @return void* - */ - Destructor *get_heap_ptr(size_t bytes_size); - - /** - * @brief - * - * @param heap_ptr - */ - void free_heap_ptr(Destructor *); + QPointer<Task> task_; }; /** * @brief Construct a new Task object * */ - Task(std::string name = DEFAULT_TASK_NAME); + explicit Task(QString name); /** * @brief Construct a new Task object * * @param callback The callback function to be executed. */ - explicit Task(TaskRunnable runnable, std::string name = DEFAULT_TASK_NAME, - DataObjectPtr data_object = nullptr, bool sequency = true); + explicit Task(TaskRunnable runnable, QString name, + DataObjectPtr data_object = nullptr); /** * @brief Construct a new Task object * * @param runnable */ - explicit Task( - TaskRunnable runnable, std::string name, DataObjectPtr data, - TaskCallback callback = [](int, const std::shared_ptr<DataObject> &) {}, - bool sequency = true); + explicit Task(TaskRunnable runnable, QString name, DataObjectPtr data, + TaskCallback callback); /** * @brief Destroy the Task object * */ - virtual ~Task() override; + ~Task() override; /** - * @brief Run - run the task + * @brief * + * @return QString */ - virtual void Run(); + [[nodiscard]] auto GetUUID() const -> QString; /** - * @brief + * @brief Get the Full I D object * - * @return std::string + * @return QString */ - std::string GetUUID() const; + [[nodiscard]] auto GetFullID() const -> QString; /** * @brief * - * @return std::string + * @param hold_on */ - std::string GetFullID() const; + void HoldOnLifeCycle(bool hold_on); /** - * @brief + * @brief can be overwrite by subclass * - * @return std::string + * @return int */ - bool GetSequency() const; - - public slots: + virtual auto Run() -> int; /** * @brief * + * @return auto */ - void SlotRun(); + [[nodiscard]] auto GetRTN(); - signals: - /** - * @brief announce runnable finished - * - */ - void SignalTaskRunnableEnd(int rtn); + public slots: /** - * @brief runnable and callabck all finished + * @brief shouldn't be overwrite by subclass * */ - void SignalTaskEnd(); + void SafelyRun(); - protected: - /** - * @brief Set the Finish After Run object - * - * @param finish_after_run - */ - void SetFinishAfterRun(bool finish_after_run); + signals: /** * @brief * - * @param rtn */ - void SetRTN(int rtn); - - private: - const std::string uuid_; - const std::string name_; - const bool sequency_ = true; ///< must run in the same thread - TaskCallback callback_; ///< - TaskRunnable runnable_; ///< - bool run_callback_after_runnable_finished_ = true; ///< - int rtn_ = 0; ///< - QThread *callback_thread_ = nullptr; ///< - DataObjectPtr data_object_ = nullptr; ///< + void SignalRun(); /** * @brief * */ - void init(); + void SignalTaskShouldEnd(int); /** * @brief * */ - virtual void run() override; + void SignalTaskEnd(); + protected: /** * @brief * - * @return std::string + * @param rtn */ - static std::string generate_uuid(); + void setRTN(int rtn); private slots: + /** * @brief * */ - void slot_task_run_callback(int rtn); + void slot_exception_safe_run() noexcept; + + private: + class Impl; + SecureUniquePtr<Impl> p_; + + void run() override; }; } // namespace GpgFrontend::Thread - -#endif // GPGFRONTEND_TASK_H
\ No newline at end of file diff --git a/src/core/thread/TaskRunner.cpp b/src/core/thread/TaskRunner.cpp index 461d5fb5..8e381384 100644 --- a/src/core/thread/TaskRunner.cpp +++ b/src/core/thread/TaskRunner.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -19,128 +19,132 @@ * 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/TaskRunner.h" #include "core/thread/Task.h" -#include "spdlog/spdlog.h" -GpgFrontend::Thread::TaskRunner::TaskRunner() = default; +namespace GpgFrontend::Thread { + +class TaskRunner::Impl : public QThread { + public: + Impl() : QThread(nullptr) {} -GpgFrontend::Thread::TaskRunner::~TaskRunner() = default; + void PostTask(Task* task) { + if (task == nullptr) { + GF_CORE_LOG_ERROR("task posted is null"); + return; + } -void GpgFrontend::Thread::TaskRunner::PostTask(Task* task) { - if (task == nullptr) { - SPDLOG_ERROR("task posted is null"); - return; + task->setParent(nullptr); + task->moveToThread(this); + + GF_CORE_LOG_TRACE("runner starts task: {} at thread: {}", task->GetFullID(), + this->currentThreadId()); + task->SafelyRun(); } - SPDLOG_TRACE("post task: {}", task->GetFullID()); + auto RegisterTask(const QString& name, const Task::TaskRunnable& runnerable, + const Task::TaskCallback& cb, DataObjectPtr params) + -> Task::TaskHandler { + auto* raw_task = new Task(runnerable, name, std::move(params), cb); + raw_task->setParent(nullptr); + raw_task->moveToThread(this); + + connect(raw_task, &Task::SignalRun, this, [this, raw_task]() { + pending_tasks_[raw_task->GetFullID()] = raw_task; + }); - task->setParent(nullptr); - task->moveToThread(this); + connect(raw_task, &Task::SignalTaskEnd, this, [this, raw_task]() { + pending_tasks_.remove(raw_task->GetFullID()); + }); - { - std::lock_guard<std::mutex> lock(tasks_mutex_); - tasks.push(task); + GF_CORE_LOG_TRACE("runner starts task: {} at thread: {}", + raw_task->GetFullID(), this->currentThreadId()); + + return Task::TaskHandler(raw_task); } - quit(); -} -void GpgFrontend::Thread::TaskRunner::PostScheduleTask(Task* task, - size_t seconds) { - if (task == nullptr) return; - // TODO -} + void PostTask(const QString& name, const Task::TaskRunnable& runnerable, + const Task::TaskCallback& cb, DataObjectPtr params) { + PostTask(new Task(runnerable, name, std::move(params), cb)); + } -[[noreturn]] void GpgFrontend::Thread::TaskRunner::run() { - SPDLOG_TRACE("task runner runing, thread id: {}", QThread::currentThreadId()); - while (true) { - if (tasks.empty()) { - SPDLOG_TRACE("no tasks to run, trapping into event loop..."); - exec(); - } else { - SPDLOG_TRACE("start to run task(s), queue size: {}", tasks.size()); - - Task* task = nullptr; - { - std::lock_guard<std::mutex> lock(tasks_mutex_); - task = std::move(tasks.front()); - tasks.pop(); - pending_tasks_.insert({task->GetUUID(), task}); - } - - if (task != nullptr) { - try { - // triger - SPDLOG_TRACE("running task {}, sequency: {}", task->GetFullID(), - task->GetSequency()); - - // when a signal SignalTaskEnd raise, do unregister work - connect(task, &Task::SignalTaskEnd, this, [this, task]() { - unregister_finished_task(task->GetUUID()); - }); - - if (!task->GetSequency()) { - // if it need to run concurrently, we should create a new thread to - // run it. - auto* concurrent_thread = new QThread(nullptr); - task->setParent(nullptr); - task->moveToThread(concurrent_thread); - // start thread - concurrent_thread->start(); - - connect(task, &Task::SignalTaskEnd, concurrent_thread, - &QThread::quit); - // concurrent thread is responsible for deleting the task - connect(concurrent_thread, &QThread::finished, task, - &Task::deleteLater); - } - - // run the task - task->run(); - } catch (const std::exception& e) { - SPDLOG_ERROR("task runner: exception in task {}, exception: {}", - task->GetFullID(), e.what()); - // if any exception caught, destroy the task, remove the task from the - // pending tasks - unregister_finished_task(task->GetUUID()); - } catch (...) { - SPDLOG_ERROR("task runner: unknown exception in task: {}", - task->GetFullID()); - // if any exception caught, destroy the task, remove the task from the - // pending tasks - unregister_finished_task(task->GetUUID()); - } - } + void PostConcurrentTask(Task* task) { + if (task == nullptr) { + GF_CORE_LOG_ERROR("task posted is null"); + return; } + + auto* concurrent_thread = new QThread(this); + + task->setParent(nullptr); + task->moveToThread(concurrent_thread); + + connect(task, &Task::SignalTaskEnd, concurrent_thread, &QThread::quit); + connect(concurrent_thread, &QThread::finished, concurrent_thread, + &QThread::deleteLater); + + concurrent_thread->start(); + + GF_CORE_LOG_TRACE("runner starts task concurrenctly: {}", + task->GetFullID()); + task->SafelyRun(); } -} -/** - * @brief - * - */ -void GpgFrontend::Thread::TaskRunner::unregister_finished_task( - std::string task_uuid) { - SPDLOG_DEBUG("cleaning task {}", task_uuid); - // search in map - auto pending_task = pending_tasks_.find(task_uuid); - if (pending_task == pending_tasks_.end()) { - SPDLOG_ERROR("cannot find task in pending list: {}", task_uuid); - return; - } else { - std::lock_guard<std::mutex> lock(tasks_mutex_); - // if thread runs sequenctly, that means the thread is living in this - // thread, so we can delete it. Or, its living thread need to delete it. - if (pending_task->second->GetSequency()) - pending_task->second->deleteLater(); - pending_tasks_.erase(pending_task); + void PostScheduleTask(Task* task, size_t seconds) { + if (task == nullptr) return; + // TODO } - SPDLOG_DEBUG("clean task {} done", task_uuid); + private: + QMap<QString, Task*> pending_tasks_; +}; + +TaskRunner::TaskRunner() : p_(SecureCreateUniqueObject<Impl>()) {} + +TaskRunner::~TaskRunner() { + if (p_->isRunning()) { + Stop(); + } +} + +void TaskRunner::PostTask(Task* task) { p_->PostTask(task); } + +void TaskRunner::PostTask(const QString& name, const Task::TaskRunnable& runner, + const Task::TaskCallback& cb, DataObjectPtr params) { + p_->PostTask(name, runner, cb, std::move(params)); +} + +void TaskRunner::PostConcurrentTask(Task* task) { + p_->PostConcurrentTask(task); +} + +void TaskRunner::PostScheduleTask(Task* task, size_t seconds) { + p_->PostScheduleTask(task, seconds); +} + +void TaskRunner::Start() { p_->start(); } + +void TaskRunner::Stop() { + p_->quit(); + p_->wait(); +} + +auto TaskRunner::GetThread() -> QThread* { return p_.get(); } + +auto TaskRunner::IsRunning() -> bool { return p_->isRunning(); } + +auto TaskRunner::RegisterTask(const QString& name, + const Task::TaskRunnable& runnable, + const Task::TaskCallback& cb, DataObjectPtr p_pbj) + -> Task::TaskHandler { + return p_->RegisterTask(name, runnable, cb, p_pbj); } +} // namespace GpgFrontend::Thread diff --git a/src/core/thread/TaskRunner.h b/src/core/thread/TaskRunner.h index 35cd1a30..8a93ad0b 100644 --- a/src/core/thread/TaskRunner.h +++ b/src/core/thread/TaskRunner.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -19,25 +19,22 @@ * 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 * */ -#ifndef GPGFRONTEND_TASKRUNNER_H -#define GPGFRONTEND_TASKRUNNER_H - -#include <cstddef> -#include <mutex> -#include <queue> +#pragma once #include "core/GpgFrontendCore.h" +#include "core/function/SecureMemoryAllocator.h" +#include "core/thread/Task.h" namespace GpgFrontend::Thread { -class Task; - -class GPGFRONTEND_CORE_EXPORT TaskRunner : public QThread { +class GPGFRONTEND_CORE_EXPORT TaskRunner : public QObject { Q_OBJECT public: /** @@ -50,13 +47,34 @@ class GPGFRONTEND_CORE_EXPORT TaskRunner : public QThread { * @brief Destroy the Task Runner object * */ - virtual ~TaskRunner() override; + ~TaskRunner() override; + + /** + * @brief + * + */ + void Start(); /** * @brief * */ - [[noreturn]] void run() override; + void Stop(); + + /** + * @brief Get the Thread object + * + * @return QThread* + */ + auto GetThread() -> QThread*; + + /** + * @brief + * + * @return true + * @return false + */ + auto IsRunning() -> bool; public slots: @@ -70,23 +88,38 @@ class GPGFRONTEND_CORE_EXPORT TaskRunner : public QThread { /** * @brief * - * @param task - * @param seconds + * @param runner + * @param cb */ - void PostScheduleTask(Task* task, size_t seconds); + void PostTask(const QString&, const Task::TaskRunnable&, + const Task::TaskCallback&, DataObjectPtr); - private: - std::queue<Task*> tasks; ///< The task queue - std::map<std::string, Task*> pending_tasks_; ///< The pending tasks - std::mutex tasks_mutex_; ///< The task queue mutex - QThreadPool thread_pool_{this}; ///< run non-sequency task + /** + * @brief + * + * @return std::tuple<QPointer<Task>, TaskTrigger> + */ + auto RegisterTask(const QString&, const Task::TaskRunnable&, + const Task::TaskCallback&, DataObjectPtr) + -> Task::TaskHandler; + + /** + * @brief + * + * @param task + */ + void PostConcurrentTask(Task* task); /** * @brief * + * @param task + * @param seconds */ - void unregister_finished_task(std::string); + void PostScheduleTask(Task* task, size_t seconds); + + private: + class Impl; + SecureUniquePtr<Impl> p_; }; } // namespace GpgFrontend::Thread - -#endif // GPGFRONTEND_TASKRUNNER_H
\ No newline at end of file diff --git a/src/core/thread/TaskRunnerGetter.cpp b/src/core/thread/TaskRunnerGetter.cpp index 186483ec..bdcd89d0 100644 --- a/src/core/thread/TaskRunnerGetter.cpp +++ b/src/core/thread/TaskRunnerGetter.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -19,28 +19,46 @@ * 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/TaskRunnerGetter.h" -GpgFrontend::Thread::TaskRunnerGetter::TaskRunnerGetter(int channel) - : SingletonFunctionObject<TaskRunnerGetter>(channel) {} +#include <mutex> + +#include "core/GpgConstants.h" +#include "core/thread/TaskRunner.h" + +namespace GpgFrontend::Thread { -GpgFrontend::Thread::TaskRunner* -GpgFrontend::Thread::TaskRunnerGetter::GetTaskRunner( - TaskRunnerType runner_type) { +TaskRunnerGetter::TaskRunnerGetter(int) + : SingletonFunctionObject<TaskRunnerGetter>(kGpgFrontendDefaultChannel) {} + +auto TaskRunnerGetter::GetTaskRunner(TaskRunnerType runner_type) + -> TaskRunnerPtr { + std::lock_guard<std::mutex> lock_guard(task_runners_map_lock_); while (true) { auto it = task_runners_.find(runner_type); if (it != task_runners_.end()) { return it->second; - } else { - auto runner = new TaskRunner(); - task_runners_[runner_type] = runner; - runner->start(); - continue; + } + + auto runner = GpgFrontend::SecureCreateSharedObject<TaskRunner>(); + task_runners_[runner_type] = runner; + runner->Start(); + } +} + +void TaskRunnerGetter::StopAllTeakRunner() { + for (const auto& [key, value] : task_runners_) { + if (value->IsRunning()) { + value->Stop(); } } } + +} // namespace GpgFrontend::Thread
\ No newline at end of file diff --git a/src/core/thread/TaskRunnerGetter.h b/src/core/thread/TaskRunnerGetter.h index 80b25c3e..49ed25c0 100644 --- a/src/core/thread/TaskRunnerGetter.h +++ b/src/core/thread/TaskRunnerGetter.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -19,20 +19,25 @@ * 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 * */ -#ifndef GPGFRONTEND_TASKRUNNERGETTER_H -#define GPGFRONTEND_TASKRUNNERGETTER_H +#pragma once + +#include <mutex> #include "core/GpgFrontendCore.h" -#include "core/GpgFunctionObject.h" +#include "core/function/basic/GpgFunctionObject.h" #include "core/thread/TaskRunner.h" namespace GpgFrontend::Thread { +using TaskRunnerPtr = std::shared_ptr<TaskRunner>; + class GPGFRONTEND_CORE_EXPORT TaskRunnerGetter : public GpgFrontend::SingletonFunctionObject<TaskRunnerGetter> { public: @@ -41,18 +46,21 @@ class GPGFRONTEND_CORE_EXPORT TaskRunnerGetter kTaskRunnerType_GPG, kTaskRunnerType_IO, kTaskRunnerType_Network, + kTaskRunnerType_Module, kTaskRunnerType_External_Process, }; - TaskRunnerGetter(int channel = SingletonFunctionObject::GetDefaultChannel()); + explicit TaskRunnerGetter( + int channel = SingletonFunctionObject::GetDefaultChannel()); - TaskRunner *GetTaskRunner( - TaskRunnerType runner_type = kTaskRunnerType_Default); + auto GetTaskRunner(TaskRunnerType runner_type = kTaskRunnerType_Default) + -> TaskRunnerPtr; + + void StopAllTeakRunner(); private: - std::map<TaskRunnerType, TaskRunner *> task_runners_; + std::map<TaskRunnerType, TaskRunnerPtr> task_runners_; + std::mutex task_runners_map_lock_; }; } // namespace GpgFrontend::Thread - -#endif // GPGFRONTEND_TASKRUNNERGETTER_H
\ No newline at end of file diff --git a/src/core/thread/ThreadingModel.h b/src/core/thread/ThreadingModel.h index eb6c9039..2fcc11c8 100644 --- a/src/core/thread/ThreadingModel.h +++ b/src/core/thread/ThreadingModel.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -20,17 +20,14 @@ * the gpg4usb project, which is under GPL-3.0-or-later. * * All the source code of GpgFrontend was modified and released by - * Saturneric<[email protected]> starting on May 12, 2021. + * Saturneric <[email protected]> starting on May 12, 2021. * * SPDX-License-Identifier: GPL-3.0-or-later * */ -#ifndef GPGFRONTEND_THREADINGMODEL_H -#define GPGFRONTEND_THREADINGMODEL_H +#pragma once #include "core/thread/Task.h" #include "core/thread/TaskRunner.h" #include "core/thread/TaskRunnerGetter.h" - -#endif // GPGFRONTEND_THREADINGMODEL_H
\ No newline at end of file |