Fixing dangling thread issues, consolidating thread management

This commit is contained in:
Bradley Austin Davis 2017-06-06 12:42:28 -07:00
parent a36727732f
commit fc6a278217
22 changed files with 205 additions and 200 deletions

View file

@ -166,11 +166,7 @@ void Agent::run() {
// Setup MessagesClient
auto messagesClient = DependencyManager::set<MessagesClient>();
QThread* messagesThread = new QThread;
messagesThread->setObjectName("Messages Client Thread");
messagesClient->moveToThread(messagesThread);
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
messagesThread->start();
messagesClient->startThread();
// make sure we hear about connected nodes so we can grab an ATP script if a request is pending
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &Agent::nodeActivated);

View file

@ -69,17 +69,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
DependencyManager::set<ResourceScriptingInterface>();
DependencyManager::set<UserActivityLoggerScriptingInterface>();
// setup a thread for the NodeList and its PacketReceiver
QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("NodeList Thread");
nodeThread->start();
// make sure the node thread is given highest priority
nodeThread->setPriority(QThread::TimeCriticalPriority);
// put the NodeList on the node thread
nodeList->moveToThread(nodeThread);
nodeList->startThread();
// set the logging target to the the CHILD_TARGET_NAME
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
@ -166,14 +156,8 @@ void AssignmentClient::stopAssignmentClient() {
}
AssignmentClient::~AssignmentClient() {
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
// ask the node thread to quit and wait until it is done
nodeThread->quit();
nodeThread->wait();
}
void AssignmentClient::aboutToQuit() {

View file

@ -236,11 +236,7 @@ void EntityScriptServer::run() {
// Setup MessagesClient
auto messagesClient = DependencyManager::set<MessagesClient>();
QThread* messagesThread = new QThread;
messagesThread->setObjectName("Messages Client Thread");
messagesClient->moveToThread(messagesThread);
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
messagesThread->start();
messagesClient->startThread();
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
connect(&domainHandler, &DomainHandler::settingsReceived, this, &EntityScriptServer::handleSettings);

View file

@ -37,20 +37,12 @@ RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString
DependencyManager::set<AvatarHashMap>();
// get our audio client setup on its own thread
QThread* audioThread = new QThread();
auto audioClient = DependencyManager::set<AudioClient>();
audioClient->setPositionGetter(getPositionForAudio);
audioClient->setOrientationGetter(getOrientationForAudio);
audioClient->startThread();
audioClient->moveToThread(audioThread);
connect(audioThread, &QThread::started, audioClient.data(), &AudioClient::start);
connect(audioClient.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
audioThread->start();
connect(&_avatarTimer, &QTimer::timeout, this, &RenderingClient::sendAvatarPacket);
_avatarTimer.setInterval(16); // 60 FPS
_avatarTimer.start();

View file

@ -651,6 +651,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us
auto nodeList = DependencyManager::get<NodeList>();
nodeList->startThread();
// Set up a watchdog thread to intentionally crash the application on deadlocks
_deadlockWatchdogThread = new DeadlockWatchdogThread();
@ -676,25 +677,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
updateHeartbeat();
// start the nodeThread so its event loop is running
QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("NodeList Thread");
nodeThread->start();
// make sure the node thread is given highest priority
nodeThread->setPriority(QThread::TimeCriticalPriority);
// setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
// put the NodeList and datagram processing on the node thread
nodeList->moveToThread(nodeThread);
// put the audio processing on a separate thread
QThread* audioThread = new QThread();
audioThread->setObjectName("Audio Thread");
auto audioIO = DependencyManager::get<AudioClient>();
audioIO->setPositionGetter([]{
@ -710,7 +697,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY;
});
audioIO->moveToThread(audioThread);
recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {
audioIO->handleRecordedAudioInput(frame->data);
});
@ -724,9 +710,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
auto audioScriptingInterface = DependencyManager::set<AudioScriptingInterface>();
connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled);
connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer);
connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket);
@ -744,19 +727,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
});
audioThread->start();
audioIO->startThread();
ResourceManager::init();
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
// Setup MessagesClient
auto messagesClient = DependencyManager::get<MessagesClient>();
QThread* messagesThread = new QThread;
messagesThread->setObjectName("Messages Client Thread");
messagesClient->moveToThread(messagesThread);
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
messagesThread->start();
DependencyManager::get<MessagesClient>()->startThread();
const DomainHandler& domainHandler = nodeList->getDomainHandler();
@ -1648,12 +1626,7 @@ void Application::aboutToQuit() {
getActiveDisplayPlugin()->deactivate();
// use the CloseEventSender via a QThread to send an event that says the user asked for the app to close
auto closeEventSender = DependencyManager::get<CloseEventSender>();
QThread* closureEventThread = new QThread(this);
closeEventSender->moveToThread(closureEventThread);
// sendQuitEventAsync will bail immediately if the UserActivityLogger is not enabled
connect(closureEventThread, &QThread::started, closeEventSender.data(), &CloseEventSender::sendQuitEventAsync);
closureEventThread->start();
DependencyManager::get<CloseEventSender>()->startThread();
// Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown.
DependencyManager::get<OffscreenUi>()->hide("RunningScripts");
@ -1741,6 +1714,8 @@ void Application::cleanupBeforeQuit() {
// stop QML
DependencyManager::destroy<OffscreenUi>();
DependencyManager::destroy<OffscreenQmlSurfaceCache>();
if (_snapshotSoundInjector != nullptr) {
_snapshotSoundInjector->stop();
}
@ -1800,15 +1775,9 @@ Application::~Application() {
ResourceManager::cleanup();
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
// ask the node thread to quit and wait until it is done
nodeThread->quit();
nodeThread->wait();
Leapmotion::destroy();
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {

View file

@ -14,6 +14,7 @@
#include <QtCore/QJsonDocument>
#include <QtNetwork/QNetworkReply>
#include <ThreadHelpers.h>
#include <AccountManager.h>
#include <NetworkAccessManager.h>
#include <NetworkingConstants.h>
@ -87,4 +88,8 @@ bool CloseEventSender::hasTimedOutQuitEvent() {
&& QDateTime::currentMSecsSinceEpoch() - _quitEventStartTimestamp > CLOSURE_EVENT_TIMEOUT_MS;
}
void CloseEventSender::startThread() {
moveToNewNamedThread(this, "CloseEvent Logger Thread", [this] {
sendQuitEventAsync();
});
}

View file

@ -24,6 +24,7 @@ class CloseEventSender : public QObject, public Dependency {
SINGLETON_DEPENDENCY
public:
void startThread();
bool hasTimedOutQuitEvent();
bool hasFinishedQuitEvent() { return _hasFinishedQuitEvent; }

View file

@ -9,9 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtConcurrent/QtConcurrentRun>
#include <ThreadHelpers.h>
#include <src/InterfaceLogging.h>
#include <src/ui/AvatarInputs.h>
#include <QtConcurrent/QtConcurrentRun>
#include "LimitlessVoiceRecognitionScriptingInterface.h"
const float LimitlessVoiceRecognitionScriptingInterface::_audioLevelThreshold = 0.33f;
@ -24,9 +27,7 @@ LimitlessVoiceRecognitionScriptingInterface::LimitlessVoiceRecognitionScriptingI
connect(&_voiceTimer, &QTimer::timeout, this, &LimitlessVoiceRecognitionScriptingInterface::voiceTimeout);
connect(&_connection, &LimitlessConnection::onReceivedTranscription, this, [this](QString transcription){emit onReceivedTranscription(transcription);});
connect(&_connection, &LimitlessConnection::onFinishedSpeaking, this, [this](QString transcription){emit onFinishedSpeaking(transcription);});
_connection.moveToThread(&_connectionThread);
_connectionThread.setObjectName("Limitless Connection");
_connectionThread.start();
moveToNewNamedThread(&_connection, "Limitless Connection");
}
void LimitlessVoiceRecognitionScriptingInterface::update() {

View file

@ -41,7 +41,6 @@ private:
static const int _voiceTimeoutDuration;
QTimer _voiceTimer;
QThread _connectionThread;
LimitlessConnection _connection;
void voiceTimeout();

View file

@ -23,6 +23,7 @@
#include <QUrlQuery>
#include <QXmlStreamReader>
#include <ThreadHelpers.h>
#include <NetworkAccessManager.h>
#include <SharedUtil.h>
@ -84,12 +85,7 @@ ModelsBrowser::ModelsBrowser(FSTReader::ModelType modelsType, QWidget* parent) :
_handler->connect(this, SIGNAL(destroyed()), SLOT(exit()));
// Setup and launch update thread
QThread* thread = new QThread();
thread->setObjectName("Models Browser");
thread->connect(_handler, SIGNAL(destroyed()), SLOT(quit()));
thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
_handler->moveToThread(thread);
thread->start();
moveToNewNamedThread(_handler, "Models Browser");
emit startDownloading();
// Initialize the view

View file

@ -31,10 +31,13 @@
#include <VersionHelpers.h>
#endif
#include <QtConcurrent/QtConcurrent>
#include <QtCore/QThreadPool>
#include <QtCore/QBuffer>
#include <QtMultimedia/QAudioInput>
#include <QtMultimedia/QAudioOutput>
#include <ThreadHelpers.h>
#include <NodeList.h>
#include <plugins/CodecPlugin.h>
#include <plugins/PluginManager.h>
@ -76,59 +79,6 @@ using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
static Mutex _deviceMutex;
class BackgroundThread : public QThread {
public:
BackgroundThread(AudioClient* client) : QThread((QObject*)client), _client(client) {}
virtual void join() = 0;
protected:
AudioClient* _client;
};
// background thread continuously polling device changes
class CheckDevicesThread : public BackgroundThread {
public:
CheckDevicesThread(AudioClient* client) : BackgroundThread(client) {}
void join() override {
_shouldQuit = true;
std::unique_lock<std::mutex> lock(_joinMutex);
_joinCondition.wait(lock, [&]{ return !_isRunning; });
}
protected:
void run() override {
while (!_shouldQuit) {
_client->checkDevices();
const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
QThread::msleep(DEVICE_CHECK_INTERVAL_MSECS);
}
std::lock_guard<std::mutex> lock(_joinMutex);
_isRunning = false;
_joinCondition.notify_one();
}
private:
std::atomic<bool> _shouldQuit { false };
bool _isRunning { true };
std::mutex _joinMutex;
std::condition_variable _joinCondition;
};
// background thread buffering local injectors
class LocalInjectorsThread : public BackgroundThread {
Q_OBJECT
public:
LocalInjectorsThread(AudioClient* client) : BackgroundThread(client) {}
void join() override { return; }
private slots:
void prepare() { _client->prepareLocalAudioInjectors(); }
};
#include "AudioClient.moc"
static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) {
for (int i = 0; i < numSamples/2; i++) {
@ -223,16 +173,15 @@ AudioClient::AudioClient() :
_inputDevices = getDeviceNames(QAudio::AudioInput);
_outputDevices = getDeviceNames(QAudio::AudioOutput);
// start a thread to detect any device changes
_checkDevicesThread = new CheckDevicesThread(this);
_checkDevicesThread->setObjectName("AudioClient CheckDevices Thread");
_checkDevicesThread->setPriority(QThread::LowPriority);
_checkDevicesThread->start();
// start a thread to process local injectors
_localInjectorsThread = new LocalInjectorsThread(this);
_localInjectorsThread->setObjectName("AudioClient LocalInjectors Thread");
_localInjectorsThread->start();
// start a thread to detect any device changes
_checkDevicesTimer = new QTimer(this);
connect(_checkDevicesTimer, &QTimer::timeout, [this] {
QtConcurrent::run(QThreadPool::globalInstance(), [this] {
checkDevices();
});
});
configureReverb();
@ -263,15 +212,7 @@ void AudioClient::cleanupBeforeQuit() {
stop();
if (_checkDevicesThread) {
static_cast<BackgroundThread*>(_checkDevicesThread)->join();
delete _checkDevicesThread;
}
if (_localInjectorsThread) {
static_cast<BackgroundThread*>(_localInjectorsThread)->join();
delete _localInjectorsThread;
}
_checkDevicesTimer->stop();
}
void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
@ -1369,10 +1310,8 @@ bool AudioClient::outputLocalInjector(AudioInjector* injector) {
if (!_activeLocalAudioInjectors.contains(injector)) {
qCDebug(audioclient) << "adding new injector";
_activeLocalAudioInjectors.append(injector);
// move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop())
injectorBuffer->setParent(nullptr);
injectorBuffer->moveToThread(_localInjectorsThread);
// update the flag
_localInjectorsAvailable.exchange(true, std::memory_order_release);
@ -1782,7 +1721,9 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
}
// prepare injectors for the next callback
QMetaObject::invokeMethod(_audio->_localInjectorsThread, "prepare", Qt::QueuedConnection);
QtConcurrent::run(QThreadPool::globalInstance(), [this] {
_audio->prepareLocalAudioInjectors();
});
int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped);
int framesPopped = samplesPopped / AudioConstants::STEREO;
@ -1855,3 +1796,8 @@ void AudioClient::setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 sca
avatarBoundingBoxCorner = corner;
avatarBoundingBoxScale = scale;
}
void AudioClient::startThread() {
moveToNewNamedThread(this, "Audio Thread", [this] { start(); });
}

View file

@ -104,6 +104,7 @@ public:
int _unfulfilledReads;
};
void startThread();
void negotiateAudioFormat();
void selectAudioFormat(const QString& selectedCodecName);
@ -386,8 +387,7 @@ private:
RateCounter<> _silentInbound;
RateCounter<> _audioInbound;
QThread* _checkDevicesThread { nullptr };
QThread* _localInjectorsThread { nullptr };
QTimer* _checkDevicesTimer { nullptr };
};

View file

@ -16,6 +16,8 @@
#include <QtCore/QBuffer>
#include <QtCore/QThread>
#include <ThreadHelpers.h>
#include "NetworkLogging.h"
#include "NodeList.h"
#include "PacketReceiver.h"
@ -30,12 +32,6 @@ MessagesClient::MessagesClient() {
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &MessagesClient::handleNodeActivated);
}
void MessagesClient::init() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection);
}
}
void MessagesClient::decodeMessagesPacket(QSharedPointer<ReceivedMessage> receivedMessage, QString& channel,
bool& isText, QString& message, QByteArray& data, QUuid& senderID) {
quint16 channelLength;
@ -185,3 +181,7 @@ void MessagesClient::handleNodeActivated(SharedNodePointer node) {
}
}
}
void MessagesClient::startThread() {
moveToNewNamedThread(this, "Messages Client Thread");
}

View file

@ -28,7 +28,7 @@ class MessagesClient : public QObject, public Dependency {
public:
MessagesClient();
Q_INVOKABLE void init();
void startThread();
Q_INVOKABLE void sendMessage(QString channel, QString message, bool localOnly = false);
Q_INVOKABLE void sendLocalMessage(QString channel, QString message);

View file

@ -20,6 +20,7 @@
#include <QtNetwork/QHostInfo>
#include <QtNetwork/QNetworkInterface>
#include <ThreadHelpers.h>
#include <LogHandler.h>
#include <UUID.h>
@ -1115,3 +1116,8 @@ void NodeList::setRequestsDomainListData(bool isRequesting) {
});
_requestsDomainListData = isRequesting;
}
void NodeList::startThread() {
moveToNewNamedThread(this, "NodeList Thread", QThread::TimeCriticalPriority);
}

View file

@ -52,6 +52,7 @@ class NodeList : public LimitedNodeList {
SINGLETON_DEPENDENCY
public:
void startThread();
NodeType_t getOwnerType() const { return _ownerType.load(); }
void setOwnerType(NodeType_t ownerType) { _ownerType.store(ownerType); }

View file

@ -0,0 +1,39 @@
//
// Created by Bradley Austin Davis on 2017/06/06
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ThreadHelpers.h"
#include <QtCore/QDebug>
void moveToNewNamedThread(QObject* object, const QString& name, std::function<void()> startCallback, QThread::Priority priority) {
Q_ASSERT(QThread::currentThread() == object->thread());
// setup a thread for the NodeList and its PacketReceiver
QThread* thread = new QThread();
thread->setObjectName(name);
if (priority != QThread::InheritPriority) {
thread->setPriority(priority);
}
QString tempName = name;
QObject::connect(thread, &QThread::started, [startCallback] {
startCallback();
});
// Make sure the thread will be destroyed and cleaned up
QObject::connect(object, &QObject::destroyed, thread, &QThread::quit);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
// put the object on the thread
object->moveToThread(thread);
thread->start();
}
void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority) {
moveToNewNamedThread(object, name, [] {}, priority);
}

