Manages individual monitoring threads for stuck process detection
| 1248 | |
| 1249 | |
| 1250 | class ProcessStuckMonitor: |
| 1251 | """Manages individual monitoring threads for stuck process detection""" |
| 1252 | |
| 1253 | def __init__(self, stuck_process_timeout: float): |
| 1254 | self.stuck_process_timeout = stuck_process_timeout |
| 1255 | self.stuck_signals: Queue[StuckProcessSignal] = Queue() |
| 1256 | self.monitoring_threads: dict[RunningProcess, threading.Thread] = {} |
| 1257 | self.shutdown_event = threading.Event() |
| 1258 | |
| 1259 | def start_monitoring(self, process: RunningProcess) -> None: |
| 1260 | """Start a monitoring thread for the given process""" |
| 1261 | if process in self.monitoring_threads: |
| 1262 | return # Already monitoring this process |
| 1263 | |
| 1264 | monitor_thread = threading.Thread( |
| 1265 | target=self._monitor_process, |
| 1266 | args=(process,), |
| 1267 | name=f"StuckMonitor-{_extract_test_name(process.command)}", |
| 1268 | daemon=True, |
| 1269 | ) |
| 1270 | self.monitoring_threads[process] = monitor_thread |
| 1271 | monitor_thread.start() |
| 1272 | |
| 1273 | def stop_monitoring(self, process: RunningProcess) -> None: |
| 1274 | """Stop monitoring a specific process""" |
| 1275 | if process in self.monitoring_threads: |
| 1276 | del self.monitoring_threads[process] |
| 1277 | |
| 1278 | def shutdown(self) -> None: |
| 1279 | """Shutdown all monitoring threads""" |
| 1280 | self.shutdown_event.set() |
| 1281 | self.monitoring_threads.clear() |
| 1282 | |
| 1283 | def check_for_stuck_processes(self) -> list[StuckProcessSignal]: |
| 1284 | """Check for any stuck process signals from monitoring threads""" |
| 1285 | stuck_processes: list[StuckProcessSignal] = [] |
| 1286 | try: |
| 1287 | while True: |
| 1288 | signal = self.stuck_signals.get_nowait() |
| 1289 | stuck_processes.append(signal) |
| 1290 | except queue.Empty: |
| 1291 | pass |
| 1292 | return stuck_processes |
| 1293 | |
| 1294 | def _monitor_process(self, process: RunningProcess) -> None: |
| 1295 | """Monitor a single process for being stuck (runs in separate thread)""" |
| 1296 | thread_id = threading.current_thread().ident |
| 1297 | thread_name = threading.current_thread().name |
| 1298 | |
| 1299 | try: |
| 1300 | last_activity_time = time.time() |
| 1301 | |
| 1302 | while not self.shutdown_event.is_set(): |
| 1303 | if process.finished: |
| 1304 | # Process completed normally, stop monitoring |
| 1305 | return |
| 1306 | |
| 1307 | # Check if we have recent stdout activity |