https://bugs.kde.org/show_bug.cgi?id=514721
[email protected] changed: What |Removed |Added ---------------------------------------------------------------------------- Ever confirmed|0 |1 Version First|9.0.0 |9.1.0 Reported In| | CC| |[email protected] Status|REPORTED |CONFIRMED --- Comment #9 from [email protected] --- # Comment for bugs.kde.org #514721 Backtraces attached — this report has been missing one since it was filed. I can reproduce this reliably on digiKam 9.1.0 (Fedora 44 packages) and captured five coredumps in ~40 minutes of trying to use the Find Duplicates view. All five have the same root cause: **`FindDuplicatesAlbumItem::calculateInfosMultithreaded()` writes into `QTreeWidgetItem` from `QtConcurrent` worker threads.** Qt widget / item-view classes are not reentrant, so this is a data race on the item's internal `QList<QWidgetItemData>` and on the shared `QTreeModel`. ## Crashing thread (SIGSEGV, PID 17981) ``` Program terminated with signal SIGSEGV, Segmentation fault. [Current thread is 1 (Thread 0x7fd703fff6c0 (LWP 18167))] #0 QVariant::QVariant(QVariant const&) libQt6Core.so.6 #1 QArrayDataPointer<QVariant>::reallocateAndGrow(...) libQt6Widgets.so.6 #2 QtPrivate::QMovableArrayOps<QVariant>::emplace<QVariant const&>(...) libQt6Widgets.so.6 #3 QTreeWidgetItem::setData(int, int, QVariant const&) [clone .part.0] libQt6Widgets.so.6 #4 Digikam::FindDuplicatesAlbumItem::calculateInfosMultithreaded(QList<long long> const&) libdigikamgui.so.9.1.0 #5 QtConcurrent::StoredFunctionCall<void (Digikam::FindDuplicatesAlbumItem::*) (QList<long long> const&), Digikam::FindDuplicatesAlbumItem*, QList<long long> >::runFunctor() libdigikamgui.so.9.1.0 #6 QtConcurrent::RunFunctionTaskBase<void>::run() libdigikamgui.so.9.1.0 #7 QThreadPoolThread::run() libQt6Core.so.6 #8 QThreadPrivate::start(void*) libQt6Core.so.6 #9 start_thread / __clone3 libc.so.6 ``` ## Direct evidence of the race In that same core dump, **23 of the 55 threads are inside `calculateInfosMultithreaded()` at the moment of the crash.** Distribution of their innermost frame: ``` 21x syscall (futex wait) 1x __syscall_cancel_arch 1x QVariant::QVariant(QVariant const&) <- the thread that segfaulted ``` The 22 non-crashing ones are all blocked on the same lock, e.g.: ``` Thread 55 (LWP 18193): #0 syscall libc.so.6 #1 QBasicMutex::lockInternal() libQt6Core.so.6 #2 QRecursiveMutex::tryLock(QDeadlineTimer) libQt6Core.so.6 #3 Digikam::SimilarityDbAccess::SimilarityDbAccess() libdigikamdatabase.so.9.1.0 #4 Digikam::ItemExtendedProperties::similarityTo(long long) libdigikamdatabase.so.9.1.0 #5 Digikam::ItemInfo::similarityTo(long long) const libdigikamdatabase.so.9.1.0 #6 Digikam::FindDuplicatesAlbumItem::calculateInfosMultithreaded(QList<long long> const&) #7 QtConcurrent::StoredFunctionCall<...>::runFunctor() #8 QtConcurrent::RunFunctionTaskBase<void>::run() ``` Two observations follow from this: 1. Whichever worker wins the `SimilarityDbAccess` mutex proceeds to call `QTreeWidgetItem::setData()` **off the GUI thread**. That is the actual defect. 2. The DB access is fully serialized on a recursive mutex anyway, so the parallelisation buys close to nothing here — it only adds the unsafe widget access. Whatever the intent of moving this to `QtConcurrent::run` (8.8.0), the throughput gain appears not to materialise. ## Second signature: heap corruption (SIGABRT, PID 16514) Same code path, manifesting as an allocator abort instead of a segfault — the expected second face of an unsynchronised concurrent write: ``` Program terminated with signal SIGABRT, Aborted. #4 malloc_printerr libc.so.6 #5 _int_free_merge_chunk libc.so.6 #6 _int_free_chunk libc.so.6 #7 QFutureInterfaceBasePrivate::~QFutureInterfaceBasePrivate() libQt6Core.so.6 #8 QFutureInterfaceBase::~QFutureInterfaceBase() libQt6Core.so.6 #9 QtConcurrent::StoredFunctionCall<void (Digikam::FindDuplicatesAlbumItem::*) (QList<long long> const&), ...>::~StoredFunctionCall() libdigikamgui.so.9.1.0 #10 QThreadPoolThread::run() libQt6Core.so.6 ``` All five dumps (3x SIGSEGV in `QTreeWidgetItem::setData`, 1x SIGSEGV in `QArrayDataPointer<QList<QWidgetItemData> >::reallocateAndGrow` from the same `setData`, 1x SIGABRT as above) share the `calculateInfosMultithreaded` frame. ## Why this looks intermittent in the field `QtConcurrent`'s global pool sizes itself from `QThread::idealThreadCount()`. This machine has 24 hardware threads, so ~23 workers pile onto the tree concurrently and the race is hit almost every run. On a 4- or 8-core machine the window is far narrower, which likely explains the "only on the first attempt" / "works after a restart" pattern in the original report — it is timing, not state. The size of the result set matters for the same reason: more duplicate groups means more concurrent tasks. ## Suggested direction Keep the DB queries in the worker, but marshal the result back to the GUI thread before touching the item — e.g. emit a queued signal with the computed values, or `QMetaObject::invokeMethod(..., Qt::QueuedConnection)` on the view — instead of calling `setData()` from the pool thread. ## Environment | | | |---|---| | digiKam | 9.1.0-1.fc44 (distribution package, not AppImage) | | build-id | `90f976672ff24c9023b3b53163ba140f44fccf44` | | Qt | 6.11.1 | | KDE Frameworks | 6.28.0 | | OS | Fedora 44, kernel 7.1.4-204.fc44.x86_64 | | CPU | Intel Core i7-13700K, 24 threads | | RAM | 31 GB | | Database | internal MariaDB 11.8.8 (MySQL backend) | | Search settings | similarity range 90–100 %, duplicatesRestriction=0 | Backtraces were produced with `coredumpctl debug ... gdb -ex 'thread apply all bt'` **without** `digikam-debuginfo` installed, so frames are resolved from dynamic symbols and carry no line numbers. Happy to reinstall with debuginfo and re-run if a line-accurate trace would help — the dumps are still on disk. ## Note on scope Filing this against #514721 because the symptom matches (crash when running a duplicate search over a large collection, 9.0–9.2). If maintainers consider the results-view population a distinct code path from what the original reporter hit, this warrants splitting into its own report — the backtrace above stands on its own either way. -- You are receiving this mail because: You are watching all bug changes.
