Fixed core dumped on close
This commit is contained in:
@@ -34,7 +34,7 @@ from itertools import groupby
|
|||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QApplication, QMainWindow, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QLineEdit,
|
QApplication, QMainWindow, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QLineEdit,
|
||||||
QTextEdit, QPushButton, QFileDialog, QComboBox, QSlider, QMessageBox, QSizePolicy,
|
QTextEdit, QPushButton, QFileDialog, QComboBox, QSlider, QMessageBox, QSizePolicy,
|
||||||
QMenu, QInputDialog, QTableWidget, QTableWidgetItem, QHeaderView, QTabWidget,
|
QMenu, QInputDialog, QTableWidget, QTableWidgetItem, QHeaderView, QTabWidget, QProgressDialog,
|
||||||
QDockWidget, QAbstractItemView, QRadioButton, QButtonGroup, QListView,
|
QDockWidget, QAbstractItemView, QRadioButton, QButtonGroup, QListView,
|
||||||
QStyledItemDelegate, QStyle, QDialog, QKeySequenceEdit, QDialogButtonBox
|
QStyledItemDelegate, QStyle, QDialog, QKeySequenceEdit, QDialogButtonBox
|
||||||
)
|
)
|
||||||
@@ -2010,6 +2010,9 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def perform_shutdown(self):
|
def perform_shutdown(self):
|
||||||
"""Performs cleanup operations before the application closes."""
|
"""Performs cleanup operations before the application closes."""
|
||||||
|
if getattr(self, '_is_shutting_down', False):
|
||||||
|
return
|
||||||
|
self._is_shutting_down = True
|
||||||
self.is_cleaning = True
|
self.is_cleaning = True
|
||||||
|
|
||||||
# Save configuration early if visible, as per user request.
|
# Save configuration early if visible, as per user request.
|
||||||
@@ -2017,6 +2020,24 @@ class MainWindow(QMainWindow):
|
|||||||
if self.isVisible():
|
if self.isVisible():
|
||||||
self.save_config()
|
self.save_config()
|
||||||
|
|
||||||
|
# Stop all pending UI timers to prevent background tasks from triggering
|
||||||
|
self.hide_progress_timer.stop()
|
||||||
|
self.filter_input_timer.stop()
|
||||||
|
self.filter_refresh_timer.stop()
|
||||||
|
self.thumbnails_refresh_timer.stop()
|
||||||
|
self.rebuild_timer.stop()
|
||||||
|
self._model_update_timer.stop()
|
||||||
|
self.resume_scan_timer.stop()
|
||||||
|
|
||||||
|
# Close all viewers first to stop their preloader threads
|
||||||
|
self.close_all_viewers()
|
||||||
|
|
||||||
|
# Close duplicate manager if open to stop its preloader threads
|
||||||
|
if HAVE_IMAGEHASH:
|
||||||
|
for widget in QApplication.topLevelWidgets():
|
||||||
|
if isinstance(widget, DuplicateManagerDialog):
|
||||||
|
widget.close()
|
||||||
|
|
||||||
self.fs_watcher.stop()
|
self.fs_watcher.stop()
|
||||||
# 1. Stop all worker threads interacting with the cache
|
# 1. Stop all worker threads interacting with the cache
|
||||||
|
|
||||||
@@ -2053,7 +2074,19 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
# Ensure all QRunnables in the shared thread pool are finished
|
# Ensure all QRunnables in the shared thread pool are finished
|
||||||
if self.thread_pool_manager:
|
if self.thread_pool_manager:
|
||||||
self.thread_pool_manager.get_pool().waitForDone()
|
pool = self.thread_pool_manager.get_pool()
|
||||||
|
if pool.activeThreadCount() > 0:
|
||||||
|
progress = QProgressDialog(UITexts.SHUTTING_DOWN, None, 0, 0, self)
|
||||||
|
progress.setWindowTitle(PROG_NAME)
|
||||||
|
progress.setWindowModality(Qt.ApplicationModal)
|
||||||
|
progress.setMinimumDuration(0)
|
||||||
|
progress.show()
|
||||||
|
|
||||||
|
# Wait in small increments to keep the UI responsive
|
||||||
|
while not pool.waitForDone(100):
|
||||||
|
if QApplication.instance():
|
||||||
|
QApplication.processEvents()
|
||||||
|
progress.close()
|
||||||
|
|
||||||
if self.duplicate_cache:
|
if self.duplicate_cache:
|
||||||
self.duplicate_cache.lmdb_close()
|
self.duplicate_cache.lmdb_close()
|
||||||
|
|||||||
@@ -571,6 +571,7 @@ class DuplicateDetector(QThread):
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._is_running = False
|
self._is_running = False
|
||||||
|
self.wait() # Add this line
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
total_files = len(self.paths_to_scan)
|
total_files = len(self.paths_to_scan)
|
||||||
|
|||||||
@@ -143,6 +143,11 @@ class DuplicateManagerDialog(QDialog):
|
|||||||
self.main_win.fs_watcher.file_moved.disconnect(self._on_file_moved_externally)
|
self.main_win.fs_watcher.file_moved.disconnect(self._on_file_moved_externally)
|
||||||
except (RuntimeError, TypeError):
|
except (RuntimeError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if hasattr(self, 'left_pane') and self.left_pane:
|
||||||
|
self.left_pane.cleanup()
|
||||||
|
if hasattr(self, 'right_pane') and self.right_pane:
|
||||||
|
self.right_pane.cleanup()
|
||||||
super().closeEvent(event)
|
super().closeEvent(event)
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class ImagePreloader(QThread):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initializes the preloader thread."""
|
"""Initializes the preloader thread."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.setObjectName("ImagePreloaderThread")
|
||||||
self.path = None
|
self.path = None
|
||||||
self.index = -1
|
self.index = -1
|
||||||
self.mutex = QMutex()
|
self.mutex = QMutex()
|
||||||
|
|||||||
@@ -134,13 +134,11 @@ class ScannerWorker(QRunnable):
|
|||||||
sizes_to_check = self.target_sizes if self.target_sizes is not None \
|
sizes_to_check = self.target_sizes if self.target_sizes is not None \
|
||||||
else SCANNER_GENERATE_SIZES
|
else SCANNER_GENERATE_SIZES
|
||||||
|
|
||||||
|
try:
|
||||||
if self._is_cancelled:
|
if self._is_cancelled:
|
||||||
if self.semaphore:
|
|
||||||
self.semaphore.release()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
fd = None
|
fd = None
|
||||||
try:
|
|
||||||
# Optimize: Open file once to reuse FD for stat and xattrs
|
# Optimize: Open file once to reuse FD for stat and xattrs
|
||||||
fd = os.open(self.path, os.O_RDONLY)
|
fd = os.open(self.path, os.O_RDONLY)
|
||||||
stat_res = os.fstat(fd)
|
stat_res = os.fstat(fd)
|
||||||
@@ -285,6 +283,7 @@ class CacheWriter(QThread):
|
|||||||
self._condition_new_data = QWaitCondition()
|
self._condition_new_data = QWaitCondition()
|
||||||
self._condition_space_available = QWaitCondition()
|
self._condition_space_available = QWaitCondition()
|
||||||
# Soft limit for blocking producers (background threads)
|
# Soft limit for blocking producers (background threads)
|
||||||
|
self.setObjectName("CacheWriterThread") # Add this line
|
||||||
self._max_size = 50
|
self._max_size = 50
|
||||||
self._running = True
|
self._running = True
|
||||||
|
|
||||||
@@ -334,6 +333,7 @@ class CacheWriter(QThread):
|
|||||||
self._running = False
|
self._running = False
|
||||||
# Do not clear the queue here; let the run loop drain it to prevent data loss.
|
# Do not clear the queue here; let the run loop drain it to prevent data loss.
|
||||||
self._condition_new_data.wakeAll()
|
self._condition_new_data.wakeAll()
|
||||||
|
logger.debug(f"{self.objectName()} stop requested, waking all.")
|
||||||
self._condition_space_available.wakeAll()
|
self._condition_space_available.wakeAll()
|
||||||
self._mutex.unlock()
|
self._mutex.unlock()
|
||||||
|
|
||||||
@@ -380,6 +380,7 @@ class CacheWriter(QThread):
|
|||||||
self.cache._batch_write_to_lmdb(batch)
|
self.cache._batch_write_to_lmdb(batch)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"CacheWriter batch write error: {e}")
|
logger.error(f"CacheWriter batch write error: {e}")
|
||||||
|
logger.debug(f"{self.objectName()} run method exiting.")
|
||||||
|
|
||||||
|
|
||||||
class CacheLoader(QThread):
|
class CacheLoader(QThread):
|
||||||
@@ -1328,6 +1329,7 @@ class CacheCleaner(QThread):
|
|||||||
def stop(self):
|
def stop(self):
|
||||||
"""Signals the thread to stop."""
|
"""Signals the thread to stop."""
|
||||||
self._is_running = False
|
self._is_running = False
|
||||||
|
self.wait()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.setPriority(QThread.IdlePriority)
|
self.setPriority(QThread.IdlePriority)
|
||||||
@@ -1914,3 +1916,4 @@ class ImageScanner(QThread):
|
|||||||
self.mutex.lock()
|
self.mutex.lock()
|
||||||
self.condition.wakeAll()
|
self.condition.wakeAll()
|
||||||
self.mutex.unlock()
|
self.mutex.unlock()
|
||||||
|
self.wait()
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class DuplicateFileCounter(QThread):
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._abort = True
|
self._abort = True
|
||||||
|
self.wait() # Add this line
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
count = 0
|
count = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user