send quit event during shutdown on thread, with timeout

This commit is contained in:
Stephen Birarda 2017-06-01 10:43:47 -07:00 committed by Chris Collins
parent f5d732f332
commit 40dfcb1e6e
5 changed files with 64 additions and 33 deletions

View file

@ -11,6 +11,8 @@
#include "Application.h" #include "Application.h"
#include <thread>
#include <gl/Config.h> #include <gl/Config.h>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtx/component_wise.hpp> #include <glm/gtx/component_wise.hpp>
@ -1570,9 +1572,13 @@ void Application::aboutToQuit() {
getActiveDisplayPlugin()->deactivate(); getActiveDisplayPlugin()->deactivate();
// use the ClosureEventSender via an std::thread (to not use QThread while the application is going down) // use the ClosureEventSender via a QThread to send an event that says the user asked for the app to close
// to send an event that says the user asked for the app to close auto closureEventSender = DependencyManager::get<ClosureEventSender>();
_userQuitThread = std::thread { &ClosureEventSender::sendQuitStart, DependencyManager::get<ClosureEventSender>() }; QThread* closureEventThread = new QThread(this);
closureEventSender->moveToThread(closureEventThread);
// sendQuitEventAsync will bail immediately if the UserActivityLogger is not enabled
connect(closureEventThread, &QThread::started, closureEventSender.data(), &ClosureEventSender::sendQuitEventAsync);
closureEventThread->start();
// Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown. // Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown.
DependencyManager::get<OffscreenUi>()->hide("RunningScripts"); DependencyManager::get<OffscreenUi>()->hide("RunningScripts");
@ -1738,6 +1744,15 @@ Application::~Application() {
_window->deleteLater(); _window->deleteLater();
// make sure that the quit event has finished sending before we take the application down
auto closureEventSender = DependencyManager::get<ClosureEventSender>();
while (!closureEventSender->hasFinishedQuitEvent() && !closureEventSender->hasTimedOutQuitEvent()) {
// yield so we're not spinning
std::this_thread::yield();
}
// quit the thread used by the closure event sender
closureEventSender->thread()->quit();
// Can't log to file passed this point, FileLogger about to be deleted // Can't log to file passed this point, FileLogger about to be deleted
qInstallMessageHandler(LogHandler::verboseMessageHandler); qInstallMessageHandler(LogHandler::verboseMessageHandler);
} }

View file

@ -13,7 +13,6 @@
#define hifi_Application_h #define hifi_Application_h
#include <functional> #include <functional>
#include <thread>
#include <QtCore/QHash> #include <QtCore/QHash>
#include <QtCore/QPointer> #include <QtCore/QPointer>
@ -681,8 +680,6 @@ private:
FileScriptingInterface* _fileDownload; FileScriptingInterface* _fileDownload;
AudioInjector* _snapshotSoundInjector { nullptr }; AudioInjector* _snapshotSoundInjector { nullptr };
SharedSoundPointer _snapshotSound; SharedSoundPointer _snapshotSound;
std::thread _userQuitThread;
}; };

View file

