wayland: Fix transaction cleanup with destroyed surfaces

When sorting surfaces in the ancestor order we need to ignore null
surfaces.

In addition to that, we also need to properly handle the case where
a transaction with dependencies is unlocked and it contains null
surfaces.

For example, if there are three transactions A -> B -> C, and the B
transaction is unlocked, we cannot apply it until transaction A is
applied. The readiness check is based on checking the first pending
transaction of the surface. But if the surface is null, the check
will be skipped, which is not ideal as transaction B can be applied
before transaction A now. To address that, this change makes every
transaction entry remember the previous transaction. With that, the
readiness check can be performed even if the surface has been destroyed.

BUG: 475648
master
Vlad Zahorodnii 11 months ago
parent 816d2866c7
commit d52ba8c3fe

@ -100,16 +100,9 @@ bool Transaction::isReady() const
return false;
}
for (const TransactionEntry &entry : m_entries) {
if (!entry.surface) {
continue;
}
if (entry.surface->firstTransaction() != this) {
return false;
}
}
return true;
return std::none_of(m_entries.cbegin(), m_entries.cend(), [](const TransactionEntry &entry) {
return entry.previousTransaction;
});
}
Transaction *Transaction::next(SurfaceInterface *surface) const
@ -195,6 +188,13 @@ void Transaction::apply()
{
// Sort surfaces so descendants come first, then their ancestors.
std::sort(m_entries.begin(), m_entries.end(), [](const TransactionEntry &a, const TransactionEntry &b) {
if (!a.surface) {
return false;
}
if (!b.surface) {
return true;
}
if (isAncestor(a.surface, b.surface)) {
return true;
}
@ -221,6 +221,12 @@ void Transaction::apply()
}
if (entry.nextTransaction) {
for (TransactionEntry &otherEntry : entry.nextTransaction->m_entries) {
if (otherEntry.previousTransaction == this) {
otherEntry.previousTransaction = nullptr;
break;
}
}
entry.nextTransaction->tryApply();
}
}
@ -258,6 +264,7 @@ void Transaction::commit()
entry.surface->setFirstTransaction(this);
}
entry.previousTransaction = entry.surface->lastTransaction();
entry.surface->setLastTransaction(this);
}

@ -33,6 +33,11 @@ struct TransactionEntry
*/
QPointer<SurfaceInterface> surface;
/**
* The previous transaction that this transaction depends on.
*/
Transaction *previousTransaction = nullptr;
/**
* Next transaction that is going to affect the surface.
*/

Loading…
Cancel
Save