Run command with timeout and deadlock detection. Returns: Tuple of (success, output, pid_if_timeout)
(
self, cmd: list[str], timeout_seconds: int = 120
)
| 193 | return Path(base_path) |
| 194 | |
| 195 | def _run_with_timeout( |
| 196 | self, cmd: list[str], timeout_seconds: int = 120 |
| 197 | ) -> RunWithTimeoutResult: |
| 198 | """ |
| 199 | Run command with timeout and deadlock detection. |
| 200 | |
| 201 | Returns: |
| 202 | Tuple of (success, output, pid_if_timeout) |
| 203 | """ |
| 204 | # Start process without built-in timeout (we monitor manually) |
| 205 | # Add the DLL/shared library directory to PATH so the binary can find |
| 206 | # fastled.dll (or .so) at runtime. |
| 207 | env = os.environ.copy() |
| 208 | dll_dir = str(Path(f".build/meson-{self.build_mode}/ci/meson/native").resolve()) |
| 209 | if "PATH" in env: |
| 210 | env["PATH"] = dll_dir + os.pathsep + env["PATH"] |
| 211 | else: |
| 212 | env["PATH"] = dll_dir |
| 213 | # Also set LD_LIBRARY_PATH for Linux |
| 214 | if "LD_LIBRARY_PATH" in env: |
| 215 | env["LD_LIBRARY_PATH"] = dll_dir + os.pathsep + env["LD_LIBRARY_PATH"] |
| 216 | else: |
| 217 | env["LD_LIBRARY_PATH"] = dll_dir |
| 218 | proc = subprocess.Popen( |
| 219 | cmd, |
| 220 | stdout=subprocess.PIPE, |
| 221 | stderr=subprocess.PIPE, |
| 222 | text=True, |
| 223 | env=env, |
| 224 | ) |
| 225 | |
| 226 | start_time = time.time() |
| 227 | |
| 228 | # Poll process with timeout monitoring |
| 229 | while True: |
| 230 | retcode = proc.poll() |
| 231 | |
| 232 | # Process finished normally |
| 233 | if retcode is not None: |
| 234 | stdout, stderr = proc.communicate() |
| 235 | output = stdout + stderr |
| 236 | |
| 237 | if retcode == 0: |
| 238 | return RunWithTimeoutResult( |
| 239 | success=True, output=output, pid_if_timeout=None |
| 240 | ) |
| 241 | else: |
| 242 | return RunWithTimeoutResult( |
| 243 | success=False, output=output, pid_if_timeout=None |
| 244 | ) |
| 245 | |
| 246 | # Check timeout |
| 247 | elapsed = time.time() - start_time |
| 248 | if elapsed > timeout_seconds: |
| 249 | # Timeout! Attach debugger before killing |
| 250 | pid = proc.pid |
| 251 | print(f"\n🚨 TIMEOUT EXCEEDED after {elapsed:.1f}s!") |
| 252 | print(f"📍 Attaching debugger to PID {pid}...") |
no test coverage detected