@ -24,13 +24,11 @@
#include <SandboxUtils.h> #include <SandboxUtils.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "AddressManager.h" #include "AddressManager.h"
#include "Application.h" #include "Application.h"
#include "InterfaceLogging.h" #include "InterfaceLogging.h"
#include "UserActivityLogger.h" #include "UserActivityLogger.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "networking/ClosureEventSender.h"
#ifdef HAS_BUGSPLAT #ifdef HAS_BUGSPLAT
#include <BugSplat.h> #include <BugSplat.h>
@ -268,12 +266,6 @@ int main(int argc, const char* argv[]) {
Application::shutdownPlugins(); Application::shutdownPlugins();
if (UserActivityLogger::getInstance().isEnabled()) {
// send a quit finished event here to indicate that this session closed cleanly
std::thread quitCompleteThread { &::ClosureEventSender::sendQuitFinish, DependencyManager::get<ClosureEventSender>() };
quitCompleteThread.join();
}
qCDebug(interfaceapp, "Normal exit."); qCDebug(interfaceapp, "Normal exit.");
#if !defined(DEBUG) && !defined(Q_OS_LINUX) #if !defined(DEBUG) && !defined(Q_OS_LINUX)
// HACK: exit immediately (don't handle shutdown callbacks) for Release build // HACK: exit immediately (don't handle shutdown callbacks) for Release build

View file

@ -9,14 +9,15 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QtCore/QDateTime>
#include <QtCore/QEventLoop> #include <QtCore/QEventLoop>
#include <QtCore/QJsonDocument> #include <QtCore/QJsonDocument>
#include <QtNetwork/QHttpMultiPart>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <AccountManager.h> #include <AccountManager.h>
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
#include <NetworkingConstants.h> #include <NetworkingConstants.h>
#include <NetworkLogging.h>
#include <UserActivityLogger.h> #include <UserActivityLogger.h>
#include <UUID.h> #include <UUID.h>
@ -43,11 +44,13 @@ QNetworkRequest createNetworkRequest() {
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setPriority(QNetworkRequest::HighPriority);
return request; return request;
} }
QByteArray postDataForAction(QString action) { QByteArray postDataForAction(QString action) {
return QString("{\"action\": \"" + action + "\"}").toUtf8(); return QString("{\"action_name\": \"" + action + "\"}").toUtf8();
} }
QNetworkReply* replyForAction(QString action) { QNetworkReply* replyForAction(QString action) {
@ -55,17 +58,33 @@ QNetworkReply* replyForAction(QString action) {
return networkAccessManager.post(createNetworkRequest(), postDataForAction(action)); return networkAccessManager.post(createNetworkRequest(), postDataForAction(action));
} }
void ClosureEventSender::sendQuitStart() { void ClosureEventSender::sendQuitEventAsync() {
if (UserActivityLogger::getInstance().isEnabled()) {
QNetworkReply* reply = replyForAction("quit_start"); QNetworkReply* reply = replyForAction("quit");
connect(reply, &QNetworkReply::finished, this, &ClosureEventSender::handleQuitEventFinished);
QEventLoop loop; _quitEventStartTimestamp = QDateTime::currentMSecsSinceEpoch();
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); } else {
_hasFinishedQuitEvent = true;
}
} }
void ClosureEventSender::sendQuitFinish() { void ClosureEventSender::handleQuitEventFinished() {
QNetworkReply* reply = replyForAction("quit_finish"); _hasFinishedQuitEvent = true;
QEventLoop loop; auto reply = qobject_cast<QNetworkReply*>(sender());
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); if (reply->error() == QNetworkReply::NoError) {
qCDebug(networking) << "Quit event sent successfully";
} else {
qCDebug(networking) << "Failed to send quit event -" << reply->errorString();
}
reply->deleteLater();
} }
bool ClosureEventSender::hasTimedOutQuitEvent() {
const int CLOSURE_EVENT_TIMEOUT_MS = 5000;
return _quitEventStartTimestamp != 0
&& QDateTime::currentMSecsSinceEpoch() - _quitEventStartTimestamp > CLOSURE_EVENT_TIMEOUT_MS;
}

View file

@ -12,24 +12,32 @@
#ifndef hifi_ClosureEventSender_h #ifndef hifi_ClosureEventSender_h
#define hifi_ClosureEventSender_h #define hifi_ClosureEventSender_h
#include <atomic>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QUuid> #include <QtCore/QUuid>
#include <DependencyManager.h> #include <DependencyManager.h>
class ClosureEventSender : public Dependency { class ClosureEventSender : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY SINGLETON_DEPENDENCY
public: public:
void setSessionID(QUuid sessionID) { _sessionID = sessionID; } void sendCrashEventSync();
void sendQuitStart(); bool hasTimedOutQuitEvent();
void sendQuitFinish(); bool hasFinishedQuitEvent() { return _hasFinishedQuitEvent; }
void sendCrashEvent();
public slots:
void sendQuitEventAsync();
private slots:
void handleQuitEventFinished();
private: private:
QUuid _sessionID; std::atomic<bool> _hasFinishedQuitEvent { false };
QString _accessToken; std::atomic<int64_t> _quitEventStartTimestamp;
}; };
#endif // hifi_ClosureEventSender_h #endif // hifi_ClosureEventSender_h