The MainWindow now uses a background task to load the catalog.
As the background task can't be cancelled yet only closing of the window can block if the task is still running.
This commit is contained in:
parent
057e745ebe
commit
2705a3417b
4 changed files with 173 additions and 64 deletions
|
|
@ -1,4 +1,64 @@
|
||||||
#include "QueuedBackgroundTask.h"
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,81 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QVector>
|
//#include <QVector>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
|
||||||
/** Base class for an object that can be queued for execution in the background.
|
/** 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
|
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:
|
public:
|
||||||
|
using CompletionFunction = std::function<void(QRunnable *)>;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// /** Request to stop running and forget progress
|
||||||
|
// */
|
||||||
|
// void requestAbort();
|
||||||
|
|
||||||
|
// QString getProgressInfo();
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Manages objects of type QueuedBackgroundTask
|
//class BackgroundTaskInfo {
|
||||||
*
|
//public:
|
||||||
* 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:
|
///** 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 {
|
||||||
|
|
||||||
/** Returns a list of tasks in the queue and their progress.
|
//public:
|
||||||
*
|
|
||||||
* 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<BackgroundTaskInfo> getTaskInfoSnapshort();
|
|
||||||
|
|
||||||
void pauseTask( );
|
// /** 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<BackgroundTaskInfo> getTaskInfoSnapshort();
|
||||||
|
|
||||||
/** Resume a paused task
|
// void pauseTask( );
|
||||||
*/
|
|
||||||
void resumeTask( );
|
|
||||||
|
|
||||||
void abortTask( );
|
// /** Resume a paused task
|
||||||
|
// */
|
||||||
|
// void resumeTask( );
|
||||||
|
|
||||||
};
|
// void abortTask( );
|
||||||
|
|
||||||
|
//};
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "MasterController.h"
|
#include "MasterController.h"
|
||||||
#include "WorkManager.h"
|
#include "WorkManager.h"
|
||||||
|
#include "ScopeGuard.h"
|
||||||
|
|
||||||
namespace pg = Pgsql;
|
namespace pg = Pgsql;
|
||||||
|
|
||||||
|
|
@ -30,6 +31,7 @@ MainWindow::MainWindow(MasterController *master, QWidget *parent)
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
|
loader.reset();
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,13 +62,10 @@ void MainWindow::setConfig(const ConnectionConfig &config)
|
||||||
title += m_config.name().c_str();
|
title += m_config.name().c_str();
|
||||||
setWindowTitle(title);
|
setWindowTitle(title);
|
||||||
|
|
||||||
WorkManager::getWorkManager()->addWork(
|
loader = std::make_shared<QLoad>(m_config, [this](QRunnable *) {
|
||||||
[config, this]() {
|
QueueTask([this] () { catalogLoaded(); });
|
||||||
auto res = OpenDatabase::createOpenDatabase(config);
|
|
||||||
QueueTask([this, res] () {
|
|
||||||
catalogLoaded(res);
|
|
||||||
});
|
|
||||||
} );
|
} );
|
||||||
|
WorkManager::getWorkManager()->addRunnable(loader.get());
|
||||||
} catch (std::runtime_error &ex) {
|
} catch (std::runtime_error &ex) {
|
||||||
QMessageBox::critical(this, "Error reading database",
|
QMessageBox::critical(this, "Error reading database",
|
||||||
QString::fromUtf8(ex.what()));
|
QString::fromUtf8(ex.what()));
|
||||||
|
|
@ -75,11 +74,11 @@ void MainWindow::setConfig(const ConnectionConfig &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::catalogLoaded(Expected<OpenDatabase::OpenDatabaseSPtr> res)
|
void MainWindow::catalogLoaded()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
SCOPE_EXIT { loader.reset(); };
|
||||||
m_database = res.get();
|
m_database = loader->GetResult();
|
||||||
|
|
||||||
auto tt = new TablesPage(this);
|
auto tt = new TablesPage(this);
|
||||||
tt->setCatalog(m_database->catalogue());
|
tt->setCatalog(m_database->catalogue());
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,20 @@
|
||||||
#include "OpenDatabase.h"
|
#include "OpenDatabase.h"
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include "ASyncWindow.h"
|
#include "ASyncWindow.h"
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QRunnable>
|
||||||
#include <QSocketNotifier>
|
#include <QSocketNotifier>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include "Pgsql_Connection.h"
|
#include "Pgsql_Connection.h"
|
||||||
|
#include "QueuedBackgroundTask.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
}
|
}
|
||||||
|
|
@ -45,18 +51,33 @@ private:
|
||||||
|
|
||||||
MasterController *m_masterController;
|
MasterController *m_masterController;
|
||||||
|
|
||||||
// class OpenDocumentController {
|
class QLoad : public QueuedBackgroundTask {
|
||||||
// public:
|
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);
|
OpenDatabase::OpenDatabaseSPtr result;
|
||||||
// QDir dir(path);
|
virtual void doRun()
|
||||||
// if (!dir.exists()) {
|
{
|
||||||
// dir.mkpath(".");
|
auto res = OpenDatabase::createOpenDatabase(m_config);
|
||||||
// }
|
result = res.get();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
|
||||||
|
ConnectionConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<QLoad> loader;
|
||||||
|
|
||||||
// };
|
|
||||||
|
|
||||||
QueryTab *GetActiveQueryTab();
|
QueryTab *GetActiveQueryTab();
|
||||||
|
|
||||||
|
|
@ -64,7 +85,7 @@ private:
|
||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent *event);
|
||||||
QueryTab *newSqlPage();
|
QueryTab *newSqlPage();
|
||||||
|
|
||||||
void catalogLoaded(Expected<OpenDatabase::OpenDatabaseSPtr> result);
|
void catalogLoaded();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue