Showing the changes to interface/src/Application.cpp (1 of 4 files). Show all
Unified · Split
| @@ -235,6 +235,42 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi · 234 unmodified lines | ||
| 235 | 235 | { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl } |
| 236 | 236 | }; |
| 237 | 237 | |
| 238 | +class DeadlockWatchdogThread : public QThread { | |
| 239 | +public: | |
| 240 | + static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1; | |
| 241 | + static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1; | |
| 242 | + static const unsigned long MAX_HEARTBEAT_AGE_USECS = 10 * USECS_PER_SECOND; | |
| 243 | + | |
| 244 | + // Set the heartbeat on launch | |
| 245 | + DeadlockWatchdogThread() { | |
| 246 | + QTimer* heartbeatTimer = new QTimer(); | |
| 247 | + connect(heartbeatTimer, &QTimer::timeout, [this] { | |
| 248 | + _heartbeat = usecTimestampNow(); | |
| 249 | + }); | |
| 250 | + heartbeatTimer->start(HEARTBEAT_UPDATE_INTERVAL_SECS * MSECS_PER_SECOND); | |
| 251 | + } | |
| 252 | + | |
| 253 | + void deadlockDetectionCrash() { | |
| 254 | + uint32_t* crashTrigger = nullptr; | |
| 255 | + *crashTrigger = 0xDEAD10CC; | |
| 256 | + } | |
| 257 | + | |
| 258 | + void run() override { | |
| 259 | + while (!qApp->isAboutToQuit()) { | |
| 260 | + QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); | |
| 261 | + auto now = usecTimestampNow(); | |
| 262 | + auto lastHeartbeatAge = now - _heartbeat; | |
| 263 | + if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) { | |
| 264 | + deadlockDetectionCrash(); | |
| 265 | + } | |
| 266 | + } | |
| 267 | + } | |
| 268 | + | |
| 269 | + static std::atomic<uint64_t> _heartbeat; | |
| 270 | +}; | |
| 271 | + | |
| 272 | +std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat; | |
| 273 | + | |
| 238 | 274 | #ifdef Q_OS_WIN |
| 239 | 275 | class MyNativeEventFilter : public QAbstractNativeEventFilter { |
| 240 | 276 | public: |
| @@ -457,6 +493,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 216 unmodified lines | ||
| 457 | 493 | |
| 458 | 494 | auto nodeList = DependencyManager::get<NodeList>(); |
| 459 | 495 | |
| 496 | + // Set up a watchdog thread to intentionally crash the application on deadlocks | |
| 497 | + (new DeadlockWatchdogThread())->start(); | |
| 498 | + | |
| 460 | 499 | qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion()); |
| 461 | 500 | |
| 462 | 501 | _bookmarks = new Bookmarks(); // Before setting up the menu |
| @@ -4960,6 +4999,15 @@ void Application::crashApplication() { · 4497 unmodified lines | ||
| 4960 | 4999 | Q_UNUSED(value); |
| 4961 | 5000 | } |
| 4962 | 5001 | |
| 5002 | +void Application::deadlockApplication() { | |
| 5003 | + qCDebug(interfaceapp) << "Intentionally deadlocked Interface"; | |
| 5004 | + // Using a loop that will *technically* eventually exit (in ~600 billion years) | |
| 5005 | + // to avoid compiler warnings about a loop that will never exit | |
| 5006 | + for (uint64_t i = 1; i != 0; ++i) { | |
| 5007 | + QThread::sleep(1); | |
| 5008 | + } | |
| 5009 | +} | |
| 5010 | + | |
| 4963 | 5011 | void Application::setActiveDisplayPlugin(const QString& pluginName) { |
| 4964 | 5012 | auto menu = Menu::getInstance(); |
| 4965 | 5013 | foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { |