os: fixup process possible deadlock

Sloppy, but should work... Process shouldn't be used in performance-critical paths anyways.

ref https://github.com/hyprwm/Hyprland/issues/8425
This commit is contained in:
Vaxry 2024-11-11 22:12:39 +00:00
parent d504d45114
commit 8d21d1dfa9

View file

@ -6,7 +6,9 @@ using namespace Hyprutils::OS;
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <cstring>
#include <array> #include <array>
#include <thread>
#include <sys/fcntl.h>
#include <sys/wait.h> #include <sys/wait.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_) {
@ -57,27 +59,51 @@ bool Hyprutils::OS::CProcess::runSync() {
close(outPipe[1]); close(outPipe[1]);
close(errPipe[1]); close(errPipe[1]);
waitpid(pid, nullptr, 0); out = "";
err = "";
std::string readOutData;
std::array<char, 1024> buf; std::array<char, 1024> buf;
buf.fill(0); buf.fill(0);
// wait for read // wait for read
size_t ret = 0; ssize_t ret = 0;
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
readOutData += std::string{(char*)buf.data(), ret}; int fdFlags = fcntl(outPipe[0], F_GETFL, 0);
if (fcntl(outPipe[0], F_SETFL, fdFlags | O_NONBLOCK) < 0)
return false;
fdFlags = fcntl(errPipe[0], F_GETFL, 0);
if (fcntl(errPipe[0], F_SETFL, fdFlags | O_NONBLOCK) < 0)
return false;
// FIXME: this sucks, but it prevents a pipe deadlock.
// 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.
// If nothing is read from either stdout or stderr, sleep for 100µs, to maybe not peg a core THAT much.
// If anyone knows a better solution, feel free to make a MR.
while (waitpid(pid, nullptr, WNOHANG) == 0) {
int any = 0;
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
out += std::string{(char*)buf.data(), (size_t)ret};
}
any += errno == EWOULDBLOCK || errno == EAGAIN ? 1 : 0;
buf.fill(0);
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
err += std::string{(char*)buf.data(), (size_t)ret};
}
any += errno == EWOULDBLOCK || errno == EAGAIN ? 1 : 0;
buf.fill(0);
if (any >= 2)
std::this_thread::sleep_for(std::chrono::microseconds(100));
} }
out = readOutData;
readOutData = "";
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
readOutData += std::string{(char*)buf.data(), ret};
}
err = readOutData;
close(outPipe[0]); close(outPipe[0]);
close(errPipe[0]); close(errPipe[0]);