mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-21 10:29:06 +00:00
fix(cache): one cleanup thread per cache directory, not per manager (#453)
The display process ran three cleanup threads over one directory:
14:22:59.954 display_controller (the real manager)
14:22:59.973 startup validation, run 1 (discarded)
14:23:01.055 startup validation, run 2 (discarded)
Two of those managers existed only to read a directory path.
StartupValidator._validate_cache_directory built a whole CacheManager to
call get_cache_dir(), and validation runs twice -- once before the
plugin manager exists and again after. Each construction also probes
writability by writing and deleting .writetest on the card.
The discarded ones never went away. cleanup_loop closes over `self`, so
the thread keeps its manager alive: two objects that could never be
collected, waking every 24 hours to re-scan the same 9,000-file
directory. Nothing stopped them either -- stop_cleanup_thread had no
callers anywhere in the tree.
Two changes. The validator now takes the CacheManager the application
actually uses, which is also the more correct thing to validate; when
no caller supplies one it still builds its own, but stops the thread
afterwards. And CacheManager now tracks which directory it is sweeping,
so the second manager over a directory skips starting a thread at all.
That is the right granularity regardless of call sites: the sweep lists
a directory and deletes from it, so a second thread only duplicates the
scan. Ownership is released on stop, so a survivor can take over rather
than leaving the directory permanently unclaimed by a dead owner.
Measured directly, three managers over one directory: 3 threads before,
1 after.
Claude-Session: https://claude.ai/code/session_01Udr6MfaFLUPhX5Fgo67Jf5
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,16 +15,23 @@ from src.logging_config import get_logger
|
||||
class StartupValidator:
|
||||
"""Validates system state on startup."""
|
||||
|
||||
def __init__(self, config_manager: Any, plugin_manager: Optional[Any] = None) -> None:
|
||||
def __init__(self, config_manager: Any, plugin_manager: Optional[Any] = None,
|
||||
cache_manager: Optional[Any] = None) -> None:
|
||||
"""
|
||||
Initialize the startup validator.
|
||||
|
||||
|
||||
Args:
|
||||
config_manager: ConfigManager instance
|
||||
plugin_manager: Optional PluginManager instance
|
||||
cache_manager: The CacheManager the application will actually use.
|
||||
Pass it. Without one this validator builds its own just to read
|
||||
a directory path, which reports on a cache the app does not
|
||||
use and leaves behind a cleanup thread that nothing stops --
|
||||
validation runs twice per startup, so that was two of them.
|
||||
"""
|
||||
self.config_manager = config_manager
|
||||
self.plugin_manager = plugin_manager
|
||||
self.cache_manager = cache_manager
|
||||
self.logger = get_logger(__name__)
|
||||
self.errors: List[str] = []
|
||||
self.warnings: List[str] = []
|
||||
@@ -91,9 +98,21 @@ class StartupValidator:
|
||||
def _validate_cache_directory(self) -> None:
|
||||
"""Validate cache directory permissions."""
|
||||
try:
|
||||
from src.cache_manager import CacheManager
|
||||
cache_manager = CacheManager()
|
||||
cache_dir = cache_manager.get_cache_dir()
|
||||
cache_manager = self.cache_manager
|
||||
if cache_manager is None:
|
||||
# No caller supplied one (older embedders, direct use in a
|
||||
# script). Build one, but do not leave its cleanup thread
|
||||
# running behind us -- this instance is discarded on the next
|
||||
# line but the thread is a closure over it, so it would never
|
||||
# be collected.
|
||||
from src.cache_manager import CacheManager
|
||||
cache_manager = CacheManager()
|
||||
try:
|
||||
cache_dir = cache_manager.get_cache_dir()
|
||||
finally:
|
||||
cache_manager.stop_cleanup_thread()
|
||||
else:
|
||||
cache_dir = cache_manager.get_cache_dir()
|
||||
|
||||
if not cache_dir:
|
||||
self.warnings.append("Cache directory not available - caching will be disabled")
|
||||
|
||||
Reference in New Issue
Block a user