22#include <sys/socket.h>
26namespace MultiProcess {
30constexpr std::uint64_t moreBit = std::uint64_t(1) << 63;
31constexpr std::uint64_t sizeMask = moreBit - 1;
40std::vector<Channel *> &liveChannels()
42 static auto *channels =
new std::vector<Channel *>;
46void registerChannel(Channel *channel)
48 liveChannels().push_back(channel);
51void unregisterChannel(Channel *channel)
53 auto &channels = liveChannels();
54 channels.erase(std::remove(channels.begin(), channels.end(), channel), channels.end());
57ssize_t send_some(
int fd,
const void *buf, std::size_t
n)
60 return ::send(fd, buf,
n, MSG_NOSIGNAL);
62 return ::send(fd, buf,
n, 0);
70 int flags = fcntl(
fd_, F_GETFL, 0);
71 if (flags == -1 || fcntl(
fd_, F_SETFL, flags | O_NONBLOCK) == -1) {
72 throw std::runtime_error(std::string(
"MultiProcess::Channel: could not set O_NONBLOCK: ") + strerror(errno));
77 setsockopt(
fd_, SOL_SOCKET, SO_NOSIGPIPE, &optval,
sizeof(optval));
79 registerChannel(
this);
85 unregisterChannel(
this);
92 out_buf_(std::move(other.out_buf_)),
93 out_pos_(other.out_pos_),
94 in_header_(other.in_header_),
95 in_header_bytes_(other.in_header_bytes_),
96 in_have_header_(other.in_have_header_),
97 in_msg_(std::move(other.in_msg_)),
98 in_msg_bytes_(other.in_msg_bytes_)
102 unregisterChannel(&other);
103 registerChannel(
this);
109 if (
this != &other) {
111 unregisterChannel(
this);
115 out_buf_ = std::move(other.out_buf_);
116 out_pos_ = other.out_pos_;
117 in_header_ = other.in_header_;
118 in_header_bytes_ = other.in_header_bytes_;
119 in_have_header_ = other.in_have_header_;
120 in_msg_ = std::move(other.in_msg_);
121 in_msg_bytes_ = other.in_msg_bytes_;
124 unregisterChannel(&other);
125 registerChannel(
this);
148 pollfd pfd{wake_fd, POLLIN, 0};
149 ::poll(&pfd, 1, 500);
153 throw ppoll_error_t(EINTR,
"MultiProcess::Channel: connection closed while terminating");
155 throw std::runtime_error(
"MultiProcess::Channel: connection closed by peer process (did it die unexpectedly?)");
160 std::uint64_t header = (std::uint64_t(
size) & sizeMask) | (more ? moreBit : 0);
168 const char *headerBytes =
reinterpret_cast<const char *
>(&header);
170 const char *dataBytes =
static_cast<const char *
>(
data);
183 }
else if (errno == EINTR) {
185 }
else if (errno == EAGAIN || errno == EWOULDBLOCK) {
187 }
else if (errno == EPIPE || errno == ECONNRESET) {
190 throw std::runtime_error(std::string(
"MultiProcess::Channel: send failed: ") + strerror(errno));
201 char *headerBytes =
reinterpret_cast<char *
>(&
in_header_);
208 }
else if (errno == EINTR) {
210 }
else if (errno == EAGAIN || errno == EWOULDBLOCK) {
212 }
else if (errno == ECONNRESET) {
215 throw std::runtime_error(std::string(
"MultiProcess::Channel: receive failed: ") + strerror(errno));
232 }
else if (errno == EINTR) {
234 }
else if (errno == EAGAIN || errno == EWOULDBLOCK) {
236 }
else if (errno == ECONNRESET) {
239 throw std::runtime_error(std::string(
"MultiProcess::Channel: receive failed: ") + strerror(errno));
264std::vector<std::size_t>
Channel::wait(
const std::vector<const Channel *> &read_channels,
int timeout_ms)
267 std::vector<pollfd> pollfds;
268 pollfds.reserve(read_channels.size() + liveChannels().
size() + 1);
272 pollfds.push_back({wake_fd, POLLIN, 0});
274 const std::size_t first_read_item = pollfds.size();
275 for (
const Channel *channel : read_channels) {
276 pollfds.push_back({channel->fd(), POLLIN, 0});
281 std::vector<Channel *> flush_channels;
282 for (
Channel *channel : liveChannels()) {
283 if (channel->has_pending_output()) {
284 flush_channels.push_back(channel);
285 pollfds.push_back({channel->fd(), POLLOUT, 0});
289 int rc = ::poll(pollfds.data(), pollfds.size(), timeout_ms);
291 if (errno == EINTR) {
306 throw std::runtime_error(std::string(
"MultiProcess::Channel::wait: poll failed: ") + strerror(errno));
311 if (wake_fd >= 0 && (pollfds[0].revents & POLLIN)) {
315 for (std::size_t fx = 0; fx < flush_channels.size(); ++fx) {
316 std::size_t item = first_read_item + read_channels.size() + fx;
317 if (pollfds[item].revents & (POLLOUT | POLLERR | POLLHUP)) {
318 flush_channels[fx]->try_flush();
322 std::vector<std::size_t> readable;
323 for (std::size_t ix = 0; ix < read_channels.size(); ++ix) {
324 if (pollfds[first_read_item + ix].revents & (POLLIN | POLLHUP | POLLERR)) {
325 readable.push_back(ix);
328 if (!readable.empty() || timeout_ms >= 0) {
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
std::string Message(const std::string &msg, const std::string &location)
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
One endpoint of a full-duplex interprocess message pipe.
std::size_t in_header_bytes_
void send_frame(const void *data, std::size_t size, bool more)
Queue one frame for sending and write out as much as the socket accepts.
static void throw_connection_closed()
Handle end-of-stream / closed-connection conditions; never returns.
bool try_recv_frame(Message &msg, bool *more)
Non-blocking receive attempt.
std::size_t in_msg_bytes_
static std::vector< std::size_t > wait(const std::vector< const Channel * > &read_channels, int timeout_ms)
Wait until at least one of read_channels has input available, flushing the pending output of all live...
Channel & operator=(const Channel &)=delete
bool try_flush()
Write out pending output; returns true when all of it has been written.
Message recv_frame(bool *more=nullptr)
Blocking receive of one complete frame, interruptible by SIGTERM (throws ppoll_error_t,...
std::vector< char > out_buf_
A contiguous byte buffer used as the unit of interprocess communication.
std::size_t size() const
Size of the message in bytes.
static bool sigterm_received()
static int sigterm_wake_fd()
Read end of the self-pipe that the SIGTERM handler writes to (or -1 on the master process,...
Thrown when a blocking wait on a Channel is interrupted, e.g.
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...