Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/backend/computermanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ void ComputerManager::startPolling()
});
connect(m_MultiSeatDiscovery, &MultiSeatDiscovery::aboutToPoll,
this, &ComputerManager::updateMultiSeatProbeTargets);
// start() only queues the first poll. It must not poll synchronously: this function holds
// m_Lock for write, and updateMultiSeatProbeTargets() takes it for read.
m_MultiSeatDiscovery->start();

// Start polling threads for each known host
Expand Down Expand Up @@ -438,6 +440,9 @@ void ComputerManager::startPollingComputer(NvComputer* computer)
// beside share one address — probing it twice would just double the requests for nothing. Local
// addresses only: seat ports are not port-forwarded, so probing a remote address would be four
// guaranteed failures per tick against someone else's network.
//
// ⛔ Takes m_Lock for read. It must never run on a thread that already holds m_Lock, because
// QReadWriteLock is not recursive and that thread would wait on itself.
void ComputerManager::updateMultiSeatProbeTargets()
{
if (m_MultiSeatDiscovery == nullptr) {
Expand Down
16 changes: 14 additions & 2 deletions app/backend/multiseatdiscovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,15 @@ MultiSeatDiscovery::~MultiSeatDiscovery()
void MultiSeatDiscovery::start()
{
m_Timer->start();
// Poll immediately on start, don't wait for first interval
poll();

// Poll soon after start rather than waiting a full interval, but from the event loop, never
// from inside start().
//
// ⛔ Do not call poll() directly here. ComputerManager::startPolling() calls start() while it
// holds m_Lock for write, and poll() emits aboutToPoll(), whose slot takes that same lock for
// read on the same thread. QReadWriteLock is not recursive, so the main thread waited on
// itself forever: 6.3.4 hung ("Not Responding") on every launch.
QMetaObject::invokeMethod(this, &MultiSeatDiscovery::poll, Qt::QueuedConnection);
}

void MultiSeatDiscovery::stop()
Expand All @@ -42,6 +49,11 @@ void MultiSeatDiscovery::setHostsToProbe(const QStringList& addresses)

void MultiSeatDiscovery::poll()
{
// The first poll is queued by start(). If stop() ran before it was delivered, do nothing.
if (!m_Timer->isActive()) {
return;
}

// Refresh the address list first — hosts may have been added or removed since the last tick.
emit aboutToPoll();

Expand Down
Loading