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.

Reply via email to