diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 297ee943fa..a637f9414b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -11,6 +11,8 @@ #include "Application.h" +#include + #include #include #include @@ -1570,9 +1572,13 @@ void Application::aboutToQuit() { getActiveDisplayPlugin()->deactivate(); - // use the ClosureEventSender via an std::thread (to not use QThread while the application is going down) - // to send an event that says the user asked for the app to close - _userQuitThread = std::thread { &ClosureEventSender::sendQuitStart, DependencyManager::get() }; + // use the ClosureEventSender via a QThread to send an event that says the user asked for the app to close + auto closureEventSender = DependencyManager::get(); + 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. DependencyManager::get()->hide("RunningScripts"); @@ -1738,6 +1744,15 @@ Application::~Application() { _window->deleteLater(); + // make sure that the quit event has finished sending before we take the application down + auto closureEventSender = DependencyManager::get(); + 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 qInstallMessageHandler(LogHandler::verboseMessageHandler); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 23673399f6..9cf03f1cef 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -13,7 +13,6 @@ #define hifi_Application_h #include -#include #include #include @@ -681,8 +680,6 @@ private: FileScriptingInterface* _fileDownload; AudioInjector* _snapshotSoundInjector { nullptr }; SharedSoundPointer _snapshotSound; - - std::thread _userQuitThread; }; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 68525dfd1e..63738d2d91 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -24,13 +24,11 @@ #include #include - #include "AddressManager.h" #include "Application.h" #include "InterfaceLogging.h" #include "UserActivityLogger.h" #include "MainWindow.h" -#include "networking/ClosureEventSender.h" #ifdef HAS_BUGSPLAT #include @@ -268,12 +266,6 @@ int main(int argc, const char* argv[]) { 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() }; - quitCompleteThread.join(); - } - qCDebug(interfaceapp, "Normal exit."); #if !defined(DEBUG) && !defined(Q_OS_LINUX) // HACK: exit immediately (don't handle shutdown callbacks) for Release build diff --git a/interface/src/networking/ClosureEventSender.cpp b/interface/src/networking/ClosureEventSender.cpp index f514fa17b6..238629b809 100644 --- a/interface/src/networking/ClosureEventSender.cpp +++ b/interface/src/networking/ClosureEventSender.cpp @@ -9,14 +9,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include -#include #include #include #include #include +#include #include #include @@ -43,11 +44,13 @@ QNetworkRequest createNetworkRequest() { request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + request.setPriority(QNetworkRequest::HighPriority); + return request; } QByteArray postDataForAction(QString action) { - return QString("{\"action\": \"" + action + "\"}").toUtf8(); + return QString("{\"action_name\": \"" + action + "\"}").toUtf8(); } QNetworkReply* replyForAction(QString action) { @@ -55,17 +58,33 @@ QNetworkReply* replyForAction(QString action) { return networkAccessManager.post(createNetworkRequest(), postDataForAction(action)); } -void ClosureEventSender::sendQuitStart() { - - QNetworkReply* reply = replyForAction("quit_start"); - - QEventLoop loop; - QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); +void ClosureEventSender::sendQuitEventAsync() { + if (UserActivityLogger::getInstance().isEnabled()) { + QNetworkReply* reply = replyForAction("quit"); + connect(reply, &QNetworkReply::finished, this, &ClosureEventSender::handleQuitEventFinished); + _quitEventStartTimestamp = QDateTime::currentMSecsSinceEpoch(); + } else { + _hasFinishedQuitEvent = true; + } } -void ClosureEventSender::sendQuitFinish() { - QNetworkReply* reply = replyForAction("quit_finish"); +void ClosureEventSender::handleQuitEventFinished() { + _hasFinishedQuitEvent = true; - QEventLoop loop; - QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + auto reply = qobject_cast(sender()); + 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; +} + + diff --git a/interface/src/networking/ClosureEventSender.h b/interface/src/networking/ClosureEventSender.h index dc726fc386..be2daca12b 100644 --- a/interface/src/networking/ClosureEventSender.h +++ b/interface/src/networking/ClosureEventSender.h @@ -12,24 +12,32 @@ #ifndef hifi_ClosureEventSender_h #define hifi_ClosureEventSender_h +#include + #include #include #include -class ClosureEventSender : public Dependency { +class ClosureEventSender : public QObject, public Dependency { + Q_OBJECT SINGLETON_DEPENDENCY public: - void setSessionID(QUuid sessionID) { _sessionID = sessionID; } + void sendCrashEventSync(); - void sendQuitStart(); - void sendQuitFinish(); - void sendCrashEvent(); + bool hasTimedOutQuitEvent(); + bool hasFinishedQuitEvent() { return _hasFinishedQuitEvent; } + +public slots: + void sendQuitEventAsync(); + +private slots: + void handleQuitEventFinished(); private: - QUuid _sessionID; - QString _accessToken; + std::atomic _hasFinishedQuitEvent { false }; + std::atomic _quitEventStartTimestamp; }; #endif // hifi_ClosureEventSender_h