diff options
author | Saturneric <[email protected]> | 2022-05-13 17:43:56 +0000 |
---|---|---|
committer | Saturneric <[email protected]> | 2022-05-13 17:43:56 +0000 |
commit | 93ac14374580d6a173b83bef0dba0ff73ff77f5a (patch) | |
tree | db4926fd694f660c48d2e18bb2bf6eaa4aaeae6d | |
parent | perf: improve core performance (diff) | |
download | GpgFrontend-93ac14374580d6a173b83bef0dba0ff73ff77f5a.tar.gz GpgFrontend-93ac14374580d6a173b83bef0dba0ff73ff77f5a.zip |
feat: add a simple TaskRunner system
1. solve multiple threads problem.
-rw-r--r-- | src/core/thread/CtxCheckTask.cpp (renamed from src/core/thread/CtxCheckThread.cpp) | 8 | ||||
-rw-r--r-- | src/core/thread/CtxCheckTask.h (renamed from src/core/thread/CtxCheckThread.h) | 11 | ||||
-rw-r--r-- | src/core/thread/FileReadTask.cpp | 88 | ||||
-rw-r--r-- | src/core/thread/FileReadTask.h (renamed from src/ui/thread/FileReadThread.h) | 46 | ||||
-rw-r--r-- | src/core/thread/Task.cpp | 74 | ||||
-rw-r--r-- | src/core/thread/Task.h | 113 | ||||
-rw-r--r-- | src/core/thread/TaskRunner.cpp | 66 | ||||
-rw-r--r-- | src/core/thread/TaskRunner.h | 75 | ||||
-rw-r--r-- | src/core/thread/TaskRunnerGetter.cpp | 46 | ||||
-rw-r--r-- | src/core/thread/TaskRunnerGetter.h | 56 | ||||
-rw-r--r-- | src/main.cpp | 1 | ||||
-rw-r--r-- | src/ui/GpgFrontendUIInit.cpp | 27 | ||||
-rw-r--r-- | src/ui/UserInterfaceUtils.cpp | 67 | ||||
-rw-r--r-- | src/ui/thread/FileReadThread.cpp | 87 | ||||
-rw-r--r-- | src/ui/widgets/KeyList.cpp | 24 | ||||
-rw-r--r-- | src/ui/widgets/PlainTextEditorPage.cpp | 60 | ||||
-rw-r--r-- | src/ui/widgets/PlainTextEditorPage.h | 31 | ||||
-rw-r--r-- | src/ui/widgets/TextEdit.cpp | 4 |
18 files changed, 655 insertions, 229 deletions
diff --git a/src/core/thread/CtxCheckThread.cpp b/src/core/thread/CtxCheckTask.cpp index edec8855..ee170fbc 100644 --- a/src/core/thread/CtxCheckThread.cpp +++ b/src/core/thread/CtxCheckTask.cpp @@ -24,20 +24,20 @@ * */ -#include "core/thread/CtxCheckThread.h" +#include "core/thread/CtxCheckTask.h" #include "core/GpgContext.h" #include "core/GpgCoreInit.h" #include "core/common/CoreCommonUtil.h" #include "core/function/gpg/GpgKeyGetter.h" -GpgFrontend::CtxCheckThread::CtxCheckThread() : QThread(nullptr) { - connect(this, &CtxCheckThread::SignalGnupgNotInstall, +GpgFrontend::Thread::CtxCheckTask::CtxCheckTask() { + connect(this, &CtxCheckTask::SignalGnupgNotInstall, CoreCommonUtil::GetInstance(), &CoreCommonUtil::SignalGnupgNotInstall); } -void GpgFrontend::CtxCheckThread::run() { +void GpgFrontend::Thread::CtxCheckTask::Run() { // init logging init_logging(); diff --git a/src/core/thread/CtxCheckThread.h b/src/core/thread/CtxCheckTask.h index c597141f..06ddfd82 100644 --- a/src/core/thread/CtxCheckThread.h +++ b/src/core/thread/CtxCheckTask.h @@ -28,20 +28,21 @@ #define GPGFRONTEND_CTXCHECKTRHEAD_H #include "core/GpgFrontendCore.h" +#include "core/thread/Task.h" -namespace GpgFrontend { +namespace GpgFrontend::Thread { /** * @brief * */ -class GPGFRONTEND_CORE_EXPORT CtxCheckThread : public QThread { +class GPGFRONTEND_CORE_EXPORT CtxCheckTask : public Task { Q_OBJECT public: /** * @brief Construct a new Ctx Check Thread object * */ - CtxCheckThread(); + CtxCheckTask(); signals: /** @@ -55,8 +56,8 @@ class GPGFRONTEND_CORE_EXPORT CtxCheckThread : public QThread { * @brief * */ - void run() override; + void Run() override; }; -} // namespace GpgFrontend +} // namespace GpgFrontend::Thread #endif // GPGFRONTEND_CTXCHECKTRHEAD_H diff --git a/src/core/thread/FileReadTask.cpp b/src/core/thread/FileReadTask.cpp new file mode 100644 index 00000000..3a235390 --- /dev/null +++ b/src/core/thread/FileReadTask.cpp @@ -0,0 +1,88 @@ +/** + * 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/FileReadTask.h" + +#include <utility> + +namespace GpgFrontend::UI { + +FileReadTask::FileReadTask(std::string path) { + connect(this, &FileReadTask::SignalFileBytesReadNext, this, + &FileReadTask::read_bytes); + +#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; +} + +void FileReadTask::Run() { + SetFinishAfterRun(false); + + if (is_regular_file(read_file_path_)) { + LOG(INFO) << "read open file" << read_file_path_; + + target_file_.setFileName( + QString::fromStdString(read_file_path_.u8string())); + target_file_.open(QIODevice::ReadOnly); + + if (!(target_file_.isOpen() && target_file_.isReadable())) { + LOG(ERROR) << "file not open or not readable"; + if (target_file_.isOpen()) target_file_.close(); + return; + } + LOG(INFO) << "started reading" << read_file_path_; + read_bytes(); + } else { + emit SignalFileBytesReadEnd(); + } +} + +void FileReadTask::read_bytes() { + QByteArray read_buffer; + if (!target_file_.atEnd() && + (read_buffer = target_file_.read(buffer_size_)).size() > 0) { + LOG(INFO) << "read bytes" << read_buffer.size(); + emit SignalFileBytesRead(std::move(read_buffer)); + } else { + LOG(INFO) << "read bytes end"; + emit SignalFileBytesReadEnd(); + // finish task + emit SignalTaskFinished(); + } +} + +FileReadTask::~FileReadTask() { + LOG(INFO) << "close file" << read_file_path_; + if (target_file_.isOpen()) target_file_.close(); +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/thread/FileReadThread.h b/src/core/thread/FileReadTask.h index e7573af8..d4e61cbe 100644 --- a/src/ui/thread/FileReadThread.h +++ b/src/core/thread/FileReadTask.h @@ -27,7 +27,8 @@ #ifndef GPGFRONTEND_FILEREADTHREAD_H #define GPGFRONTEND_FILEREADTHREAD_H -#include "ui/GpgFrontendUI.h" +#include "core/GpgFrontendCore.h" +#include "core/thread/Task.h" namespace GpgFrontend::UI { @@ -35,41 +36,28 @@ namespace GpgFrontend::UI { * @brief * */ -class FileReadThread : public QThread { +class GPGFRONTEND_CORE_EXPORT FileReadTask : public GpgFrontend::Thread::Task { Q_OBJECT - public: - /** - * @brief Construct a new File Read Thread object - * - * @param path - */ - explicit FileReadThread(std::string path); - - signals: + explicit FileReadTask(std::string path); - /** - * @brief - * - * @param block - */ - void SignalSendReadBlock(const std::string& block); + virtual ~FileReadTask() override; - /** - * @brief - * - */ - void SignalReadDone(); + void Run() override; - protected: - /** - * @brief - * - */ - void run() override; + signals: + void SignalFileBytesRead(QByteArray bytes); + void SignalFileBytesReadEnd(); + void SignalFileBytesReadNext(); private: - std::string path_; ///< + std::filesystem::path read_file_path_; + QFile target_file_; + const size_t buffer_size_ = 4096; + QEventLoop looper; + + private slots: + void read_bytes(); }; } // namespace GpgFrontend::UI diff --git a/src/core/thread/Task.cpp b/src/core/thread/Task.cpp new file mode 100644 index 00000000..9626ba69 --- /dev/null +++ b/src/core/thread/Task.cpp @@ -0,0 +1,74 @@ +/** + * 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/Task.h" + +#include <functional> + +#include "core/thread/TaskRunner.h" + +GpgFrontend::Thread::Task::Task() { init(); } + +GpgFrontend::Thread::Task::Task(TaskCallback callback) + : callback_(std::move(callback)) { + init(); +} + +GpgFrontend::Thread::Task::Task(TaskRunnable runnable, TaskCallback callback) + : runnable_(runnable), callback_(std::move(callback)) { + init(); +} + +GpgFrontend::Thread::Task::~Task() = default; + +void GpgFrontend::Thread::Task::SetFinishAfterRun(bool finish_after_run) { + this->finish_after_run_ = finish_after_run; +} + +void GpgFrontend::Thread::Task::SetRTN(int rtn) { this->rtn_ = rtn; } + +void GpgFrontend::Thread::Task::init() { + LOG(INFO) << "called"; + connect(this, &Task::SignalTaskFinished, this, &Task::before_finish_task); + connect(this, &Task::SignalTaskFinished, this, &Task::deleteLater); +} + +void GpgFrontend::Thread::Task::before_finish_task() { + LOG(INFO) << "called"; + if (callback_) callback_(rtn_); +} + +void GpgFrontend::Thread::Task::run() { + LOG(INFO) << "called"; + Run(); + if (finish_after_run_) emit SignalTaskFinished(); +} + +void GpgFrontend::Thread::Task::Run() { + if (runnable_) { + rtn_ = runnable_(); + } +} diff --git a/src/core/thread/Task.h b/src/core/thread/Task.h new file mode 100644 index 00000000..4b536176 --- /dev/null +++ b/src/core/thread/Task.h @@ -0,0 +1,113 @@ +/** + * 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_TASK_H +#define GPGFRONTEND_TASK_H + +#include <functional> + +#include "core/GpgFrontendCore.h" + +namespace GpgFrontend::Thread { + +class TaskRunner; + +class GPGFRONTEND_CORE_EXPORT Task : public QObject, public QRunnable { + Q_OBJECT + public: + using TaskRunnable = std::function<int()>; ///< + using TaskCallback = std::function<void(int)>; ///< + friend class TaskRunner; + + /** + * @brief Construct a new Task object + * + */ + Task(); + + /** + * @brief Construct a new Task object + * + * @param callback The callback function to be executed. + * callback must not be nullptr, and not tp opreate UI object. + */ + Task(TaskCallback callback); + + /** + * @brief Construct a new Task object + * + * @param runnable + */ + Task( + TaskRunnable runnable, TaskCallback callback = [](int) {}); + + /** + * @brief Destroy the Task object + * + */ + virtual ~Task() override; + + /** + * @brief Run - run the task + * + */ + virtual void Run(); + + signals: + void SignalTaskFinished(); + + protected: + void SetFinishAfterRun(bool finish_after_run); + + void SetRTN(int rtn); + + private: + TaskCallback callback_; ///< + TaskRunnable runnable_; ///< + bool finish_after_run_ = true; ///< + int rtn_ = 0; ///< + + /** + * @brief + * + */ + void before_finish_task(); + + /** + * @brief + * + */ + void init(); + + /** + * @brief + * + */ + virtual 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 new file mode 100644 index 00000000..2223bdda --- /dev/null +++ b/src/core/thread/TaskRunner.cpp @@ -0,0 +1,66 @@ +/** + * 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/TaskRunner.h" + +#include "core/thread/Task.h" +#include "easylogging++.h" + +GpgFrontend::Thread::TaskRunner::TaskRunner() = default; + +GpgFrontend::Thread::TaskRunner::~TaskRunner() = default; + +void GpgFrontend::Thread::TaskRunner::PostTask(Task* task) { + LOG(INFO) << "called"; + if (task == nullptr) return; + task->setParent(nullptr); + task->moveToThread(this); + { + std::lock_guard<std::mutex> lock(tasks_mutex_); + tasks.push(task); + } + quit(); +} + +void GpgFrontend::Thread::TaskRunner::run() { + LOG(INFO) << "called"; + while (true) { + if (tasks.empty()) { + LOG(INFO) << "TaskRunner: No tasks to run"; + exec(); + } else { + LOG(INFO) << "TaskRunner: Running task, queue size:" << tasks.size(); + + Task* task = nullptr; + { + std::lock_guard<std::mutex> lock(tasks_mutex_); + task = std::move(tasks.front()); + tasks.pop(); + } + if (task != nullptr) task->run(); + } + } +} diff --git a/src/core/thread/TaskRunner.h b/src/core/thread/TaskRunner.h new file mode 100644 index 00000000..14eaeae7 --- /dev/null +++ b/src/core/thread/TaskRunner.h @@ -0,0 +1,75 @@ +/** + * 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_TASKRUNNER_H +#define GPGFRONTEND_TASKRUNNER_H + +#include <mutex> +#include <queue> + +#include "core/GpgFrontendCore.h" + +namespace GpgFrontend::Thread { + +class Task; + +class GPGFRONTEND_CORE_EXPORT TaskRunner : public QThread { + Q_OBJECT + public: + /** + * @brief Construct a new Task Runner object + * + */ + TaskRunner(); + + /** + * @brief Destroy the Task Runner object + * + */ + virtual ~TaskRunner() override; + + /** + * @brief + * + */ + void run() override; + + public slots: + + /** + * @brief + * + * @param task + */ + void PostTask(Task* task); + + private: + std::queue<Task*> tasks; ///< The task queue + std::mutex tasks_mutex_; ///< The task queue mutex +}; +} // 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 new file mode 100644 index 00000000..186483ec --- /dev/null +++ b/src/core/thread/TaskRunnerGetter.cpp @@ -0,0 +1,46 @@ +/** + * 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/TaskRunnerGetter.h" + +GpgFrontend::Thread::TaskRunnerGetter::TaskRunnerGetter(int channel) + : SingletonFunctionObject<TaskRunnerGetter>(channel) {} + +GpgFrontend::Thread::TaskRunner* +GpgFrontend::Thread::TaskRunnerGetter::GetTaskRunner( + TaskRunnerType runner_type) { + 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; + } + } +} diff --git a/src/core/thread/TaskRunnerGetter.h b/src/core/thread/TaskRunnerGetter.h new file mode 100644 index 00000000..722484b5 --- /dev/null +++ b/src/core/thread/TaskRunnerGetter.h @@ -0,0 +1,56 @@ +/** + * 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_TASKRUNNERGETTER_H +#define GPGFRONTEND_TASKRUNNERGETTER_H + +#include "core/GpgFrontendCore.h" +#include "core/GpgFunctionObject.h" +#include "core/thread/TaskRunner.h" + +namespace GpgFrontend::Thread { + +class GPGFRONTEND_CORE_EXPORT TaskRunnerGetter + : public GpgFrontend::SingletonFunctionObject<TaskRunnerGetter> { + public: + enum TaskRunnerType { + kTaskRunnerType_Default, + kTaskRunnerType_GPG, + kTaskRunnerType_IO, + }; + + TaskRunnerGetter(int channel = SingletonFunctionObject::GetDefaultChannel()); + + TaskRunner *GetTaskRunner( + TaskRunnerType runner_type = kTaskRunnerType_Default); + + private: + std::map<TaskRunnerType, TaskRunner *> task_runners_; +}; + +} // namespace GpgFrontend::Thread + +#endif // GPGFRONTEND_TASKRUNNERGETTER_H
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 24f2b7fa..fd20a664 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,7 +36,6 @@ #include "GpgFrontendBuildInfo.h" #include "core/GpgFunctionObject.h" -#include "core/thread/CtxCheckThread.h" #include "ui/GpgFrontendUIInit.h" #include "ui/main_window/MainWindow.h" diff --git a/src/ui/GpgFrontendUIInit.cpp b/src/ui/GpgFrontendUIInit.cpp index 2a8ac8b4..a5302c7b 100644 --- a/src/ui/GpgFrontendUIInit.cpp +++ b/src/ui/GpgFrontendUIInit.cpp @@ -29,7 +29,8 @@ #include "GpgFrontendUIInit.h" #include "core/function/GlobalSettingStation.h" -#include "core/thread/CtxCheckThread.h" +#include "core/thread/CtxCheckTask.h" +#include "core/thread/TaskRunnerGetter.h" #include "ui/SignalStation.h" #include "ui/UserInterfaceUtils.h" #include "ui/main_window/MainWindow.h" @@ -49,9 +50,7 @@ void InitGpgFrontendUI() { CommonUtils::GetInstance(); // create the thread to load the gpg context - auto* init_ctx_thread = new GpgFrontend::CtxCheckThread(); - QApplication::connect(init_ctx_thread, &QThread::finished, init_ctx_thread, - &QThread::deleteLater); + auto* init_ctx_task = new Thread::CtxCheckTask(); // create and show loading window before starting the main window auto* waiting_dialog = new QProgressDialog(); @@ -66,27 +65,27 @@ void InitGpgFrontendUI() { waiting_dialog_label->setWordWrap(true); waiting_dialog->setLabel(waiting_dialog_label); waiting_dialog->resize(420, 120); - QApplication::connect(init_ctx_thread, &QThread::finished, [=]() { - waiting_dialog->finished(0); - waiting_dialog->deleteLater(); - }); + QApplication::connect(init_ctx_task, + &Thread::CtxCheckTask::SignalTaskFinished, + waiting_dialog, [=]() { + LOG(INFO) << "Gpg context loaded"; + waiting_dialog->finished(0); + waiting_dialog->deleteLater(); + }); QApplication::connect(waiting_dialog, &QProgressDialog::canceled, [=]() { LOG(INFO) << "cancel clicked"; - if (init_ctx_thread->isRunning()) init_ctx_thread->terminate(); QCoreApplication::quit(); exit(0); }); // show the loading window + waiting_dialog->setModal(true); waiting_dialog->show(); waiting_dialog->setFocus(); // start the thread to load the gpg context - init_ctx_thread->start(); - QEventLoop loop; - QApplication::connect(init_ctx_thread, &QThread::finished, &loop, - &QEventLoop::quit); - loop.exec(); + Thread::TaskRunnerGetter::GetInstance().GetTaskRunner()->PostTask( + init_ctx_task); } int RunGpgFrontendUI() { diff --git a/src/ui/UserInterfaceUtils.cpp b/src/ui/UserInterfaceUtils.cpp index 724a467e..65c55c8f 100644 --- a/src/ui/UserInterfaceUtils.cpp +++ b/src/ui/UserInterfaceUtils.cpp @@ -35,9 +35,13 @@ #include "core/function/FileOperator.h" #include "core/function/GlobalSettingStation.h" #include "core/function/gpg/GpgKeyGetter.h" +#include "core/thread/Task.h" +#include "core/thread/TaskRunner.h" +#include "core/thread/TaskRunnerGetter.h" #include "easylogging++.h" #include "ui/SignalStation.h" #include "ui/dialog/WaitingDialog.h" +#include "ui/struct/SettingsObject.h" #include "ui/widgets/TextEdit.h" namespace GpgFrontend::UI { @@ -111,17 +115,21 @@ void process_result_analyse(TextEdit *edit, InfoBoardWidget *info_board, void process_operation(QWidget *parent, const std::string &waiting_title, const std::function<void()> &func) { + auto *dialog = + new WaitingDialog(QString::fromStdString(waiting_title), parent); + auto thread = QThread::create(func); QApplication::connect(thread, &QThread::finished, thread, &QThread::deleteLater); - thread->start(); + QApplication::connect(thread, &QThread::finished, dialog, &QDialog::close); + QApplication::connect(thread, &QThread::finished, dialog, + &QDialog::deleteLater); - auto *dialog = - new WaitingDialog(QString::fromStdString(waiting_title), parent); - while (thread->isRunning()) { - QApplication::processEvents(); - } - dialog->close(); + QEventLoop looper; + QApplication::connect(dialog, &QDialog::finished, &looper, &QEventLoop::quit); + + thread->start(); + looper.exec(); } CommonUtils *CommonUtils::GetInstance() { @@ -245,11 +253,23 @@ void CommonUtils::SlotExecuteGpgCommand( void CommonUtils::SlotImportKeyFromKeyServer( const KeyIdArgsList &key_ids, const ImportCallbackFunctiopn &callback) { std::string target_keyserver; + if (target_keyserver.empty()) { try { auto &settings = GlobalSettingStation::GetInstance().GetUISettings(); + SettingsObject key_server_json("key_server"); + + // get key servers from settings + const auto key_server_list = + key_server_json.Check("server_list", nlohmann::json::array()); + if (key_server_list.empty()) { + throw std::runtime_error("No key server configured"); + } - target_keyserver = settings.lookup("keyserver.default_server").c_str(); + const int target_key_server_index = + key_server_json.Check("default_server", 0); + target_keyserver = + key_server_list[target_key_server_index].get<std::string>(); LOG(INFO) << _("Set target Key Server to default Key Server") << target_keyserver; @@ -323,39 +343,26 @@ void CommonUtils::SlotImportKeyFromKeyServer( current_index++; } }); - connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } void CommonUtils::slot_update_key_status() { LOG(INFO) << "called"; - auto *thread = QThread::create([this]() { - std::vector<QThread *> threads; + auto refresh_task = new Thread::Task([]() -> int { // flush key cache for all GpgKeyGetter Intances. for (const auto &channel_id : GpgKeyGetter::GetAllChannelId()) { - // multi threading - auto *refresh_thread = QThread::create([channel_id]() { - LOG(INFO) << "FlushKeyCache thread start" - << "channel:" << channel_id; - GpgKeyGetter::GetInstance(channel_id).FlushKeyCache(); - }); - refresh_thread->start(); - threads.push_back(refresh_thread); + GpgKeyGetter::GetInstance(channel_id).FlushKeyCache(); } - - for (auto *thread : threads) { - thread->wait(); - thread->deleteLater(); - } - - emit SignalKeyDatabaseRefreshDone(); - LOG(INFO) << "finished"; + return 0; }); - connect(thread, &QThread::finished, thread, &QThread::deleteLater); - LOG(INFO) << "start thread"; - thread->start(); + connect(refresh_task, &Thread::Task::SignalTaskFinished, this, + &CommonUtils::SignalKeyDatabaseRefreshDone); + + // post the task to the default task runner + Thread::TaskRunnerGetter::GetInstance().GetTaskRunner()->PostTask( + refresh_task); } } // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/thread/FileReadThread.cpp b/src/ui/thread/FileReadThread.cpp deleted file mode 100644 index 83731c8a..00000000 --- a/src/ui/thread/FileReadThread.cpp +++ /dev/null @@ -1,87 +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 "FileReadThread.h" - - -#include <utility> - -namespace GpgFrontend::UI { - -FileReadThread::FileReadThread(std::string path) : path_(std::move(path)) { - qRegisterMetaType<std::string>("std::string"); -} - -void FileReadThread::run() { - LOG(INFO) << "started reading" << path_; - -#ifdef WINDOWS - std::filesystem::path read_file_path(QString::fromStdString(path_).toStdU16String()); -#else - std::filesystem::path read_file_path(QString::fromStdString(path_).toStdString()); -#endif - - if (is_regular_file(read_file_path)) { - LOG(INFO) << "read open" << read_file_path; - - QFile target_file; - target_file.setFileName(QString::fromStdString(read_file_path.u8string())); - target_file.open(QIODevice::ReadOnly); - QByteArray read_buffer; - LOG(INFO) << "thread start reading"; - - const size_t buffer_size = 4096; - if(!(target_file.isOpen() && target_file.isReadable())) { - LOG(ERROR) << "file not open or not readable"; - if(target_file.isOpen()) - target_file.close(); - return; - } - - while (!target_file.atEnd() && (read_buffer = target_file.read(buffer_size)).size() > 0) { - // Check isInterruptionRequested - if (QThread::currentThread()->isInterruptionRequested()) { - LOG(INFO) << "thread is interruption requested "; - target_file.close(); - return; - } - LOG(INFO) << "block size " << read_buffer.size(); - std::string buffer_str(read_buffer.toStdString()); - - emit SignalSendReadBlock(buffer_str); -#ifdef RELEASE - QThread::msleep(32); -#else - QThread::msleep(128); -#endif - } - target_file.close(); - emit SignalReadDone(); - LOG(INFO) << "thread end reading"; - } -} - -} // namespace GpgFrontend::UI diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp index bbddc84b..0bd65f25 100644 --- a/src/ui/widgets/KeyList.cpp +++ b/src/ui/widgets/KeyList.cpp @@ -164,7 +164,6 @@ void KeyList::SlotRefresh() { ui_->syncButton->setDisabled(true); emit SignalRefreshStatusBar(_("Refreshing Key List..."), 3000); - this->buffered_keys_list_ = GpgKeyGetter::GetInstance().FetchKey(); this->slot_refresh_ui(); } @@ -463,7 +462,7 @@ void KeyList::slot_sync_with_key_server() { ui_->syncButton->setDisabled(false); ui_->refreshKeyListButton->setDisabled(false); emit SignalRefreshStatusBar(_("Key List Sync Done."), 3000); - emit SignalRefreshDatabase(); + emit this->SignalRefreshDatabase(); } }); } @@ -501,8 +500,9 @@ KeyIdArgsListPtr& KeyTable::GetChecked() { if (checked_key_ids_ == nullptr) checked_key_ids_ = std::make_unique<KeyIdArgsList>(); auto& ret = checked_key_ids_; - for (int i = 0; i < key_list_->rowCount(); i++) { + for (int i = 0; i < buffered_keys_.size(); i++) { auto key_id = buffered_keys_[i].GetId(); + LOG(INFO) << "i: " << i << " key_id: " << key_id; if (key_list_->item(i, 0)->checkState() == Qt::Checked && std::find(ret->begin(), ret->end(), key_id) == ret->end()) { ret->push_back(key_id); @@ -517,7 +517,7 @@ void KeyTable::SetChecked(KeyIdArgsListPtr key_ids) { } void KeyTable::Refresh(KeyLinkListPtr m_keys) { - LOG(INFO) << "Called"; + LOG(INFO) << "called"; auto& checked_key_list = GetChecked(); // while filling the table, sort enabled causes errors @@ -532,16 +532,19 @@ void KeyTable::Refresh(KeyLinkListPtr m_keys) { else keys = std::move(m_keys); + LOG(INFO) << "keys size: " << keys->size(); auto it = keys->begin(); int row_count = 0; while (it != keys->end()) { + LOG(INFO) << "filtering key id: " << it->GetId(); if (filter_ != nullptr) { if (!filter_(*it)) { it = keys->erase(it); continue; } } + LOG(INFO) << "adding key id: " << it->GetId(); if (select_type_ == KeyListRow::ONLY_SECRET_KEY && !it->IsPrivateKey()) { it = keys->erase(it); continue; @@ -550,18 +553,15 @@ void KeyTable::Refresh(KeyLinkListPtr m_keys) { it++; } + LOG(INFO) << "row_count: " << row_count; key_list_->setRowCount(row_count); int row_index = 0; it = keys->begin(); - auto& table_buffered_keys = buffered_keys_; - - table_buffered_keys.clear(); + buffered_keys_.clear(); while (it != keys->end()) { - table_buffered_keys.push_back(it->Copy()); - auto* tmp0 = new QTableWidgetItem(QString::number(row_index)); tmp0->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); @@ -627,6 +627,12 @@ void KeyTable::Refresh(KeyLinkListPtr m_keys) { tmp2->setFont(strike); tmp3->setFont(strike); } + + LOG(INFO) << "key id: " << it->GetId() << "added into key_list_:" << this; + + // move to buffered keys + buffered_keys_.emplace_back(std::move(*it)); + it++; ++row_index; } diff --git a/src/ui/widgets/PlainTextEditorPage.cpp b/src/ui/widgets/PlainTextEditorPage.cpp index c1787c36..6f1727e8 100644 --- a/src/ui/widgets/PlainTextEditorPage.cpp +++ b/src/ui/widgets/PlainTextEditorPage.cpp @@ -31,7 +31,9 @@ #include <utility> #include "core/function/CharsetOperator.h" -#include "ui/thread/FileReadThread.h" +#include "core/thread/FileReadTask.h" +#include "core/thread/Task.h" +#include "core/thread/TaskRunnerGetter.h" #include "ui_PlainTextEditor.h" namespace GpgFrontend::UI { @@ -54,7 +56,7 @@ PlainTextEditorPage::PlainTextEditorPage(QString file_path, QWidget *parent) this->ui_->encodingLabel->setText(_("utf-8")); connect(ui_->textPage, &QPlainTextEdit::textChanged, this, [=]() { - // if file is loaded + // if file is loading if (!read_done_) return; auto text = ui_->textPage->document()->toPlainText(); @@ -163,36 +165,34 @@ void PlainTextEditorPage::ReadFile() { auto text_page = this->GetTextPage(); text_page->setReadOnly(true); - auto thread = new FileReadThread(this->full_file_path_.toStdString()); - connect(thread, &FileReadThread::SignalSendReadBlock, this, - &PlainTextEditorPage::slot_insert_text); + const auto target_path = this->full_file_path_.toStdString(); - connect(thread, &FileReadThread::SignalReadDone, this, [=]() { - LOG(INFO) << "thread read done"; - if (!binary_mode_) { - text_page->setReadOnly(false); - } + auto *task_runner = + GpgFrontend::Thread::TaskRunnerGetter::GetInstance().GetTaskRunner(); + + auto *read_task = new FileReadTask(target_path); + connect(read_task, &FileReadTask::SignalFileBytesRead, this, + &PlainTextEditorPage::slot_insert_text, Qt::QueuedConnection); + connect(this, &PlainTextEditorPage::SignalUIBytesDisplayed, read_task, + &FileReadTask::SignalFileBytesReadNext, Qt::QueuedConnection); + + connect(read_task, &FileReadTask::SignalTaskFinished, this, + []() { LOG(INFO) << "read thread closed"; }); + connect(this, &PlainTextEditorPage::close, read_task, + &FileReadTask::SignalTaskFinished); + connect(read_task, &FileReadTask::SignalFileBytesReadEnd, this, [=]() { + // set the UI + if (!binary_mode_) text_page->setReadOnly(false); this->read_done_ = true; this->ui_->textPage->setEnabled(true); text_page->document()->setModified(false); this->ui_->textPage->blockSignals(false); this->ui_->textPage->document()->blockSignals(false); this->ui_->loadingLabel->setHidden(true); - - // delete thread - read_thread_->deleteLater(); }); - connect(this, &PlainTextEditorPage::destroyed, [=]() { - LOG(INFO) << "request interruption for read thread"; - if (read_thread_ != nullptr && read_thread_->isRunning()) - read_thread_->requestInterruption(); - read_thread_ = nullptr; - }); - - this->read_thread_ = thread; - thread->start(); + task_runner->PostTask(read_task); } std::string binary_to_string(const std::string &source) { @@ -203,11 +203,12 @@ std::string binary_to_string(const std::string &source) { return ss.str(); } -void PlainTextEditorPage::slot_insert_text(const std::string &data) { +void PlainTextEditorPage::slot_insert_text(QByteArray bytes_data) { + std::string data = bytes_data.toStdString(); LOG(INFO) << "data size" << data.size(); read_bytes_ += data.size(); - // If binary format is detected, the entire file is converted to binary format - // for display. + // If binary format is detected, the entire file is converted to binary + // format for display. bool if_last_binary_mode = binary_mode_; if (!binary_mode_ && !read_done_) { detect_encoding(data); @@ -251,13 +252,8 @@ void PlainTextEditorPage::slot_insert_text(const std::string &data) { auto str = boost::format(_("%1% character(s)")) % text.size(); this->ui_->characterLabel->setText(str.str().c_str()); } -} - -void PlainTextEditorPage::PrepareToDestroy() { - if (read_thread_) { - read_thread_->requestInterruption(); - read_thread_ = nullptr; - } + QTimer::singleShot(25, this, &PlainTextEditorPage::SignalUIBytesDisplayed); + LOG(INFO) << "end"; } void PlainTextEditorPage::detect_encoding(const std::string &data) { diff --git a/src/ui/widgets/PlainTextEditorPage.h b/src/ui/widgets/PlainTextEditorPage.h index 0009b7d6..e5c1c89d 100644 --- a/src/ui/widgets/PlainTextEditorPage.h +++ b/src/ui/widgets/PlainTextEditorPage.h @@ -101,12 +101,6 @@ class PlainTextEditorPage : public QWidget { [[nodiscard]] bool ReadDone() const { return this->read_done_; } /** - * @brief - * - */ - void PrepareToDestroy(); - - /** * @brief detect if the charset of the file will change * */ @@ -118,18 +112,25 @@ class PlainTextEditorPage : public QWidget { */ void NotifyFileSaved(); + signals: + + /** + * @brief this signal is emitted when the bytes has been append in texteditor. + * + */ + void SignalUIBytesDisplayed(); + private: std::shared_ptr<Ui_PlainTextEditor> ui_; ///< QString full_file_path_; ///< The path to the file handled in the tab bool sign_marked_{}; ///< true, if the signed header is marked, false if not - bool read_done_ = false; ///< - QThread* read_thread_ = nullptr; ///< - bool binary_mode_ = false; ///< - size_t read_bytes_ = 0; ///< - std::string charset_name_; ///< - std::string language_name_; ///< - int32_t charset_confidence_; ///< - bool is_crlf_ = false; ///< + bool read_done_ = false; ///< + bool binary_mode_ = false; ///< + size_t read_bytes_ = 0; ///< + std::string charset_name_; ///< + std::string language_name_; ///< + int32_t charset_confidence_; ///< + bool is_crlf_ = false; ///< /** * @brief @@ -157,7 +158,7 @@ class PlainTextEditorPage : public QWidget { * * @param data */ - void slot_insert_text(const std::string& data); + void slot_insert_text(QByteArray bytes_data); }; } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp index 689f877b..713dbb80 100644 --- a/src/ui/widgets/TextEdit.cpp +++ b/src/ui/widgets/TextEdit.cpp @@ -315,9 +315,7 @@ bool TextEdit::maybe_save_current_tab(bool askToSave) { return false; } } - - // destroy - page->PrepareToDestroy(); + page->deleteLater(); return true; } |