mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2025-01-07 08:48:45 +00:00
Ensure all polling threads are dead before destroying hosts, even ones that have been detached by stopPollingAsync()
This commit is contained in:
parent
e0bebeae25
commit
75f631599c
2 changed files with 104 additions and 66 deletions
|
@ -174,38 +174,19 @@ ComputerManager::~ComputerManager()
|
||||||
delete m_MdnsBrowser;
|
delete m_MdnsBrowser;
|
||||||
m_MdnsBrowser = nullptr;
|
m_MdnsBrowser = nullptr;
|
||||||
|
|
||||||
{
|
// Interrupt polling
|
||||||
QMutableMapIterator<QString, QThread*> i(m_PollThreads);
|
for (ComputerPollingEntry* entry : m_PollEntries) {
|
||||||
|
entry->interrupt();
|
||||||
// Interrupt all polling threads
|
|
||||||
while (i.hasNext()) {
|
|
||||||
i.next();
|
|
||||||
i.value()->requestInterruption();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all threads to terminate before destroying
|
|
||||||
// the NvComputer objects.
|
|
||||||
i.toFront();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
i.next();
|
|
||||||
|
|
||||||
QThread* thread = i.value();
|
|
||||||
thread->wait();
|
|
||||||
delete thread;
|
|
||||||
|
|
||||||
i.remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
// Delete all polling entries (and associated threads)
|
||||||
QMutableMapIterator<QString, NvComputer*> i(m_KnownHosts);
|
for (ComputerPollingEntry* entry : m_PollEntries) {
|
||||||
|
delete entry;
|
||||||
|
}
|
||||||
|
|
||||||
// Destroy all NvComputer objects
|
// Destroy all NvComputer objects now that polling is halted
|
||||||
while (i.hasNext()) {
|
for (NvComputer* computer : m_KnownHosts) {
|
||||||
i.next();
|
delete computer;
|
||||||
delete i.value();
|
|
||||||
i.remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,16 +246,22 @@ void ComputerManager::startPollingComputer(NvComputer* computer)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_PollThreads.contains(computer->uuid)) {
|
ComputerPollingEntry* pollingEntry;
|
||||||
Q_ASSERT(m_PollThreads.value(computer->uuid)->isRunning());
|
|
||||||
return;
|
if (!m_PollEntries.contains(computer->uuid)) {
|
||||||
|
pollingEntry = m_PollEntries[computer->uuid] = new ComputerPollingEntry();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pollingEntry = m_PollEntries[computer->uuid];
|
||||||
}
|
}
|
||||||
|
|
||||||
PcMonitorThread* thread = new PcMonitorThread(computer);
|
if (!pollingEntry->isActive()) {
|
||||||
connect(thread, SIGNAL(computerStateChanged(NvComputer*)),
|
PcMonitorThread* thread = new PcMonitorThread(computer);
|
||||||
this, SLOT(handleComputerStateChanged(NvComputer*)));
|
connect(thread, SIGNAL(computerStateChanged(NvComputer*)),
|
||||||
m_PollThreads[computer->uuid] = thread;
|
this, SLOT(handleComputerStateChanged(NvComputer*)));
|
||||||
thread->start();
|
pollingEntry->setActiveThread(thread);
|
||||||
|
thread->start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComputerManager::handleMdnsServiceResolved(MdnsPendingComputer* computer,
|
void ComputerManager::handleMdnsServiceResolved(MdnsPendingComputer* computer,
|
||||||
|
@ -317,14 +304,14 @@ public:
|
||||||
|
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
QThread* pollingThread;
|
ComputerPollingEntry* pollingEntry;
|
||||||
|
|
||||||
// Only do the minimum amount of work while holding the writer lock.
|
// Only do the minimum amount of work while holding the writer lock.
|
||||||
// We must release it before calling saveHosts().
|
// We must release it before calling saveHosts().
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&m_ComputerManager->m_Lock);
|
QWriteLocker lock(&m_ComputerManager->m_Lock);
|
||||||
|
|
||||||
pollingThread = m_ComputerManager->m_PollThreads.take(m_Computer->uuid);
|
pollingEntry = m_ComputerManager->m_PollEntries.take(m_Computer->uuid);
|
||||||
|
|
||||||
m_ComputerManager->m_KnownHosts.remove(m_Computer->uuid);
|
m_ComputerManager->m_KnownHosts.remove(m_Computer->uuid);
|
||||||
}
|
}
|
||||||
|
@ -332,18 +319,8 @@ public:
|
||||||
// Persist the new host list
|
// Persist the new host list
|
||||||
m_ComputerManager->saveHosts();
|
m_ComputerManager->saveHosts();
|
||||||
|
|
||||||
// Delete the polling thread
|
// Delete the polling entry first. This will stop all polling threads too.
|
||||||
if (pollingThread != nullptr) {
|
delete pollingEntry;
|
||||||
pollingThread->requestInterruption();
|
|
||||||
|
|
||||||
// We must wait here because we're going to delete computer
|
|
||||||
// and we can't do that out from underneath the poller.
|
|
||||||
pollingThread->wait();
|
|
||||||
|
|
||||||
// The thread is safe to delete now
|
|
||||||
Q_ASSERT(pollingThread->isFinished());
|
|
||||||
delete pollingThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, delete the computer itself. This must be done
|
// Finally, delete the computer itself. This must be done
|
||||||
// last because the polling thread might be using it.
|
// last because the polling thread might be using it.
|
||||||
|
@ -498,20 +475,8 @@ void ComputerManager::stopPollingAsync()
|
||||||
m_MdnsBrowser = nullptr;
|
m_MdnsBrowser = nullptr;
|
||||||
|
|
||||||
// Interrupt all threads, but don't wait for them to terminate
|
// Interrupt all threads, but don't wait for them to terminate
|
||||||
QMutableMapIterator<QString, QThread*> i(m_PollThreads);
|
for (ComputerPollingEntry* entry : m_PollEntries) {
|
||||||
while (i.hasNext()) {
|
entry->interrupt();
|
||||||
i.next();
|
|
||||||
|
|
||||||
QThread* thread = i.value();
|
|
||||||
|
|
||||||
// Let this thread delete itself when it's finished
|
|
||||||
connect(thread, SIGNAL(finished()),
|
|
||||||
thread, SLOT(deleteLater()));
|
|
||||||
|
|
||||||
// The threads will delete themselves when they terminate,
|
|
||||||
// but we remove them from the polling threads map here.
|
|
||||||
i.value()->requestInterruption();
|
|
||||||
i.remove();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,79 @@ private:
|
||||||
QMdnsEngine::Resolver m_Resolver;
|
QMdnsEngine::Resolver m_Resolver;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ComputerPollingEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ComputerPollingEntry()
|
||||||
|
: m_ActiveThread(nullptr)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ComputerPollingEntry()
|
||||||
|
{
|
||||||
|
interrupt();
|
||||||
|
|
||||||
|
// interrupt() should have taken care of this
|
||||||
|
Q_ASSERT(m_ActiveThread == nullptr);
|
||||||
|
|
||||||
|
for (QThread* thread : m_InactiveList) {
|
||||||
|
thread->wait();
|
||||||
|
delete thread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isActive()
|
||||||
|
{
|
||||||
|
cleanInactiveList();
|
||||||
|
|
||||||
|
return m_ActiveThread != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setActiveThread(QThread* thread)
|
||||||
|
{
|
||||||
|
cleanInactiveList();
|
||||||
|
|
||||||
|
Q_ASSERT(!isActive());
|
||||||
|
m_ActiveThread = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void interrupt()
|
||||||
|
{
|
||||||
|
cleanInactiveList();
|
||||||
|
|
||||||
|
if (m_ActiveThread != nullptr) {
|
||||||
|
// Interrupt the active thread
|
||||||
|
m_ActiveThread->requestInterruption();
|
||||||
|
|
||||||
|
// Place it on the inactive list awaiting death
|
||||||
|
m_InactiveList.append(m_ActiveThread);
|
||||||
|
|
||||||
|
m_ActiveThread = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void cleanInactiveList()
|
||||||
|
{
|
||||||
|
QMutableListIterator<QThread*> i(m_InactiveList);
|
||||||
|
|
||||||
|
// Reap any threads that have finished
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
|
||||||
|
QThread* thread = i.value();
|
||||||
|
if (thread->isFinished()) {
|
||||||
|
delete thread;
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QThread* m_ActiveThread;
|
||||||
|
QList<QThread*> m_InactiveList;
|
||||||
|
};
|
||||||
|
|
||||||
class ComputerManager : public QObject
|
class ComputerManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -100,7 +173,7 @@ private:
|
||||||
int m_PollingRef;
|
int m_PollingRef;
|
||||||
QReadWriteLock m_Lock;
|
QReadWriteLock m_Lock;
|
||||||
QMap<QString, NvComputer*> m_KnownHosts;
|
QMap<QString, NvComputer*> m_KnownHosts;
|
||||||
QMap<QString, QThread*> m_PollThreads;
|
QMap<QString, ComputerPollingEntry*> m_PollEntries;
|
||||||
QMdnsEngine::Server m_MdnsServer;
|
QMdnsEngine::Server m_MdnsServer;
|
||||||
QMdnsEngine::Browser* m_MdnsBrowser;
|
QMdnsEngine::Browser* m_MdnsBrowser;
|
||||||
QMdnsEngine::Cache m_MdnsCache;
|
QMdnsEngine::Cache m_MdnsCache;
|
||||||
|
|
Loading…
Reference in a new issue