|
21 | 21 | #ifndef _WIN32 |
22 | 22 | #include <fcntl.h> |
23 | 23 | #include <signal.h> |
| 24 | +#include <sys/time.h> |
24 | 25 | #include <unistd.h> |
25 | 26 | #endif |
26 | 27 |
|
@@ -192,6 +193,52 @@ TEST(subprocess_retries_transient_spawn_refusal) { |
192 | 193 | #endif |
193 | 194 | } |
194 | 195 |
|
| 196 | +#ifndef _WIN32 |
| 197 | +static volatile sig_atomic_t g_spawn_backoff_alarm_count = 0; |
| 198 | + |
| 199 | +static void spawn_backoff_alarm_handler(int signal_number) { |
| 200 | + (void)signal_number; |
| 201 | + g_spawn_backoff_alarm_count++; |
| 202 | +} |
| 203 | +#endif |
| 204 | + |
| 205 | +TEST(subprocess_spawn_backoff_resumes_after_eintr) { |
| 206 | +#ifdef _WIN32 |
| 207 | + SKIP_PLATFORM("POSIX signal interruption"); |
| 208 | +#else |
| 209 | + struct sigaction action = {0}; |
| 210 | + struct sigaction previous_action = {0}; |
| 211 | + action.sa_handler = spawn_backoff_alarm_handler; |
| 212 | + (void)sigemptyset(&action.sa_mask); |
| 213 | + bool handler_installed = sigaction(SIGALRM, &action, &previous_action) == 0; |
| 214 | + |
| 215 | + struct itimerval timer = { |
| 216 | + .it_interval = {.tv_sec = 0, .tv_usec = 1000}, |
| 217 | + .it_value = {.tv_sec = 0, .tv_usec = 1000}, |
| 218 | + }; |
| 219 | + g_spawn_backoff_alarm_count = 0; |
| 220 | + bool timer_started = handler_installed && setitimer(ITIMER_REAL, &timer, NULL) == 0; |
| 221 | + |
| 222 | + uint64_t started_at = cbm_now_ms(); |
| 223 | + cbm_subprocess_force_spawn_eagain_for_testing(3); |
| 224 | + cbm_proc_result_t result = run_sh("exit 0", 0); |
| 225 | + uint64_t elapsed_ms = cbm_now_ms() - started_at; |
| 226 | + |
| 227 | + struct itimerval disabled = {0}; |
| 228 | + (void)setitimer(ITIMER_REAL, &disabled, NULL); |
| 229 | + if (handler_installed) { |
| 230 | + (void)sigaction(SIGALRM, &previous_action, NULL); |
| 231 | + } |
| 232 | + |
| 233 | + ASSERT_TRUE(handler_installed); |
| 234 | + ASSERT_TRUE(timer_started); |
| 235 | + ASSERT_TRUE(g_spawn_backoff_alarm_count > 0); |
| 236 | + ASSERT_TRUE(elapsed_ms >= 50); |
| 237 | + ASSERT_EQ(result.outcome, CBM_PROC_CLEAN); |
| 238 | + PASS(); |
| 239 | +#endif |
| 240 | +} |
| 241 | + |
195 | 242 | TEST(subprocess_gives_up_after_the_retry_budget) { |
196 | 243 | #ifdef _WIN32 |
197 | 244 | SKIP_PLATFORM("POSIX fork/EAGAIN path"); |
@@ -1033,6 +1080,7 @@ SUITE(subprocess) { |
1033 | 1080 | RUN_TEST(subprocess_run_crash_is_crash); |
1034 | 1081 | RUN_TEST(subprocess_run_hang_is_hang); |
1035 | 1082 | RUN_TEST(subprocess_retries_transient_spawn_refusal); |
| 1083 | + RUN_TEST(subprocess_spawn_backoff_resumes_after_eintr); |
1036 | 1084 | RUN_TEST(subprocess_gives_up_after_the_retry_budget); |
1037 | 1085 | RUN_TEST(subprocess_run_spawn_failure); |
1038 | 1086 | RUN_TEST(subprocess_run_null_bin_rejected); |
|
0 commit comments