use process descriptors where available for readiness notification

This commit is contained in:
Ariadne Conill 2021-09-19 07:28:30 -05:00
parent 97b1a11be0
commit 2477e7266c

View file

@ -30,23 +30,78 @@
#define SHELL "/bin/sh" #define SHELL "/bin/sh"
/* TODO: Add support for Linux process descriptors once it is okay to require #if defined(__linux__)
* Linux 5.3 or newer. # include <sys/syscall.h>
*/ #endif
/* POSIX compatible fallback using waitpid(2) and usleep(3) */
static inline bool
lif_process_monitor_busyloop(pid_t child, int timeout_sec, int *status)
{
int ticks = 0;
while (ticks < timeout_sec * 10)
{
/* Ugly hack: most executors finish very quickly,
* so give them a chance to finish before sleeping.
*/
usleep(50);
if (waitpid(child, status, WNOHANG) == child)
return true;
usleep(99950);
ticks++;
}
return false;
}
#if defined(__linux__) && defined(__NR_pidfd_open)
/* TODO: remove this wrapper once musl and glibc gain pidfd_open() directly. */
static inline int
lif_pidfd_open(pid_t pid, unsigned int flags)
{
return syscall(__NR_pidfd_open, pid, flags);
}
static inline bool
lif_process_monitor_procdesc(pid_t child, int timeout_sec, int *status)
{
int pidfd = lif_pidfd_open(child, 0);
/* pidfd_open() not available, fall back to busyloop */
if (pidfd == -1 && errno == ENOSYS)
return lif_process_monitor_busyloop(child, timeout_sec, status);
struct pollfd pfd = {
.fd = pidfd,
.events = POLLIN,
};
if (poll(&pfd, 1, timeout_sec * 1000) < 1)
return false;
waitpid(child, status, 0);
close(pidfd);
return true;
}
#endif
static inline bool static inline bool
lif_process_monitor(const char *cmdbuf, pid_t child, int timeout_sec) lif_process_monitor(const char *cmdbuf, pid_t child, int timeout_sec)
{ {
int status; int status;
int ticks = 0;
while (ticks < timeout_sec) #if defined(__linux__) && defined(__NR_pidfd_open)
{ if (lif_process_monitor_procdesc(child, timeout_sec, &status))
if (waitpid(child, &status, WNOHANG) == child) return WIFEXITED(status) && WEXITSTATUS(status) == 0;
return WIFEXITED(status) && WEXITSTATUS(status) == 0; #else
if (lif_process_monitor_busyloop(child, timeout_sec, &status))
sleep(1); return WIFEXITED(status) && WEXITSTATUS(status) == 0;
ticks++; #endif
}
fprintf(stderr, "execution of '%s': timeout after %d seconds\n", cmdbuf, timeout_sec); fprintf(stderr, "execution of '%s': timeout after %d seconds\n", cmdbuf, timeout_sec);
kill(child, SIGKILL); kill(child, SIGKILL);