Created attachment 192040 [details] Test file for reproducing the problem SUMMARY This bug report originates from this KA thread https://krita-artists.org/t/waiting-for-image-operation-to-complete-takes-2-hours-on-100-cpu/180876 A KRA file with text in it and 2400 PPI takes forever to open. Altering the file to be 300 PPI opens immediately. STEPS TO REPRODUCE 1. Attempt to open the attached TEST003.kra. OBSERVED RESULT Krita takes seemingly forever to open it while "waiting for image operation to complete". EXPECTED RESULT The file opens in a reasonable time. SOFTWARE/OS VERSIONS Original author is on Arch Linux with AppImage. Also tested on Linux Mint with the AppImage and with compiled sources in the development Docker container under Arch. ADDITIONAL INFORMATION The issue only occurs when opening the file, at "runtime" it works just fine. Getting some stack traces in gdb shows that most threads are twiddling their thumbs on a mutex and one thread keeps pumping jobs that don't seem to do anything. After trying for a while, I could never break into any code outside of the job scheduler stuff anyway.
This was reproduced several times in the linked thread, setting to confirmed.
Here is a backtrace: Thread 1 (Thread 0x7fffeeeec8c0 (LWP 56) "krita"): #0 syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38 #1 0x00007ffff5029a9d in QBasicMutex::lockInternal() () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Core.so.6 #2 0x00007ffff6c37498 in QBasicMutex::lock (this=0x5555590ecaa0) at /home/appimage/appimage-workspace/deps/usr/include/QtCore/qmutex.h:41 #3 QMutexLocker<QMutex>::QMutexLocker (mutex=0x5555590ecaa0, this=<synthetic pointer>) at /home/appimage/appimage-workspace/deps/usr/include/QtCore/qmutex.h:223 #4 KisSimpleUpdateQueue::isEmpty (this=0x5555590eca98) at /home/appimage/persistent/krita/libs/image/kis_simple_update_queue.cpp:267 #5 0x00007ffff6c40c05 in KisUpdateScheduler::tryBarrierLock (this=this@entry=0x7fff5c007938) at /home/appimage/persistent/krita/libs/image/kis_update_scheduler.cpp:331 #6 0x00007ffff6c40c81 in KisUpdateScheduler::isIdle (this=0x7fff5c007938) at /home/appimage/persistent/krita/libs/image/kis_update_scheduler.cpp:313 #7 0x00007ffff6c5ed37 in KisImage::isIdle (this=<optimized out>, allowLocked=<optimized out>) at /home/appimage/persistent/krita/libs/image/kis_image.cc:791 #8 0x00007ffff75fdc3a in KisDelayedSaveDialog::Private::checkImageIdle (this=<optimized out>) at /home/appimage/persistent/krita/libs/ui/dialogs/kis_delayed_save_dialog.cpp:28 #9 KisDelayedSaveDialog::slotTimerTimeout (this=0x7fffffffb8e0) at /home/appimage/persistent/krita/libs/ui/dialogs/kis_delayed_save_dialog.cpp:117 #10 0x00007ffff4ed5f9b in void doActivate<false>(QObject*, int, void**) () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Core.so.6 #11 0x00007ffff4ee56ee in QTimer::timeout(QTimer::QPrivateSignal) () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Core.so.6 #12 0x00007ffff4ec9419 in QObject::event(QEvent*) () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Core.so.6 #13 0x00007ffff5ed4735 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Widgets.so.6 #14 0x00007ffff7b1b395 in KisApplication::notify (this=0x7fffffffdfc0, receiver=0x5555590f6e68, event=0x7fffffffb550) at /home/appimage/persistent/krita/libs/ui/KisApplication.cpp:920 #15 0x00007ffff4e76258 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Core.so.6 #16 0x00007ffff50241da in QTimerInfoList::activateTimers() () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Core.so.6 #17 0x00007ffff51502f4 in idleTimerSourceDispatch(_GSource*, int (*)(void*), void*) () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Core.so.6 #18 0x00007ffff211dd3b in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 #19 0x00007ffff21732b8 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 #20 0x00007ffff211b3e3 in g_main_context_iteration () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 #21 0x00007ffff5150480 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Core.so.6 #22 0x00007ffff4e82bb2 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Core.so.6 #23 0x00007ffff6142c87 in QDialog::exec() () from /home/appimage/appimage-workspace/deps/usr/lib/libQt6Widgets.so.6 #24 0x00007ffff75fdd3f in KisDelayedSaveDialog::blockIfImageIsBusy (this=this@entry=0x7fffffffb8e0) at /home/appimage/persistent/krita/libs/ui/dialogs/kis_delayed_save_dialog.cpp:111 #25 0x00007ffff7b95bc4 in (anonymous namespace)::busyWaitWithFeedback (image=...) at /home/appimage/persistent/krita/libs/ui/KisPart.cpp:142 #26 0x00007ffff7b97052 in std::__invoke_impl<void, void (*&)(KisSharedPtr<KisImage>), KisSharedPtr<KisImage> > (__f=<optimized out>) at /usr/include/c++/13/bits/invoke.h:61 #27 std::__invoke_r<void, void (*&)(KisSharedPtr<KisImage>), KisSharedPtr<KisImage> > (__fn=<optimized out>) at /usr/include/c++/13/bits/invoke.h:111 #28 std::_Function_handler<void (KisSharedPtr<KisImage>), void (*)(KisSharedPtr<KisImage>)>::_M_invoke(std::_Any_data const&, KisSharedPtr<KisImage>&&) (__functor=..., __args#0=...) at /usr/include/c++/13/bits/std_function.h:290 #29 0x00007ffff6b90127 in std::function<void (KisSharedPtr<KisImage>)>::operator()(KisSharedPtr<KisImage>) const (__args#0=..., this=<optimized out>) at /usr/include/c++/13/bits/std_function.h:591 #30 KisBusyWaitBroker::notifyWaitOnImageStarted (this=0x7ffff7010048 <QGlobalStatic<QtGlobalStatic::Holder<(anonymous namespace)::Q_QGS_s_instance> >::instance()::holder>, image=<optimized out>, image@entry=0x555562fd87d0) at /home/appimage/persistent/krita/libs/image/KisBusyWaitBroker.cpp:61 #31 0x00007ffff6c5fbfd in KisImage::waitForDone (this=0x555562fd87d0) at /home/appimage/persistent/krita/libs/image/kis_image.cc:1957 #32 0x00007ffff6c5fd02 in KisImage::initialRefreshGraph (this=<optimized out>) at /home/appimage/persistent/krita/libs/image/kis_image.cc:2219 #33 0x00007ffff7b34dd3 in KisDocument::setCurrentImage (this=this@entry=0x555563051d20, image=..., forceInitialUpdate=false, forceInitialUpdate@entry=true, preActivatedNode=...) at /home/appimage/persistent/krita/libs/ui/KisDocument.cpp:2898 #34 0x00007fff95dac082 in KraImport::convert (this=<optimized out>, document=0x555563051d20, io=<optimized out>) at /home/appimage/persistent/krita/plugins/impex/kra/kra_import.cpp:32 #35 0x00007ffff7b56ebe in KisImportExportManager::doImport (this=0x5555616e47e0, location=..., filter=...) at /home/appimage/persistent/krita/libs/ui/KisImportExportManager.cpp:743 #36 0x00007ffff7b589ce in KisImportExportManager::convert (this=0x5555616e47e0, direction=<optimized out>, location=..., realLocation=..., mimeType=..., showWarnings=<optimized out>, exportConfiguration=..., isAsync=<optimized out>, isAdvancedExporting=<optimized out>) at /home/appimage/persistent/krita/libs/ui/KisImportExportManager.cpp:404 #37 0x00007ffff7b5989d in KisImportExportManager::importDocument (this=<optimized out>, location=..., mimeType=...) at /home/appimage/persistent/krita/libs/global/kis_shared_ptr.h:209 #38 0x00007ffff7b37c4e in KisDocument::openFile (this=0x555563051d20) at /home/appimage/persistent/krita/libs/ui/KisDocument.cpp:2070 #39 0x00007ffff7b38a7e in KisDocument::openPathInternal (this=0x555563051d20, path=...) at /home/appimage/persistent/krita/libs/ui/KisDocument.cpp:2650 #40 0x00007ffff7b42e36 in KisDocument::openPath (this=this@entry=0x555563051d20, _path=..., flags=..., flags@entry=...) at /home/appimage/persistent/krita/libs/ui/KisDocument.cpp:2000 #41 0x00007ffff7b6d289 in KisMainWindow::openDocumentInternal (this=<optimized out>, path=..., flags=...) at /home/appimage/persistent/krita/libs/ui/KisMainWindow.cpp:1098 #42 0x00007ffff7b6d9ee in KisMainWindow::openDocument (this=0x5555588f5a00, path=..., flags=...) at /home/appimage/persistent/krita/libs/ui/KisMainWindow.cpp:1072 #43 0x00007ffff7b6de65 in KisMainWindow::slotFileOpen (this=0x5555588f5a00, isImporting=false) at /home/appimage/persistent/krita/libs/ui/KisMainWindow.cpp:1806 It seems like we somehow manage to enter recursive locking while loading the file. I don't yet know how.
Okay, that is NOT Qt-version specific.
Git commit 91e60134b1dd26f48ddeb3a34c3a68b9a51630e3 by Dmitry Kazakov. Committed on 06/05/2026 at 11:02. Pushed by dkazakov into branch 'master'. Skip adding empty walkers into the updates queue If some code accidentially adds a huge dirty rect to the updates queue it may choke the scheduler for quite a long time. It can basically add 500000 of empty walkers into the list, each of which would be effectively noops. When the scheduler will later try to execute these noop-walkers in a multithreaded environment, it can make Krita freeze for several minutes. This patch allows skipping the potential noop walkers at the state of adding into the queue. Care must be taken to **not** skip the updates, which are technically executed outside of the bounds of the image, but have clone layers linked, which may read from these out-of-bounds areas. M +10 -1 libs/image/kis_base_rects_walker.h M +3 -1 libs/image/kis_simple_update_queue.cpp M +80 -0 libs/image/tests/kis_walkers_test.cpp M +2 -0 libs/image/tests/kis_walkers_test.h https://invent.kde.org/graphics/krita/-/commit/91e60134b1dd26f48ddeb3a34c3a68b9a51630e3
Git commit d627fb434dabb9874c528f1b4fbce28f3667b3c6 by Dmitry Kazakov. Committed on 06/05/2026 at 11:02. Pushed by dkazakov into branch 'master'. Fix freeze on opening a hi-dpi image with a vector layer There was a bug in `KisShapeLayerCanvas::setImage()`, which requested an update in **image** coordinates instead of **document** coordinates. This caused a really huge update to be requested (330704x466138 pixels). These pixels were mostly outside of the image bounds, so were cropped by the cropping mechanism in `KisMergeWalker`. But due to a mis-optimization in `KisSimpleUpdatesQueue`, these noop updates could choke the queue and the scheduler. M +2 -1 libs/ui/flake/kis_shape_layer_canvas.cpp https://invent.kde.org/graphics/krita/-/commit/d627fb434dabb9874c528f1b4fbce28f3667b3c6
Git commit e67889d0d83e589ca4a6898b82d2d6baab2d1181 by Dmitry Kazakov. Committed on 06/05/2026 at 11:02. Pushed by dkazakov into branch 'master'. Do not block progress bar when there is contestion on the image When the image is busy, `KisDelayedSaveDialog` is shown to the user via the `KisBusyWaitBroker` interface. The dialog regularly calls `image->isIdle()` to check if all operations are completed. Before the patch, `KisUpdateScheduler::isIdle()` tried to hold the mutexes of the queues to check their state. In highly contesting environment (e.g. when 24 worker threads are running), it could cause the GUI thread to just block on these mutexes until the entire operation is completed. This is the exact opposite of what "progress update" code is supposed to do. Now `KisUpdateScheduler::isIdle()` first tries to get the queues' state in `std::try_to_lock` mode, i.e. not blocking on the mutexes. If the mutex is not available, it, obviously, means that the image in **not** idle :) M +7 -0 libs/image/kis_simple_update_queue.cpp M +14 -0 libs/image/kis_simple_update_queue.h M +7 -0 libs/image/kis_strokes_queue.cpp M +14 -0 libs/image/kis_strokes_queue.h M +12 -0 libs/image/kis_update_scheduler.cpp M +8 -0 libs/image/kis_updater_context.cpp M +10 -1 libs/image/kis_updater_context.h https://invent.kde.org/graphics/krita/-/commit/e67889d0d83e589ca4a6898b82d2d6baab2d1181
Git commit 7c1c2e12fee45ea385c222db16f052211163560d by Dmitry Kazakov. Committed on 06/05/2026 at 11:35. Pushed by dkazakov into branch 'krita/6.0'. Do not block progress bar when there is contestion on the image When the image is busy, `KisDelayedSaveDialog` is shown to the user via the `KisBusyWaitBroker` interface. The dialog regularly calls `image->isIdle()` to check if all operations are completed. Before the patch, `KisUpdateScheduler::isIdle()` tried to hold the mutexes of the queues to check their state. In highly contesting environment (e.g. when 24 worker threads are running), it could cause the GUI thread to just block on these mutexes until the entire operation is completed. This is the exact opposite of what "progress update" code is supposed to do. Now `KisUpdateScheduler::isIdle()` first tries to get the queues' state in `std::try_to_lock` mode, i.e. not blocking on the mutexes. If the mutex is not available, it, obviously, means that the image in **not** idle :) M +7 -0 libs/image/kis_simple_update_queue.cpp M +14 -0 libs/image/kis_simple_update_queue.h M +7 -0 libs/image/kis_strokes_queue.cpp M +14 -0 libs/image/kis_strokes_queue.h M +12 -0 libs/image/kis_update_scheduler.cpp M +8 -0 libs/image/kis_updater_context.cpp M +10 -1 libs/image/kis_updater_context.h https://invent.kde.org/graphics/krita/-/commit/7c1c2e12fee45ea385c222db16f052211163560d
Git commit a43daa407cba3471e1989decde3d6c3af2b8e07e by Dmitry Kazakov. Committed on 06/05/2026 at 11:35. Pushed by dkazakov into branch 'krita/6.0'. Fix freeze on opening a hi-dpi image with a vector layer There was a bug in `KisShapeLayerCanvas::setImage()`, which requested an update in **image** coordinates instead of **document** coordinates. This caused a really huge update to be requested (330704x466138 pixels). These pixels were mostly outside of the image bounds, so were cropped by the cropping mechanism in `KisMergeWalker`. But due to a mis-optimization in `KisSimpleUpdatesQueue`, these noop updates could choke the queue and the scheduler. M +2 -1 libs/ui/flake/kis_shape_layer_canvas.cpp https://invent.kde.org/graphics/krita/-/commit/a43daa407cba3471e1989decde3d6c3af2b8e07e