Skip to content

Use a recursive mutex for the PersistentSymbolTable

The visitDeclarations and visitFilteredDeclarations might be nested, which means we have to account for potentially recursive mutex locking. The kdev-php plugin exhibits this behavior when it calls KDevelop::DUContext::findDeclarations, which might lead to recursive visitFilteredDeclarations within the KDevelop::TopDUContext::applyAliases method. I.e. the deadlock in the uses unit test suite of kdev-php occurred within:

(gdb) bt
#0  0x00007ffff2b1959d in syscall () at /usr/lib/libc.so.6
#1  0x00007ffff30dfb06 in QBasicMutex::lockInternal() () at /usr/lib/libQt5Core.so.5
#2  0x00007ffff5c95d87 in QMutexLocker::QMutexLocker(QBasicMutex*) (this=0x7fffffff7808, m=0x7ffff75ede50 <KDevelop::ItemRepositoryFor<KDevelop::PersistentSymbolTable>::repo()::mutex>)
    at /usr/include/qt/QtCore/qmutex.h:238
#3  0x00007ffff5dc0d0e in KDevelop::LockedItemRepository::write<KDevelop::PersistentSymbolTable, KDevelop::PersistentSymbolTable::visitFilteredDeclarations(const KDevelop::IndexedQualifiedIdentifier&, const KDevelop::TopDUContext::IndexedRecursiveImports&, const DeclarationVisitor&) const::<lambda(KDevelop::(anonymous namespace)::PersistentSymbolTableRepo&)> >(struct {...} &&) (op=...) at /home/milian/projects/kde/src/kdevelop/kdevplatform/serialization/itemrepository.h:2310
#4  0x00007ffff5dc0df8 in KDevelop::PersistentSymbolTable::visitFilteredDeclarations(KDevelop::IndexedQualifiedIdentifier const&, Utils::StorableSet<KDevelop::IndexedTopDUContext, KDevelop::IndexedTopDUContextIndexConversion, KDevelop::RecursiveImportRepository, true, Utils::DummyLocker> const&, std::function<KDevelop::PersistentSymbolTable::VisitorState (KDevelop::IndexedDeclaration const&)> const&) const (this=0x7ffff77ec5c8 <KDevelop::PersistentSymbolTable::self()::ret>, id=..., visibility=..., visitor=...)
    at /home/milian/projects/kde/src/kdevelop/kdevplatform/language/duchain/persistentsymboltable.cpp:361
#5  0x00007ffff5d2eb11 in KDevelop::TopDUContext::FindDeclarationsAcceptor::operator()(KDevelop::QualifiedIdentifier const&) (this=0x7fffffff8ff0, id=...)
    at /home/milian/projects/kde/src/kdevelop/kdevplatform/language/duchain/topducontext.cpp:688
#6  0x00007ffff5d3542c in KDevelop::TopDUContext::applyAliases<KDevelop::TopDUContext::FindDeclarationsAcceptor>(KDevelop::QualifiedIdentifier const&, QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem> const&, KDevelop::TopDUContext::FindDeclarationsAcceptor&, KDevelop::CursorInRevision const&, bool, KDevelop::TopDUContext::ApplyAliasesBuddyInfo*, unsigned int) const (this=0x555555b80260, previous=..., identifier=..., accept=..., position=..., canBeNamespace=false, buddy=0x7fffffff7b00, recursionDepth=1)
    at /home/milian/projects/kde/src/kdevelop/kdevplatform/language/duchain/topducontext.cpp:857
#7  0x00007ffff5d34aed in KDevelop::TopDUContext::applyAliases<KDevelop::TopDUContext::FindDeclarationsAcceptor>(KDevelop::QualifiedIdentifier const&, QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem> const&, KDevelop::TopDUContext::FindDeclarationsAcceptor&, KDevelop::CursorInRevision const&, bool, KDevelop::TopDUContext::ApplyAliasesBuddyInfo*, unsigned int) const::{lambda(KDevelop::IndexedDeclaration const&)#1}::operator()(KDevelop::IndexedDeclaration const&) const (__closure=0x555555a86fe0, indexedAliasDecl=...)
    at /home/milian/projects/kde/src/kdevelop/kdevplatform/language/duchain/topducontext.cpp:839
