From 54b1a79cb6f90ac9b26db57e616dc750fb2c6b54 Mon Sep 17 00:00:00 2001 From: Maxim Zakharov Date: Thu, 15 Mar 2018 13:26:24 +0530 Subject: [PATCH] Sporadically crashes on Windows when exit. Fixes #3177 1) Shutdown the python server properly. 2) Disabled "Shutdown server" menu till server is not successfully started. Initial patch sent by Maxim, modified by Akshay Joshi. --- runtime/Server.cpp | 11 +++++++++ runtime/Server.h | 3 +++ runtime/TrayIcon.cpp | 11 +++++++++ runtime/TrayIcon.h | 4 ++++ runtime/pgAdmin4.cpp | 46 ++++++++++++++++++++++++++++++++++++ runtime/pgAdmin4.h | 1 + web/pgadmin/misc/__init__.py | 16 ++++++++++++- 7 files changed, 91 insertions(+), 1 deletion(-) diff --git a/runtime/Server.cpp b/runtime/Server.cpp index d31be2118..805772bef 100644 --- a/runtime/Server.cpp +++ b/runtime/Server.cpp @@ -328,3 +328,14 @@ void Server::run() fclose(cp); } +void Server::shutdown(QUrl url) +{ + bool shotdown = shutdownServer(url); + if (!shotdown) + setError(tr("Failed to shutdown application server thread.")); + + QThread::quit(); + QThread::wait(); + while(!this->isFinished()){} +} + diff --git a/runtime/Server.h b/runtime/Server.h index 97e5ca3a3..7d9831cc0 100644 --- a/runtime/Server.h +++ b/runtime/Server.h @@ -29,6 +29,9 @@ public: bool Init(); QString getError() { return m_error; } +public slots: + void shutdown(QUrl url); + protected: void run(); diff --git a/runtime/TrayIcon.cpp b/runtime/TrayIcon.cpp index aefc58125..4cf1eff22 100644 --- a/runtime/TrayIcon.cpp +++ b/runtime/TrayIcon.cpp @@ -148,6 +148,7 @@ void TrayIcon::createActions() connect(m_logAction, SIGNAL(triggered()), this, SLOT(onLog())); m_quitAction = new QAction(tr("&Shutdown server"), this); + m_quitAction->setEnabled(false); connect(m_quitAction, SIGNAL(triggered()), this, SLOT(onQuit())); } @@ -239,6 +240,16 @@ void TrayIcon::onQuit() { if (QMessageBox::Yes == QMessageBox::question(this, tr("Shutdown server?"), QString(tr("Are you sure you want to shutdown the %1 server?")).arg(PGA_APP_NAME), QMessageBox::Yes | QMessageBox::No)) { + // Emit the signal to shutdown the python server. + emit shutdownSignal(m_appServerUrl); exit(0); } } + +void TrayIcon::enableShutdownMenu() +{ + if (m_quitAction != NULL) + { + m_quitAction->setEnabled(true); + } +} diff --git a/runtime/TrayIcon.h b/runtime/TrayIcon.h index f6538123d..48c4462d0 100644 --- a/runtime/TrayIcon.h +++ b/runtime/TrayIcon.h @@ -31,6 +31,7 @@ public: bool Init(); void setAppServerUrl(QString appServerUrl); + void enableShutdownMenu(); private: void createTrayIcon(); @@ -56,6 +57,9 @@ private slots: void onConfig(); void onLog(); void onQuit(); + +signals: + void shutdownSignal(QUrl); }; #endif // TRAYICON_H diff --git a/runtime/pgAdmin4.cpp b/runtime/pgAdmin4.cpp index 89e119e00..e9490b620 100644 --- a/runtime/pgAdmin4.cpp +++ b/runtime/pgAdmin4.cpp @@ -239,6 +239,7 @@ int main(int argc, char * argv[]) exit(1); } + QObject::connect(server, SIGNAL(finished()), server, SLOT(deleteLater())); server->start(); // This is a hack to give the server a chance to start and potentially fail. As @@ -336,6 +337,8 @@ int main(int argc, char * argv[]) // Go! trayicon->setAppServerUrl(appServerUrl); + // Enable the shutdown server menu as server started successfully. + trayicon->enableShutdownMenu(); QString cmd = settings.value("BrowserCommand").toString(); @@ -355,6 +358,8 @@ int main(int argc, char * argv[]) } } + QObject::connect(trayicon, SIGNAL(shutdownSignal(QUrl)), server, SLOT(shutdown(QUrl))); + splash->finish(NULL); return app.exec(); @@ -435,3 +440,44 @@ unsigned long sdbm(unsigned char *str) return hash; } + +// Shutdown the application server +bool shutdownServer(QUrl url) +{ + QNetworkAccessManager manager; + QEventLoop loop; + QNetworkReply *reply; + QVariant redirectUrl; + + url.setPath("/misc/shutdown"); + + do + { + reply = manager.get(QNetworkRequest(url)); + + QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + + redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + url = redirectUrl.toUrl(); + + if (!redirectUrl.isNull()) + delete reply; + + } while (!redirectUrl.isNull()); + + if (reply->error() != QNetworkReply::NoError) + { + return false; + } + + QString response = reply->readAll(); + + if (response != "SHUTDOWN") + { + qDebug() << "Failed to connect, server response: " << response; + return false; + } + + return true; +} diff --git a/runtime/pgAdmin4.h b/runtime/pgAdmin4.h index 9d7135f26..63653bd0f 100644 --- a/runtime/pgAdmin4.h +++ b/runtime/pgAdmin4.h @@ -39,5 +39,6 @@ bool PingServer(QUrl url); void delay(int milliseconds); void cleanup(); unsigned long sdbm(unsigned char *str); +bool shutdownServer(QUrl url); #endif // PGADMIN4_H diff --git a/web/pgadmin/misc/__init__.py b/web/pgadmin/misc/__init__.py index 45eb5983c..848e6ee24 100644 --- a/web/pgadmin/misc/__init__.py +++ b/web/pgadmin/misc/__init__.py @@ -10,7 +10,7 @@ """A blueprint module providing utility functions for the application.""" import pgadmin.utils.driver as driver -from flask import url_for, render_template, Response +from flask import url_for, render_template, Response, request from flask_babel import gettext from pgadmin.utils import PgAdminModule from pgadmin.utils.preferences import Preferences @@ -116,3 +116,17 @@ def explain_js(): status=200, mimetype="application/javascript" ) + +########################################################################## +# A special URL used to shutdown the server +########################################################################## +@blueprint.route("/shutdown", methods=('get', 'post')) +def shutdown(): + if config.SERVER_MODE is not True: + func = request.environ.get('werkzeug.server.shutdown') + if func is None: + raise RuntimeError('Not running with the Werkzeug Server') + func() + return 'SHUTDOWN' + else: + return ''