refactor(app_controller): add submit_io helper; migrate log_pruner ad-hoc threads
Phase 6 (partial) of startup_speedup_20260606 track. Added AppController.submit_io(fn, *args, **kwargs) as the public API for submitting fire-and-forget background work. Returns a concurrent.futures.Future for lifecycle tracking. The _io_pool is the shared 4-worker pool from src/io_pool.py. Migrated 2 ad-hoc threading.Thread spawns to use submit_io: - _manual_prune_logs() spawn: manual log pruning (cb) - _prune_old_logs() spawn: startup log pruning (startup) Both were threading.Thread(target=fn, daemon=True).start() calls. The spawn cost (~1-5ms per thread creation) is eliminated; both jobs now share the 4-worker _io_pool. REMAINING AD-HOC THREADS (documented in state.toml as follow-up): - app_controller.py: ~13 more threading.Thread() spawns (models fetch, project switch, fetch workers, post workers, MMA spawn workers, etc.) - gui_2.py: 2 spawns (stats worker, secondary worker) - api_hooks.py: 2 spawns (HookServer and WebSocketServer threads - these are domain-specific, NOT migrated per the spec exemption) - multi_agent_conductor.py: 1 spawn (WorkerPool - domain-specific) - performance_monitor.py: 1 spawn (CPU monitor - continuous sampling) The remaining ad-hoc thread migrations could be a follow-up sub-track. The architectural pattern is now established (submit_io); the migration of the remaining cases is mechanical and lower-risk. TESTS: - tests/test_log_pruner.py, test_log_pruning_heuristic.py, test_logging_e2e.py, test_app_controller_mcp.py, test_app_controller_offloading.py, test_app_controller_no_top_level_fastapi.py: 15/15 PASS
This commit is contained in:
+21
-4
@@ -2002,8 +2002,7 @@ class AppController:
|
||||
self.ai_status = f"Manual prune error: {e}"
|
||||
print(f"Error during manual log pruning: {e}")
|
||||
|
||||
thread = threading.Thread(target=run_manual_prune, daemon=True)
|
||||
thread.start()
|
||||
self.submit_io(run_manual_prune)
|
||||
|
||||
def _load_active_project(self) -> None:
|
||||
"""Loads the active project configuration, with fallbacks."""
|
||||
@@ -2052,8 +2051,7 @@ class AppController:
|
||||
pruner.prune()
|
||||
except Exception as e:
|
||||
print(f"Error during log pruning: {e}")
|
||||
thread = threading.Thread(target=run_prune, daemon=True)
|
||||
thread.start()
|
||||
self.submit_io(run_prune)
|
||||
|
||||
def _fetch_models(self, provider: str) -> None:
|
||||
"""
|
||||
@@ -2152,6 +2150,25 @@ class AppController:
|
||||
"""
|
||||
self._warmup.on_complete(callback)
|
||||
|
||||
def submit_io(self, fn: Callable, *args: Any, **kwargs: Any) -> Any:
|
||||
"""
|
||||
Submit a background job to the shared _io_pool. Use this for any
|
||||
fire-and-forget background work; avoids the per-spawn cost of a new
|
||||
threading.Thread.
|
||||
|
||||
Returns a concurrent.futures.Future that can be used to track
|
||||
completion, raise exceptions, or cancel the job. The pool is capped
|
||||
at 4 workers (see src/io_pool.py) so the job may queue briefly if
|
||||
the pool is saturated.
|
||||
|
||||
Domain-specific threads (HookServer, WebSocketServer, MMA WorkerPool,
|
||||
asyncio loop) are NOT submitted here - they have their own lifecycle
|
||||
management.
|
||||
[SDM: src/app_controller.py:submit_io]
|
||||
"""
|
||||
import concurrent.futures
|
||||
return self._io_pool.submit(fn, *args, **kwargs)
|
||||
|
||||
def shutdown(self) -> None:
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user