|
15 | 15 |
|
16 | 16 | SCRIPT_EXECUTION_TIMEOUT = 120 |
17 | 17 | TIMEOUT_ERROR_EXIT_CODE = 124 |
| 18 | +POLL_INTERVAL_SECONDS = 0.2 |
| 19 | +SIGTERM_GRACE_PERIOD_SECONDS = 0.2 |
| 20 | +STDOUT_READ_TIMEOUT_SECONDS = 5 |
18 | 21 |
|
19 | 22 |
|
20 | 23 | def revert_changes_for_frid(render_context): |
@@ -56,9 +59,15 @@ def _kill_process(proc: subprocess.Popen) -> None: |
56 | 59 | else: |
57 | 60 | proc.terminate() |
58 | 61 | try: |
59 | | - proc.wait(timeout=5) |
| 62 | + proc.wait(timeout=SIGTERM_GRACE_PERIOD_SECONDS) |
60 | 63 | except subprocess.TimeoutExpired: |
61 | | - proc.kill() |
| 64 | + if sys.platform != "win32": |
| 65 | + try: |
| 66 | + os.killpg(os.getpgid(proc.pid), signal.SIGKILL) |
| 67 | + except OSError: |
| 68 | + proc.kill() |
| 69 | + else: |
| 70 | + proc.kill() |
62 | 71 |
|
63 | 72 |
|
64 | 73 | def execute_script( # noqa: C901 |
@@ -100,14 +109,19 @@ def execute_script( # noqa: C901 |
100 | 109 | exc.stdout = partial_stdout |
101 | 110 | raise exc |
102 | 111 | if stop_event is not None: |
103 | | - stop_event.wait(timeout=0.2) |
| 112 | + stop_event.wait(timeout=POLL_INTERVAL_SECONDS) |
104 | 113 | if stop_event.is_set(): |
105 | 114 | _kill_process(proc) |
106 | 115 | raise RenderCancelledError() |
107 | 116 | else: |
108 | | - time.sleep(0.2) |
| 117 | + time.sleep(POLL_INTERVAL_SECONDS) |
109 | 118 |
|
110 | | - stdout = proc.stdout.read() |
| 119 | + # Use communicate() with a timeout because child processes may hold the pipe open after the main process exits. |
| 120 | + try: |
| 121 | + stdout, _ = proc.communicate(timeout=STDOUT_READ_TIMEOUT_SECONDS) |
| 122 | + except subprocess.TimeoutExpired: |
| 123 | + proc.stdout.close() |
| 124 | + stdout = "" |
111 | 125 | elapsed_time = time.time() - start_time |
112 | 126 |
|
113 | 127 | # Log the info about the script execution |
|
0 commit comments