#8  0x00007ffff5d3becf in std::__invoke_impl<KDevelop::PersistentSymbolTable::VisitorState, KDevelop::TopDUContext::applyAliases<KDevelop::TopDUContext::FindDeclarationsAcceptor>(KDevelop::QualifiedIdentifier const&, QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem> const&, KDevelop::TopDUContext::FindDeclarationsAcceptor&, KDevelop::CursorInRevision const&, bool, KDevelop::TopDUContext::ApplyAliasesBuddyInfo*, unsigned int) const::{lambda(KDevelop::IndexedDeclaration const&)#1}&, KDevelop::IndexedDeclaration const&>(std::__invoke_other, KDevelop::TopDUContext::applyAliases<KDevelop::TopDUContext::FindDeclarationsAcceptor>(KDevelop::QualifiedIdentifier const&, QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem> const&, KDevelop::TopDUContext::FindDeclarationsAcceptor&, KDevelop::CursorInRevision const&, bool, KDevelop::TopDUContext::ApplyAliasesBuddyInfo*, unsigned int) const::{lambda(KDevelop::IndexedDeclaration const&)#1}&, KDevelop::IndexedDeclaration const&) (__f=...) at /usr/include/c++/12.2.0/bits/invoke.h:61
#9  0x00007ffff5d3b932 in std::__invoke_r<KDevelop::PersistentSymbolTable::VisitorState, KDevelop::TopDUContext::applyAliases<KDevelop::TopDUContext::FindDeclarationsAcceptor>(KDevelop::QualifiedIdentifier const&, QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem> const&, KDevelop::TopDUContext::FindDeclarationsAcceptor&, KDevelop::CursorInRevision const&, bool, KDevelop::TopDUContext::ApplyAliasesBuddyInfo*, unsigned int) const::{lambda(KDevelop::IndexedDeclaration const&)#1}&, KDevelop::IndexedDeclaration const&>(KDevelop::TopDUContext::applyAliases<KDevelop::TopDUContext::FindDeclarationsAcceptor>(KDevelop::QualifiedIdentifier const&, QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem> const&, KDevelop::TopDUContext::FindDeclarationsAcceptor&, KDevelop::CursorInRevision const&, bool, KDevelop::TopDUContext::ApplyAliasesBuddyInfo*, unsigned int) const::{lambda(KDevelop::IndexedDeclaration const&)#1}&, KDevelop::IndexedDeclaration const&) (__fn=...) at /usr/include/c++/12.2.0/bits/invoke.h:114
#10 0x00007ffff5d3adbe in std::_Function_handler<KDevelop::PersistentSymbolTable::VisitorState (KDevelop::IndexedDeclaration const&), KDevelop::TopDUContext::applyAliases<KDevelop::TopDUContext::FindDeclarationsAcceptor>(KDevelop::QualifiedIdentifier const&, QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem> const&, KDevelop::TopDUContext::FindDeclarationsAcceptor&, KDevelop::CursorInRevision const&, bool, KDevelop::TopDUContext::ApplyAliasesBuddyInfo*, unsigned int) const::{lambda(KDevelop::IndexedDeclaration const&)#1}>::_M_invoke(std::_Any_data const&, KDevelop::IndexedDeclaration const&) (__functor=..., __args#0=...) at /usr/include/c++/12.2.0/bits/std_function.h:290
#11 0x00007ffff5dcf371 in std::function<KDevelop::PersistentSymbolTable::VisitorState (KDevelop::IndexedDeclaration const&)>::operator()(KDevelop::IndexedDeclaration const&) const
    (this=0x7fffffff8ed0, __args#0=...) at /usr/include/c++/12.2.0/bits/std_function.h:591
#12 0x00007ffff5dc0c5d in operator()(KDevelop::(anonymous namespace)::PersistentSymbolTableRepo&) const (__closure=0x7fffffff8d70, repo=...)
#13 0x00007ffff5dc0d21 in KDevelop::LockedItemRepository::write<KDevelop::PersistentSymbolTable, KDevelop::PersistentSymbolTable::visitFilteredDeclarations(const KDevelop::IndexedQualifiedIdentifier&, const KDevelop::TopDUContext::IndexedRecursiveImports&, const DeclarationVisitor&) const::<lambda(KDevelop::(anonymous namespace)::PersistentSymbolTableRepo&)> >(struct {...} &&) (op=...) at /home/milian/projects/kde/src/kdevelop/kdevplatform/serialization/itemrepository.h:2311
#14 0x00007ffff5dc0df8 in KDevelop::PersistentSymbolTable::visitFilteredDeclarations(KDevelop::IndexedQualifiedIdentifier const&, Utils::StorableSet<KDevelop::IndexedTopDUContext, KDevelop::IndexedTopDUContextIndexConversion, KDevelop::RecursiveImportRepository, true, Utils::DummyLocker> const&, std::function<KDevelop::PersistentSymbolTable::VisitorState (KDevelop::IndexedDeclaration const&)> const&) const (this=0x7ffff77ec5c8 <KDevelop::PersistentSymbolTable::self()::ret>, id=..., visibility=..., visitor=...)
    at /home/milian/projects/kde/src/kdevelop/kdevplatform/language/duchain/persistentsymboltable.cpp:361
#15 0x00007ffff5d35372 in KDevelop::TopDUContext::applyAliases<KDevelop::TopDUContext::FindDeclarationsAcceptor>(KDevelop::QualifiedIdentifier const&, QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem> const&, KDevelop::TopDUContext::FindDeclarationsAcceptor&, KDevelop::CursorInRevision const&, bool, KDevelop::TopDUContext::ApplyAliasesBuddyInfo*, unsigned int) const (this=0x555555b80260, previous=..., identifier=..., accept=..., position=..., canBeNamespace=false, buddy=0x0, recursionDepth=0)
    at /home/milian/projects/kde/src/kdevelop/kdevplatform/language/duchain/topducontext.cpp:795
#16 0x00007ffff5d30e98 in KDevelop::TopDUContext::applyAliases<KDevelop::TopDUContext::FindDeclarationsAcceptor>(KDevVarLengthArray<QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem>, 256> const&, KDevelop::TopDUContext::FindDeclarationsAcceptor&, KDevelop::CursorInRevision const&, bool) const
    (this=0x555555b80260, identifiers=..., acceptor=..., position=..., canBeNamespace=false) at /home/milian/projects/kde/src/kdevelop/kdevplatform/language/duchain/topducontext.cpp:942
#17 0x00007ffff5d2b2fe in KDevelop::TopDUContext::findDeclarationsInternal(KDevVarLengthArray<QExplicitlySharedDataPointer<KDevelop::DUContext::SearchItem>, 256> const&, KDevelop::CursorInRevision const&, KDevelop::TypePtr<KDevelop::AbstractType> const&, QList<KDevelop::Declaration*>&, KDevelop::TopDUContext const*, QFlags<KDevelop::DUContext::SearchFlag>, unsigned int) const (this=0x555555b80260, identifiers=..., position=..., dataType=..., ret=QList<KDevelop::Declaration *> (size = 0), flags=...)
    at /home/milian/projects/kde/src/kdevelop/kdevplatform/language/duchain/topducontext.cpp:724
#18 0x00007ffff5d00345 in KDevelop::DUContext::findDeclarations(KDevelop::QualifiedIdentifier const&, KDevelop::CursorInRevision const&, KDevelop::TypePtr<KDevelop::AbstractType> const&, KDevelop::TopDUContext const*, QFlags<KDevelop::DUContext::SearchFlag>) const (this=0x555555b80260, identifier=..., position=..., dataType=..., topContext=0x0, flags=...)
    at /home/milian/projects/kde/src/kdevelop/kdevplatform/language/duchain/ducontext.cpp:793
...

This patch here fixes that deadlock.

@wiesinger

Merge request reports