""" System Cleanup Manager Gestionnaire centralisé pour le nettoyage propre du système à l'arrêt. Exigence 6.5: Libération propre de toutes les ressources """ import atexit import gc import logging import signal import sys import threading import weakref from typing import Any, Callable, Dict, List, Optional logger = logging.getLogger(__name__) class CleanupManager: """ Gestionnaire centralisé pour le nettoyage du système. Gère l'arrêt propre de tous les composants du système : - Memory managers et caches - GPU resource managers - Threads et timers - Connexions réseau - Fichiers ouverts - Processus enfants """ _instance: Optional["CleanupManager"] = None _lock = threading.Lock() def __new__(cls): with cls._lock: if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance def __init__(self): if self._initialized: return self._cleanup_functions: List[Callable[[], None]] = [] self._cleanup_objects: List[weakref.ReferenceType] = [] self._shutdown_in_progress = False self._lock = threading.Lock() # Enregistrer les handlers de signaux self._register_signal_handlers() # Enregistrer atexit atexit.register(self.shutdown) self._initialized = True logger.info("CleanupManager initialized") def _register_signal_handlers(self) -> None: """Enregistre les handlers pour les signaux système.""" def signal_handler(signum, frame): logger.info(f"Received signal {signum}, initiating shutdown...") self.shutdown() sys.exit(0) # Enregistrer les signaux principaux try: signal.signal(signal.SIGINT, signal_handler) # Ctrl+C signal.signal(signal.SIGTERM, signal_handler) # Termination if hasattr(signal, 'SIGHUP'): signal.signal(signal.SIGHUP, signal_handler) # Hangup (Unix) except (OSError, ValueError) as e: logger.warning(f"Could not register signal handler: {e}") def register_cleanup_function(self, cleanup_func: Callable[[], None], name: str = None) -> None: """ Enregistre une fonction de nettoyage. Args: cleanup_func: Fonction à appeler lors du shutdown name: Nom optionnel pour le logging """ with self._lock: if self._shutdown_in_progress: logger.warning("Cannot register cleanup function during shutdown") return self._cleanup_functions.append(cleanup_func) logger.debug(f"Registered cleanup function: {name or 'unnamed'}") def register_cleanup_object(self, obj: Any, method_name: str = "shutdown") -> None: """ Enregistre un objet avec une méthode de nettoyage. Args: obj: Objet à nettoyer method_name: Nom de la méthode à appeler (défaut: "shutdown") """ with self._lock: if self._shutdown_in_progress: logger.warning("Cannot register cleanup object during shutdown") return if not hasattr(obj, method_name): logger.warning(f"Object {obj} does not have method {method_name}") return # Utiliser une weak reference pour éviter les cycles def cleanup_callback(ref): # Callback appelé quand l'objet est garbage collected with self._lock: if ref in self._cleanup_objects: self._cleanup_objects.remove(ref) weak_ref = weakref.ref(obj, cleanup_callback) self._cleanup_objects.append(weak_ref) logger.debug(f"Registered cleanup object: {obj.__class__.__name__}") def register_memory_manager(self) -> None: """Enregistre le memory manager global pour nettoyage.""" try: from core.execution.memory_cache import shutdown_memory_manager self.register_cleanup_function(shutdown_memory_manager, "MemoryManager") except ImportError: logger.warning("Could not import memory manager for cleanup") def register_gpu_manager(self) -> None: """Enregistre le GPU resource manager pour nettoyage.""" try: from core.gpu.gpu_resource_manager import get_gpu_resource_manager gpu_manager = get_gpu_resource_manager() self.register_cleanup_object(gpu_manager, "shutdown") except ImportError: logger.warning("Could not import GPU manager for cleanup") def register_analytics_system(self) -> None: """Enregistre le système d'analytics pour nettoyage.""" try: from core.analytics.analytics_system import get_analytics_system analytics = get_analytics_system() self.register_cleanup_object(analytics, "shutdown") except ImportError: logger.debug("Analytics system not available for cleanup") def register_all_core_systems(self) -> None: """Enregistre tous les systèmes core pour nettoyage automatique.""" logger.info("Registering all core systems for cleanup...") self.register_memory_manager() self.register_gpu_manager() self.register_analytics_system() logger.info("Core systems registered for cleanup") def shutdown(self) -> None: """ Effectue le nettoyage complet du système. Ordre de nettoyage : 1. Fonctions de nettoyage personnalisées 2. Objets enregistrés 3. Garbage collection forcé """ with self._lock: if self._shutdown_in_progress: return self._shutdown_in_progress = True logger.info("Starting system cleanup...") # 1. Nettoyer les fonctions personnalisées cleanup_count = 0 for cleanup_func in self._cleanup_functions: try: cleanup_func() cleanup_count += 1 except Exception as e: logger.error(f"Error in cleanup function: {e}") logger.info(f"Executed {cleanup_count} cleanup functions") # 2. Nettoyer les objets enregistrés object_count = 0 for weak_ref in self._cleanup_objects[:]: # Copie pour éviter modification pendant itération obj = weak_ref() if obj is not None: try: if hasattr(obj, 'shutdown'): obj.shutdown() elif hasattr(obj, 'close'): obj.close() elif hasattr(obj, 'cleanup'): obj.cleanup() object_count += 1 except Exception as e: logger.error(f"Error cleaning up object {obj}: {e}") logger.info(f"Cleaned up {object_count} objects") # 3. Garbage collection forcé try: collected = gc.collect() logger.info(f"Garbage collection freed {collected} objects") except Exception as e: logger.error(f"Error during garbage collection: {e}") # 4. Nettoyer les listes with self._lock: self._cleanup_functions.clear() self._cleanup_objects.clear() logger.info("System cleanup completed") def is_shutdown_in_progress(self) -> bool: """Vérifie si le shutdown est en cours.""" return self._shutdown_in_progress # Instance globale _cleanup_manager: Optional[CleanupManager] = None def get_cleanup_manager() -> CleanupManager: """ Retourne l'instance globale du cleanup manager. Returns: Instance du CleanupManager """ global _cleanup_manager if _cleanup_manager is None: _cleanup_manager = CleanupManager() return _cleanup_manager def register_cleanup_function(cleanup_func: Callable[[], None], name: str = None) -> None: """ Fonction utilitaire pour enregistrer une fonction de nettoyage. Args: cleanup_func: Fonction à appeler lors du shutdown name: Nom optionnel pour le logging """ get_cleanup_manager().register_cleanup_function(cleanup_func, name) def register_cleanup_object(obj: Any, method_name: str = "shutdown") -> None: """ Fonction utilitaire pour enregistrer un objet avec nettoyage. Args: obj: Objet à nettoyer method_name: Nom de la méthode à appeler """ get_cleanup_manager().register_cleanup_object(obj, method_name) def initialize_system_cleanup() -> None: """ Initialise le système de cleanup avec tous les composants core. À appeler au démarrage de l'application. """ cleanup_manager = get_cleanup_manager() cleanup_manager.register_all_core_systems() logger.info("System cleanup initialized") def shutdown_system() -> None: """ Force le shutdown du système. À appeler pour un arrêt propre programmé. """ get_cleanup_manager().shutdown()