void Subprocess::communicate(FdCallback readCallback, FdCallback writeCallback) { returnCode_.enforce(ProcessReturnCode::RUNNING); setAllNonBlocking(); std::vector<pollfd> fds; fds.reserve(pipes_.size()); std::vector<int> toClose; toClose.reserve(pipes_.size()); while (!pipes_.empty()) { fds.clear(); toClose.clear(); for (auto& p : pipes_) { pollfd pfd; pfd.fd = p.parentFd; // Yes, backwards, PIPE_IN / PIPE_OUT are defined from the // child's point of view. if (!p.enabled) { // Still keeping fd in watched set so we get notified of POLLHUP / // POLLERR pfd.events = 0; } else if (p.direction == PIPE_IN) { pfd.events = POLLOUT; } else { pfd.events = POLLIN; } fds.push_back(pfd); } int r; do { r = ::poll(fds.data(), fds.size(), -1); } while (r == -1 && errno == EINTR); checkUnixError(r, "poll"); for (int i = 0; i < pipes_.size(); ++i) { auto& p = pipes_[i]; DCHECK_EQ(fds[i].fd, p.parentFd); short events = fds[i].revents; bool closed = false; if (events & POLLOUT) { DCHECK(!(events & POLLIN)); if (writeCallback(p.parentFd, p.childFd)) { toClose.push_back(i); closed = true; } } if (events & POLLIN) { DCHECK(!(events & POLLOUT)); if (readCallback(p.parentFd, p.childFd)) { toClose.push_back(i); closed = true; } } if ((events & (POLLHUP | POLLERR)) && !closed) { toClose.push_back(i); closed = true; } } // Close the fds in reverse order so the indexes hold after erase() for (int idx : boost::adaptors::reverse(toClose)) { auto pos = pipes_.begin() + idx; closeChecked(pos->parentFd); pipes_.erase(pos); } } }
void Subprocess::communicate(FdCallback readCallback, FdCallback writeCallback) { // This serves to prevent wait() followed by communicate(), but if you // legitimately need that, send a patch to delete this line. returnCode_.enforce(ProcessReturnCode::RUNNING); setAllNonBlocking(); std::vector<pollfd> fds; fds.reserve(pipes_.size()); std::vector<size_t> toClose; // indexes into pipes_ toClose.reserve(pipes_.size()); while (!pipes_.empty()) { fds.clear(); toClose.clear(); for (auto& p : pipes_) { pollfd pfd; pfd.fd = p.pipe.fd(); // Yes, backwards, PIPE_IN / PIPE_OUT are defined from the // child's point of view. if (!p.enabled) { // Still keeping fd in watched set so we get notified of POLLHUP / // POLLERR pfd.events = 0; } else if (p.direction == PIPE_IN) { pfd.events = POLLOUT; } else { pfd.events = POLLIN; } fds.push_back(pfd); } int r; do { r = ::poll(fds.data(), fds.size(), -1); } while (r == -1 && errno == EINTR); checkUnixError(r, "poll"); for (size_t i = 0; i < pipes_.size(); ++i) { auto& p = pipes_[i]; auto parentFd = p.pipe.fd(); DCHECK_EQ(fds[i].fd, parentFd); short events = fds[i].revents; bool closed = false; if (events & POLLOUT) { DCHECK(!(events & POLLIN)); if (writeCallback(parentFd, p.childFd)) { toClose.push_back(i); closed = true; } } // Call read callback on POLLHUP, to give it a chance to read (and act // on) end of file if (events & (POLLIN | POLLHUP)) { DCHECK(!(events & POLLOUT)); if (readCallback(parentFd, p.childFd)) { toClose.push_back(i); closed = true; } } if ((events & (POLLHUP | POLLERR)) && !closed) { toClose.push_back(i); closed = true; } } // Close the fds in reverse order so the indexes hold after erase() for (int idx : boost::adaptors::reverse(toClose)) { auto pos = pipes_.begin() + idx; pos->pipe.close(); // Throws on error pipes_.erase(pos); } } }