Skip to content

Commit

Permalink
os: fixup process possible deadlock
Browse files Browse the repository at this point in the history
Sloppy, but should work... Process shouldn't be used in performance-critical paths anyways.

ref hyprwm/Hyprland#8425
  • Loading branch information
vaxerski committed Nov 11, 2024
1 parent d504d45 commit 8d21d1d
Showing 1 changed file with 38 additions and 12 deletions.
50 changes: 38 additions & 12 deletions src/os/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ using namespace Hyprutils::OS;
#include <unistd.h>
#include <cstring>
#include <array>
#include <thread>

#include <sys/fcntl.h>
#include <sys/wait.h>

Hyprutils::OS::CProcess::CProcess(const std::string& binary_, const std::vector<std::string>& args_) : binary(binary_), args(args_) {
Expand Down Expand Up @@ -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<char, 1024> 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]);
Expand Down

0 comments on commit 8d21d1d

Please sign in to comment.