ClickHouse/src/Common/waitForPid.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

199 lines
4.5 KiB
C++
Raw Normal View History

#include <Common/waitForPid.h>
#include <Common/VersionNumber.h>
#include <Poco/Environment.h>
#include <Common/Stopwatch.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu-statement-expression"
#define HANDLE_EINTR(x) ({ \
decltype(x) eintr_wrapper_result; \
do { \
eintr_wrapper_result = (x); \
} while (eintr_wrapper_result == -1 && errno == EINTR); \
eintr_wrapper_result; \
})
2022-07-21 09:45:09 +00:00
namespace DB
{
enum PollPidResult
{
RESTART,
FAILED
};
}
#if defined(OS_LINUX)
#include <poll.h>
#include <string>
#if !defined(__NR_pidfd_open)
#if defined(__x86_64__)
#define SYS_pidfd_open 434
#elif defined(__aarch64__)
#define SYS_pidfd_open 434
#elif defined(__ppc64__)
#define SYS_pidfd_open 434
#elif defined(__riscv)
#define SYS_pidfd_open 434
#else
#error "Unsupported architecture"
#endif
#else
#define SYS_pidfd_open __NR_pidfd_open
#endif
namespace DB
{
static int syscall_pidfd_open(pid_t pid)
{
return syscall(SYS_pidfd_open, pid, 0);
}
static bool supportsPidFdOpen()
{
VersionNumber pidfd_open_minimal_version(5, 3, 0);
VersionNumber linux_version(Poco::Environment::osVersion());
return linux_version >= pidfd_open_minimal_version;
}
2022-07-21 09:45:09 +00:00
static PollPidResult pollPid(pid_t pid, int timeout_in_ms)
{
2022-07-21 09:45:09 +00:00
int pid_fd = 0;
if (supportsPidFdOpen())
{
2022-07-21 09:45:09 +00:00
// pidfd_open cannot be interrupted, no EINTR handling
pid_fd = syscall_pidfd_open(pid);
if (pid_fd < 0)
{
if (errno == ESRCH)
return PollPidResult::RESTART;
return PollPidResult::FAILED;
}
}
else
{
2022-07-21 09:45:09 +00:00
std::string path = "/proc/" + std::to_string(pid);
pid_fd = HANDLE_EINTR(open(path.c_str(), O_DIRECTORY));
2022-07-21 09:45:09 +00:00
if (pid_fd < 0)
{
if (errno == ENOENT)
return PollPidResult::RESTART;
2022-07-21 09:45:09 +00:00
return PollPidResult::FAILED;
}
}
2022-07-21 09:45:09 +00:00
struct pollfd pollfd;
pollfd.fd = pid_fd;
pollfd.events = POLLIN;
2022-07-21 09:45:09 +00:00
int ready = HANDLE_EINTR(poll(&pollfd, 1, timeout_in_ms));
if (ready <= 0)
return PollPidResult::FAILED;
close(pid_fd);
2022-07-21 09:45:09 +00:00
return PollPidResult::RESTART;
}
2022-07-21 09:45:09 +00:00
#elif defined(OS_DARWIN) || defined(OS_FREEBSD)
2022-07-22 10:53:44 +00:00
#pragma clang diagnostic ignored "-Wreserved-identifier"
#include <sys/event.h>
#include <err.h>
namespace DB
{
2022-07-21 09:45:09 +00:00
static PollPidResult pollPid(pid_t pid, int timeout_in_ms)
{
2022-07-21 09:45:09 +00:00
int kq = kqueue();
if (kq == -1)
2022-07-21 09:45:09 +00:00
return PollPidResult::FAILED;
2022-09-20 19:27:18 +00:00
struct kevent change = {.ident = 0};
EV_SET(&change, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
2022-07-21 09:45:09 +00:00
int event_add_result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
if (event_add_result == -1)
{
2022-07-21 09:45:09 +00:00
if (errno == ESRCH)
return PollPidResult::RESTART;
return PollPidResult::FAILED;
}
2022-09-20 19:27:18 +00:00
struct kevent event = {.ident = 0};
struct timespec remaining_timespec = {.tv_sec = timeout_in_ms / 1000, .tv_nsec = (timeout_in_ms % 1000) * 1000000};
2022-07-21 09:45:09 +00:00
int ready = HANDLE_EINTR(kevent(kq, nullptr, 0, &event, 1, &remaining_timespec));
PollPidResult result = ready < 0 ? PollPidResult::FAILED : PollPidResult::RESTART;
close(kq);
2022-07-21 09:45:09 +00:00
return result;
}
#else
#error "Unsupported OS type"
#endif
bool waitForPid(pid_t pid, size_t timeout_in_seconds)
{
int status = 0;
Stopwatch watch;
if (timeout_in_seconds == 0)
{
/// If there is no timeout before signal try to waitpid 1 time without block so we can avoid sending
/// signal if process is already normally terminated.
2022-07-21 09:45:09 +00:00
int waitpid_res = HANDLE_EINTR(waitpid(pid, &status, WNOHANG));
bool process_terminated_normally = (waitpid_res == pid);
return process_terminated_normally;
}
/// If timeout is positive try waitpid without block in loop until
/// process is normally terminated or waitpid return error
int timeout_in_ms = timeout_in_seconds * 1000;
while (timeout_in_ms > 0)
{
2022-07-21 09:45:09 +00:00
int waitpid_res = HANDLE_EINTR(waitpid(pid, &status, WNOHANG));
bool process_terminated_normally = (waitpid_res == pid);
if (process_terminated_normally)
return true;
2022-07-21 09:45:09 +00:00
if (waitpid_res != 0)
return false;
2022-07-21 09:45:09 +00:00
watch.restart();
PollPidResult result = pollPid(pid, timeout_in_ms);
if (result == PollPidResult::FAILED)
return false;
timeout_in_ms -= watch.elapsedMilliseconds();
}
2022-07-21 09:45:09 +00:00
return false;
}
}
#pragma GCC diagnostic pop