View file

@ -12,8 +12,13 @@
#define hifi_ThreadHelpers_h
#include <exception>
#include <QMutex>
#include <QMutexLocker>
#include <functional>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QThread>
template <typename L, typename F>
void withLock(L lock, F function) {
@ -26,4 +31,7 @@ void withLock(QMutex& lock, F function) {
function();
}
void moveToNewNamedThread(QObject* object, const QString& name, std::function<void()> startCallback, QThread::Priority priority = QThread::InheritPriority);
void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority = QThread::InheritPriority);
#endif

15
tests/qt59/CMakeLists.txt Normal file
View file

@ -0,0 +1,15 @@
set(TARGET_NAME qt59)
if (WIN32)
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217")
endif()
# This is not a testcase -- just set it up as a regular hifi project
setup_hifi_project(Gui)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries
link_hifi_libraries(shared networking)
package_libraries_for_deployment()

78
tests/qt59/src/main.cpp Normal file
View file

@ -0,0 +1,78 @@
//
// Created by Bradley Austin Davis on 2017/06/06
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <mutex>
#include <QtCore/QCoreApplication>
#include <NodeList.h>
#include <AccountManager.h>
#include <AddressManager.h>
#include <MessagesClient.h>
#include <BuildInfo.h>
class Qt59TestApp : public QCoreApplication {
Q_OBJECT
public:
Qt59TestApp(int argc, char* argv[]);
~Qt59TestApp();
private:
void finish(int exitCode);
};
Qt59TestApp::Qt59TestApp(int argc, char* argv[]) :
QCoreApplication(argc, argv)
{
Setting::init();
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::set<AccountManager>([&] { return QString("Mozilla/5.0 (HighFidelityACClient)"); });
DependencyManager::set<AddressManager>();
DependencyManager::set<NodeList>(NodeType::Agent, 0);
auto nodeList = DependencyManager::get<NodeList>();
nodeList->startThread();
auto messagesClient = DependencyManager::set<MessagesClient>();
messagesClient->startThread();
QTimer::singleShot(1000, [this] { finish(0); });
}
Qt59TestApp::~Qt59TestApp() {
}
void Qt59TestApp::finish(int exitCode) {
auto nodeList = DependencyManager::get<NodeList>();
// send the domain a disconnect packet, force stoppage of domain-server check-ins
nodeList->getDomainHandler().disconnect();
nodeList->setIsShuttingDown(true);
nodeList->getPacketReceiver().setShouldDropPackets(true);
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
QCoreApplication::exit(exitCode);
}
int main(int argc, char * argv[]) {
QCoreApplication::setApplicationName("Qt59Test");
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
Qt59TestApp app(argc, argv);
return app.exec();
}
#include "main.moc"

