diff --git a/core/QueuedBackgroundTask.cpp b/core/QueuedBackgroundTask.cpp index d7f22ce..d7ed17e 100644 --- a/core/QueuedBackgroundTask.cpp +++ b/core/QueuedBackgroundTask.cpp @@ -1,4 +1,64 @@ #include "QueuedBackgroundTask.h" +#include "ScopeGuard.h" -void QueuedBackgroundTask::requestPause() -{} +//void QueuedBackgroundTask::requestPause() +//{} + +QueuedBackgroundTask::QueuedBackgroundTask(CompletionFunction on_completion) + : m_completionFunction(on_completion) +{ + setAutoDelete(false); +} + +QueuedBackgroundTask::~QueuedBackgroundTask() +{ + cancel(); + wait(); +} + +void QueuedBackgroundTask::cancel() +{ + QMutexLocker lock(&stateMutex); + if (!m_finished) + m_canceled = true; +} + +void QueuedBackgroundTask::wait() +{ + QMutexLocker lock(&stateMutex); + if (!m_finished) + finished.wait(&stateMutex); +} + +bool QueuedBackgroundTask::hasException() const +{ + return doRunException != nullptr; +} + +void QueuedBackgroundTask::rethrow() +{ + std::rethrow_exception(doRunException); +} + +void QueuedBackgroundTask::run() +{ + SCOPE_EXIT { + m_finished = true; + QMutexLocker lock(&stateMutex); + finished.notify_all(); + }; + QMutexLocker lock(&stateMutex); + m_started = true; + if (!m_canceled) { + lock.unlock(); + try { + doRun(); + } + catch (...) { + doRunException = std::current_exception(); + } + + if (!m_canceled && m_completionFunction) + m_completionFunction(this); + } +} diff --git a/core/QueuedBackgroundTask.h b/core/QueuedBackgroundTask.h index a4dcdb4..49fa2d5 100644 --- a/core/QueuedBackgroundTask.h +++ b/core/QueuedBackgroundTask.h @@ -1,52 +1,81 @@ -#pragma once +#pragma once +#include #include -#include +//#include +#include +#include /** Base class for an object that can be queued for execution in the background. * - * It is meaned for long running processes the end user might want to abort, pause and check progress on. + * It is meaned for long running processes the end user might want to abort, + * pause and check progress on. */ -class QueuedBackgroundTask: public QRunnable -{ - /** Task should exit it's run method but remember it's state, it might be resumed later. - */ - void requestPause(); - - /** Request to stop running and forget progress - */ - void requestAbort(); - - QString getProgressInfo(); -}; - -class BackgroundTaskInfo { -public: -}; - -/** Manages objects of type QueuedBackgroundTask - * - * The basic operation of this queue is hand everything directly of to the default - * threadpool and that will decide when to run each task. But extra functions are - * provided to control tasks to allow the program to have a UI for controlling the tasks - */ -class BackgroundTaskQueue { - +class QueuedBackgroundTask: public QRunnable { public: + using CompletionFunction = std::function; + + explicit QueuedBackgroundTask(CompletionFunction on_completion); + virtual ~QueuedBackgroundTask(); + + void cancel(); + void wait(); + bool hasException() const; + void rethrow(); + +protected: + + bool m_canceled = false; + bool m_started = false; + bool m_finished = false; + std::exception_ptr doRunException = nullptr; + CompletionFunction m_completionFunction = nullptr; + + QMutex stateMutex; + QWaitCondition finished; + + virtual void run() override; + + virtual void doRun() = 0; + +// /** Task should exit it's run method but remember it's state, it might be resumed later. +// */ +// void requestPause(); - /** Returns a list of tasks in the queue and their progress. - * - * When building a UI to show realtime activity then make sure to first register the update events - * then get the snapshot. After that you can use the events to update the contents of the UI. - */ - QVector getTaskInfoSnapshort(); - - void pauseTask( ); - - /** Resume a paused task - */ - void resumeTask( ); - - void abortTask( ); +// /** Request to stop running and forget progress +// */ +// void requestAbort(); +// QString getProgressInfo(); }; + +//class BackgroundTaskInfo { +//public: +//}; + +///** Manages objects of type QueuedBackgroundTask +// * +// * The basic operation of this queue is hand everything directly of to the default +// * threadpool and that will decide when to run each task. But extra functions are +// * provided to control tasks to allow the program to have a UI for controlling the tasks +// */ +//class BackgroundTaskQueue { + +//public: + +// /** Returns a list of tasks in the queue and their progress. +// * +// * When building a UI to show realtime activity then make sure to first register the update events +// * then get the snapshot. After that you can use the events to update the contents of the UI. +// */ +// QVector getTaskInfoSnapshort(); + +// void pauseTask( ); + +// /** Resume a paused task +// */ +// void resumeTask( ); + +// void abortTask( ); + +//}; diff --git a/pglab/MainWindow.cpp b/pglab/MainWindow.cpp index be3287f..360a3c5 100644 --- a/pglab/MainWindow.cpp +++ b/pglab/MainWindow.cpp @@ -15,6 +15,7 @@ #include "util.h" #include "MasterController.h" #include "WorkManager.h" +#include "ScopeGuard.h" namespace pg = Pgsql; @@ -30,6 +31,7 @@ MainWindow::MainWindow(MasterController *master, QWidget *parent) MainWindow::~MainWindow() { + loader.reset(); delete ui; } @@ -60,13 +62,10 @@ void MainWindow::setConfig(const ConnectionConfig &config) title += m_config.name().c_str(); setWindowTitle(title); - WorkManager::getWorkManager()->addWork( - [config, this]() { - auto res = OpenDatabase::createOpenDatabase(config); - QueueTask([this, res] () { - catalogLoaded(res); - }); - }); + loader = std::make_shared(m_config, [this](QRunnable *) { + QueueTask([this] () { catalogLoaded(); }); + } ); + WorkManager::getWorkManager()->addRunnable(loader.get()); } catch (std::runtime_error &ex) { QMessageBox::critical(this, "Error reading database", QString::fromUtf8(ex.what())); @@ -75,11 +74,11 @@ void MainWindow::setConfig(const ConnectionConfig &config) } } -void MainWindow::catalogLoaded(Expected res) +void MainWindow::catalogLoaded() { try { - - m_database = res.get(); + SCOPE_EXIT { loader.reset(); }; + m_database = loader->GetResult(); auto tt = new TablesPage(this); tt->setCatalog(m_database->catalogue()); diff --git a/pglab/MainWindow.h b/pglab/MainWindow.h index e727c4f..48123fc 100644 --- a/pglab/MainWindow.h +++ b/pglab/MainWindow.h @@ -6,14 +6,20 @@ #include "OpenDatabase.h" #include #include "ASyncWindow.h" +#include +#include #include #include #include #include "Pgsql_Connection.h" +#include "QueuedBackgroundTask.h" #include #include #include +#include +#include + namespace Ui { class MainWindow; } @@ -45,18 +51,33 @@ private: MasterController *m_masterController; -// class OpenDocumentController { -// public: + class QLoad : public QueuedBackgroundTask { + public: + explicit QLoad(ConnectionConfig config, CompletionFunction on_completion) + : QueuedBackgroundTask(on_completion) + , m_config(config) + {} -// private: + OpenDatabase::OpenDatabaseSPtr GetResult() + { + if (hasException()) rethrow(); + return result; + } + protected: -// QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); -// QDir dir(path); -// if (!dir.exists()) { -// dir.mkpath("."); -// } + OpenDatabase::OpenDatabaseSPtr result; + virtual void doRun() + { + auto res = OpenDatabase::createOpenDatabase(m_config); + result = res.get(); + } + private: + + ConnectionConfig m_config; + }; + + std::shared_ptr loader; -// }; QueryTab *GetActiveQueryTab(); @@ -64,7 +85,7 @@ private: void showEvent(QShowEvent *event); QueryTab *newSqlPage(); - void catalogLoaded(Expected result); + void catalogLoaded(); private slots: