From 8d21d1dfa91c9a2e7e65418c12eb3f913cc609b9 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 11 Nov 2024 22:12:39 +0000 Subject: [PATCH] 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 --- src/os/Process.cpp | 50 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/os/Process.cpp b/src/os/Process.cpp index 2cf00ea..58b597c 100644 --- a/src/os/Process.cpp +++ b/src/os/Process.cpp @@ -6,7 +6,9 @@ using namespace Hyprutils::OS; #include #include #include +#include +#include #include Hyprutils::OS::CProcess::CProcess(const std::string& binary_, const std::vector& args_) : binary(binary_), args(args_) { @@ -57,26 +59,50 @@ bool Hyprutils::OS::CProcess::runSync() { close(outPipe[1]); close(errPipe[1]); - waitpid(pid, nullptr, 0); + out = ""; + err = ""; - std::string readOutData; std::array buf; buf.fill(0); // wait for read - size_t ret = 0; - while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) { - readOutData += std::string{(char*)buf.data(), ret}; - } + ssize_t ret = 0; + + 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}; + } - out = readOutData; - readOutData = ""; + any += errno == EWOULDBLOCK || errno == EAGAIN ? 1 : 0; - while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) { - readOutData += std::string{(char*)buf.data(), ret}; - } + 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; - err = readOutData; + buf.fill(0); + + if (any >= 2) + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } close(outPipe[0]); close(errPipe[0]);