23#include <sys/socket.h>
26#include <unordered_set>
29namespace MultiProcess {
103 pid_t child_pid = fork();
105 while (child_pid == -1) {
108 printf(
"fork returned with error number %d, retrying after 1 second...\n", errno);
112 printf(
"fork returned with error number %d\n", errno);
113 throw std::runtime_error(
"fork returned with error 3 times, aborting!");
125void set_close_on_exec(
int fd)
127 int flags = fcntl(fd, F_GETFD, 0);
128 if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
129 throw std::runtime_error(std::string(
"ProcessManager: could not set FD_CLOEXEC: ") + strerror(errno));
133void make_socketpair(std::array<int, 2> &fds)
135 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds.data()) != 0) {
136 throw std::runtime_error(std::string(
"ProcessManager: socketpair failed: ") + strerror(errno));
138 set_close_on_exec(fds[0]);
139 set_close_on_exec(fds[1]);
142void close_fd_pair(std::array<int, 2> &fds,
int keep = -1)
144 for (
int &fd : fds) {
145 if (fd >= 0 && fd != keep) {
155 throw std::logic_error(
"ProcessManager: channel file descriptor already claimed or not owned by this process");
172 for (std::size_t ix = 0; ix <
N_workers_; ++ix) {
181 for (std::size_t ix = 0; ix <
N_workers_; ++ix) {
246 for (std::size_t ix = 0; ix <
N_workers_; ++ix) {
280 if (pipe(pipe_fds) != 0) {
281 std::perror(
"pipe failed");
284 for (
int fd : pipe_fds) {
285 int flags = fcntl(fd, F_GETFL, 0);
286 if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
287 std::perror(
"fcntl failed");
290 int fd_flags = fcntl(fd, F_GETFD, 0);
291 if (fd_flags == -1 || fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC) == -1) {
292 std::perror(
"fcntl failed");
301 memset(&sa,
'\0',
sizeof(sa));
304 if (sigaction(SIGTERM, &sa,
nullptr) < 0) {
305 std::perror(
"sigaction failed");
311#if defined(__APPLE__)
313 static bool affinity_warned =
false;
315 std::cout <<
"CPU affinity cannot be set on macOS" << std::endl;
316 affinity_warned =
true;
322 std::cerr <<
"WARNING: CPU affinity setting not implemented on Windows, continuing..." << std::endl;
337 CPU_SET(set_cpu, &
mask);
340 if (sched_setaffinity(0,
sizeof(
mask), &
mask) == -1) {
341 std::cerr <<
"WARNING: Could not set CPU affinity, continuing..." << std::endl;
343 std::cerr <<
"CPU affinity set to cpu " << set_cpu <<
" in process " << getpid() << std::endl;
368 }
catch (
const std::exception &
e) {
369 std::cerr <<
"WARNING: something in ProcessManager::terminate threw an exception! Original exception message:\n"
370 <<
e.what() << std::endl;
389 }
while (-1 == pid && EINTR == errno);
392 if (WIFEXITED(status)) {
393 printf(
"exited, status=%d\n", WEXITSTATUS(status));
394 }
else if (WIFSIGNALED(status)) {
395 if (WTERMSIG(status) != SIGTERM) {
396 printf(
"killed by signal %d\n", WTERMSIG(status));
398 }
else if (WIFSTOPPED(status)) {
399 printf(
"stopped by signal %d\n", WSTOPSIG(status));
400 }
else if (WIFCONTINUED(status)) {
401 printf(
"continued\n");
406 if (errno == ECHILD) {
407 printf(
"chill_wait: no children (got ECHILD error code from wait call), done\n");
409 throw std::runtime_error(std::string(
"chill_wait: error in wait call: ") + strerror(errno) +
410 std::string(
", errno ") + std::to_string(errno));
425 std::unordered_set<pid_t> children;
430 children.insert(pid);
433 while (!children.empty()) {
473 printf(
"I'm a worker, PID %d\n", getpid());
475 printf(
"I'm master, PID %d\n", getpid());
477 printf(
"I'm queue, PID %d\n", getpid());
479 printf(
"I'm not master, queue or worker, weird! PID %d\n", getpid());
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t mask
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
R__EXTERN C unsigned int sleep(unsigned int seconds)
static bool getTimingAnalysis()
void initialize_processes(bool cpu_pinning=true)
Fork processes and activate CPU pinning.
std::vector< pid_t > worker_pids_
std::array< int, 2 > mq_fds_
std::vector< std::array< int, 2 > > qw_fds_
std::size_t N_workers() const
void close_channel_fds()
Close all channel ends still owned by this ProcessManager (i.e.
static void handle_sigterm(int signum)
We need this to tell the children to die, because we can't talk to them anymore during JobManager des...
static int sigterm_wake_read_fd_
bool is_initialized() const
int claim_mw_fd(std::size_t worker_ix)
Hand over the master-worker channel end for the current process type.
void close_unused_channel_fds()
Close the channel ends that do not belong to the current process type.
static bool sigterm_received()
int claim_mq_fd()
Hand over the master-queue channel end for the current process type.
void wait_for_sigterm_then_exit()
static int sigterm_wake_write_fd_
std::vector< std::array< int, 2 > > mw_fds_
void identify_processes() const
Print to stdout which type of process we are on and what its PID is (for debugging)
static int sigterm_wake_fd()
Read end of the self-pipe that the SIGTERM handler writes to (or -1 on the master process,...
void terminate() noexcept
Shutdown forked processes if on master and if this process manager is initialized.
ProcessManager(std::size_t N_workers)
void shutdown_processes()
Shutdown forked processes if on master.
static volatile sig_atomic_t sigterm_received_
int claim_qw_fd(std::size_t worker_ix)
Hand over the queue-worker channel end for the current process type.
void create_channel_fds()
Create the socketpairs that connect the processes.
std::size_t worker_id() const
static void setup(pid_t proc, bool set_begin=true)
pid_t fork_and_handle_errors()
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...