process: better reading for spawnSync

This commit is contained in:
Vaxry 2024-11-11 22:33:38 +00:00
parent 8d21d1dfa9
commit 4c5f18d06b

View file

@ -10,6 +10,7 @@ using namespace Hyprutils::OS;
#include <sys/fcntl.h> #include <sys/fcntl.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/poll.h>
Hyprutils::OS::CProcess::CProcess(const std::string& binary_, const std::vector<std::string>& args_) : binary(binary_), args(args_) { Hyprutils::OS::CProcess::CProcess(const std::string& binary_, const std::vector<std::string>& args_) : binary(binary_), args(args_) {
; ;
@ -75,35 +76,69 @@ bool Hyprutils::OS::CProcess::runSync() {
if (fcntl(errPipe[0], F_SETFL, fdFlags | O_NONBLOCK) < 0) if (fcntl(errPipe[0], F_SETFL, fdFlags | O_NONBLOCK) < 0)
return false; return false;
// FIXME: this sucks, but it prevents a pipe deadlock. pollfd pollfds[2] = {
// Problem is, if we exceed the 64k buffer, we end up in a deadlock. {
// So, as a "solution", we keep reading until the child pid exits. .fd = outPipe[0],
// If nothing is read from either stdout or stderr, sleep for 100µs, to maybe not peg a core THAT much. .events = POLLIN,
// If anyone knows a better solution, feel free to make a MR. },
{
.fd = errPipe[0],
.events = POLLIN,
},
};
while (waitpid(pid, nullptr, WNOHANG) == 0) { while (1337) {
int any = 0; int ret = poll(pollfds, 2, 5000);
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) { if (ret < 0) {
out += std::string{(char*)buf.data(), (size_t)ret}; if (errno == EINTR)
continue;
return false;
} }
any += errno == EWOULDBLOCK || errno == EAGAIN ? 1 : 0; bool hupd = false;
for (size_t i = 0; i < 2; ++i) {
if (pollfds[i].revents & POLLHUP) {
hupd = true;
break;
}
}
if (hupd)
break;
if (pollfds[0].revents & POLLIN) {
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
out += std::string_view{(char*)buf.data(), (size_t)ret};
}
buf.fill(0);
}
if (pollfds[1].revents & POLLIN) {
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
err += std::string_view{(char*)buf.data(), (size_t)ret};
}
buf.fill(0);
}
}
// Final reads. Nonblock, so its ok.
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
out += std::string_view{(char*)buf.data(), (size_t)ret};
}
buf.fill(0); buf.fill(0);
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) { while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
err += std::string{(char*)buf.data(), (size_t)ret}; err += std::string_view{(char*)buf.data(), (size_t)ret};
} }
any += errno == EWOULDBLOCK || errno == EAGAIN ? 1 : 0;
buf.fill(0); buf.fill(0);
if (any >= 2)
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
close(outPipe[0]); close(outPipe[0]);
close(errPipe[0]); close(errPipe[0]);