View file

@ -90,23 +90,14 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
auto nodeList = DependencyManager::get<NodeList>();
// start the nodeThread so its event loop is running
QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("NodeList Thread");
nodeThread->start();
// make sure the node thread is given highest priority
nodeThread->setPriority(QThread::TimeCriticalPriority);
nodeList->startThread();
// setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
// put the NodeList and datagram processing on the node thread
nodeList->moveToThread(nodeThread);
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
@ -239,12 +230,8 @@ void ACClientApp::finish(int exitCode) {
// tell the packet receiver we're shutting down, so it can drop packets
nodeList->getPacketReceiver().setShouldDropPackets(true);
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
// ask the node thread to quit and wait until it is done
nodeThread->quit();
nodeThread->wait();
printFailedServers();
QCoreApplication::exit(exitCode);

View file

@ -111,23 +111,13 @@ ATPGetApp::ATPGetApp(int argc, char* argv[]) :
auto nodeList = DependencyManager::get<NodeList>();
// start the nodeThread so its event loop is running
QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("NodeList Thread");
nodeThread->start();
// make sure the node thread is given highest priority
nodeThread->setPriority(QThread::TimeCriticalPriority);
nodeList->startThread();
// setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
// put the NodeList and datagram processing on the node thread
nodeList->moveToThread(nodeThread);
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
@ -258,12 +248,8 @@ void ATPGetApp::finish(int exitCode) {
// tell the packet receiver we're shutting down, so it can drop packets
nodeList->getPacketReceiver().setShouldDropPackets(true);
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
// ask the node thread to quit and wait until it is done
nodeThread->quit();
nodeThread->wait();
QCoreApplication::exit(exitCode);
}