| 1310 | } |
| 1311 | |
| 1312 | static cbm_proc_poll_t cbm_subprocess_poll_posix(cbm_subprocess_t *process, |
| 1313 | cbm_proc_result_t *out) { |
| 1314 | uint64_t now = cbm_now_ms(); |
| 1315 | |
| 1316 | if (!process->root_reaped) { |
| 1317 | int status = 0; |
| 1318 | pid_t waited = waitpid(process->pid, &status, WNOHANG); |
| 1319 | if (waited == process->pid) { |
| 1320 | cbm_posix_capture_root(process, status); |
| 1321 | } else if (waited < 0 && errno != EINTR) { |
| 1322 | /* ECHILD means another reaper consumed the status. Other permanent |
| 1323 | * wait failures are treated the same: retain containment, stop the |
| 1324 | * tree, and never spin forever on the failed wait operation. */ |
| 1325 | process->root_reaped = true; |
| 1326 | process->result.outcome = process->timed_out ? CBM_PROC_HANG : CBM_PROC_KILLED; |
| 1327 | process->result.exit_code = -1; |
| 1328 | process->result.term_signal = 0; |
| 1329 | cbm_posix_begin_termination(process, now); |
| 1330 | } |
| 1331 | } |
| 1332 | |
| 1333 | bool group_active = cbm_posix_group_active(process); |
| 1334 | if (!process->termination_started) { |
| 1335 | if (cbm_subprocess_cancellation_requested(process)) { |
| 1336 | cbm_posix_begin_termination(process, now); |
| 1337 | } else if (!process->root_reaped && process->quiet_timeout_ms > 0 && |
| 1338 | now - process->last_activity_ms >= (uint64_t)process->quiet_timeout_ms) { |
| 1339 | process->timed_out = true; |
| 1340 | cbm_posix_begin_termination(process, now); |
| 1341 | } else if (process->root_reaped && group_active) { |
| 1342 | /* A root that daemonizes children is not terminal. Preserve its exit |
| 1343 | * classification, but drain the descendants through the same path. */ |
| 1344 | cbm_posix_begin_termination(process, now); |
| 1345 | } |
| 1346 | } |
| 1347 | if (process->termination_started && group_active && !process->force_sent && |
| 1348 | now - process->termination_started_ms >= (uint64_t)process->cancel_grace_ms) { |
| 1349 | cbm_posix_force_tree(process, now); |
| 1350 | } |
| 1351 | group_active = cbm_posix_group_active(process); |
| 1352 | if (process->force_started_ms != 0 && group_active && |
| 1353 | now - process->force_started_ms >= CBM_SUBPROCESS_FORCE_SETTLE_MS) { |
| 1354 | return cbm_subprocess_finish_failed(process, out); |
| 1355 | } |
| 1356 | if (process->root_reaped && !group_active) { |
| 1357 | return cbm_subprocess_finish(process, out); |
| 1358 | } |
| 1359 | return CBM_PROC_POLL_RUNNING; |
| 1360 | } |
| 1361 | |
| 1362 | #endif /* _WIN32 */ |
| 1363 |
no test coverage detected