mirror of
https://github.com/audacity/linuxdeploy.git
synced 2025-12-13 04:48:55 -06:00
Make sure to read until EOF from subprocesses
This commit is contained in:
parent
0e89061417
commit
11ca1efddb
@ -43,6 +43,7 @@ namespace linuxdeploy {
|
|||||||
std::string stream_name_;
|
std::string stream_name_;
|
||||||
ldLog log_;
|
ldLog log_;
|
||||||
bool print_prefix_in_next_iteration_;
|
bool print_prefix_in_next_iteration_;
|
||||||
|
bool eof = false;
|
||||||
|
|
||||||
pipe_to_be_logged(int pipe_fd, std::string stream_name) : reader_(pipe_fd),
|
pipe_to_be_logged(int pipe_fd, std::string stream_name) : reader_(pipe_fd),
|
||||||
stream_name_(std::move(stream_name)),
|
stream_name_(std::move(stream_name)),
|
||||||
@ -62,12 +63,17 @@ namespace linuxdeploy {
|
|||||||
// since we have our own ldLog instance for every pipe, we can get away with this rather small read buffer
|
// since we have our own ldLog instance for every pipe, we can get away with this rather small read buffer
|
||||||
subprocess::subprocess_result_buffer_t intermediate_buffer(4096);
|
subprocess::subprocess_result_buffer_t intermediate_buffer(4096);
|
||||||
|
|
||||||
|
if (pipe_to_be_logged.eof) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// (try to) read from pipe
|
// (try to) read from pipe
|
||||||
const auto bytes_read = pipe_to_be_logged.reader_.read(intermediate_buffer);
|
const auto bytes_read = pipe_to_be_logged.reader_.read(intermediate_buffer);
|
||||||
|
|
||||||
// no action required in case we have not read anything from the pipe
|
// 0 means EOF
|
||||||
if (bytes_read <= 0) {
|
if (bytes_read == 0) {
|
||||||
continue;
|
pipe_to_be_logged.eof = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we just trim the buffer to the bytes we read (makes the code below easier)
|
// we just trim the buffer to the bytes we read (makes the code below easier)
|
||||||
@ -119,7 +125,12 @@ namespace linuxdeploy {
|
|||||||
if (proc.is_running()) {
|
if (proc.is_running()) {
|
||||||
// reduce load on CPU
|
// reduce load on CPU
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// once all buffers are EOF, we can stop reading
|
||||||
|
if (std::all_of(pipes_to_be_logged.begin(), pipes_to_be_logged.end(), [](const pipe_to_be_logged& pipe_state) {
|
||||||
|
return pipe_state.eof;
|
||||||
|
})) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,16 +17,21 @@ pipe_reader::pipe_reader(int pipe_fd) : pipe_fd_(pipe_fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t pipe_reader::read(std::vector<std::string::value_type>& buffer) const {
|
size_t pipe_reader::read(std::vector<std::string::value_type>& buffer) const {
|
||||||
ssize_t rv = ::read(pipe_fd_, buffer.data(), buffer.size());
|
for (;;) {
|
||||||
|
ssize_t rv = ::read(pipe_fd_, buffer.data(), buffer.size());
|
||||||
|
|
||||||
if (rv == -1) {
|
if (rv == -1) {
|
||||||
// no data available
|
switch (errno) {
|
||||||
if (errno == EAGAIN)
|
// retry in case data is currently not available
|
||||||
return 0;
|
case EINTR:
|
||||||
|
case EAGAIN:
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
// TODO: introduce custom subprocess_error
|
||||||
|
throw std::runtime_error{"unexpected error reading from pipe: " + std::string(strerror(errno))};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: introduce custom subprocess_error
|
return rv;
|
||||||
throw std::runtime_error{"unexpected error reading from pipe: " + std::string(strerror(errno))};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,55 +27,70 @@ namespace linuxdeploy {
|
|||||||
subprocess_result subprocess::run() const {
|
subprocess_result subprocess::run() const {
|
||||||
process proc{args_, env_};
|
process proc{args_, env_};
|
||||||
|
|
||||||
|
class PipeState {
|
||||||
|
public:
|
||||||
|
pipe_reader reader;
|
||||||
|
subprocess_result_buffer_t buffer;
|
||||||
|
bool eof = false;
|
||||||
|
|
||||||
|
explicit PipeState(int fd) : reader(fd) {}
|
||||||
|
};
|
||||||
|
|
||||||
// create pipe readers and empty buffers for both stdout and stderr
|
// create pipe readers and empty buffers for both stdout and stderr
|
||||||
// we manage them in this (admittedly, kind of complex-looking) array so we can later easily perform the
|
// we manage them in this (admittedly, kind of complex-looking) array so we can later easily perform the
|
||||||
// operations in a loop
|
// operations in a loop
|
||||||
std::array<std::pair<pipe_reader, subprocess_result_buffer_t>, 2> buffers{
|
std::array<PipeState, 2> buffers = {
|
||||||
std::make_pair(pipe_reader(proc.stdout_fd()), subprocess_result_buffer_t{}),
|
PipeState(proc.stdout_fd()),
|
||||||
std::make_pair(pipe_reader(proc.stderr_fd()), subprocess_result_buffer_t{}),
|
PipeState(proc.stderr_fd())
|
||||||
};
|
};
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
for (auto& pair : buffers) {
|
for (auto& pipe_state : buffers) {
|
||||||
// make code more readable
|
|
||||||
auto& reader = pair.first;
|
|
||||||
auto& buffer = pair.second;
|
|
||||||
|
|
||||||
// read some bytes into smaller intermediate buffer to prevent either of the pipes to overflow
|
// read some bytes into smaller intermediate buffer to prevent either of the pipes to overflow
|
||||||
// the results are immediately appended to the main buffer
|
// the results are immediately appended to the main buffer
|
||||||
subprocess_result_buffer_t intermediate_buffer(4096);
|
subprocess_result_buffer_t intermediate_buffer(4096);
|
||||||
|
|
||||||
// (try to) read all available data from pipe
|
// (try to) read all available data from pipe
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const auto bytes_read = reader.read(intermediate_buffer);
|
if (pipe_state.eof) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto bytes_read = pipe_state.reader.read(intermediate_buffer);
|
||||||
|
|
||||||
|
// 0 means EOF
|
||||||
if (bytes_read == 0) {
|
if (bytes_read == 0) {
|
||||||
|
pipe_state.eof = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append to main buffer
|
// append to main buffer
|
||||||
buffer.reserve(buffer.size() + bytes_read);
|
pipe_state.buffer.reserve(pipe_state.buffer.size() + bytes_read);
|
||||||
std::copy(intermediate_buffer.begin(), (intermediate_buffer.begin() + bytes_read),
|
std::copy(intermediate_buffer.begin(), (intermediate_buffer.begin() + bytes_read),
|
||||||
std::back_inserter(buffer));
|
std::back_inserter(pipe_state.buffer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do-while might be a little more elegant, but we can save this one unnecessary sleep, so...
|
|
||||||
if (proc.is_running()) {
|
if (proc.is_running()) {
|
||||||
// reduce load on CPU
|
// reduce load on CPU until EOF
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// once all buffers are EOF, we can stop reading
|
||||||
|
if (std::all_of(buffers.begin(), buffers.end(), [](const PipeState& pipe_state) {
|
||||||
|
return pipe_state.eof;
|
||||||
|
})) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure contents are null-terminated
|
// make sure contents are null-terminated
|
||||||
buffers[0].second.emplace_back('\0');
|
buffers[0].buffer.emplace_back('\0');
|
||||||
buffers[1].second.emplace_back('\0');
|
buffers[1].buffer.emplace_back('\0');
|
||||||
|
|
||||||
auto exit_code = proc.close();
|
auto exit_code = proc.close();
|
||||||
|
|
||||||
return subprocess_result{exit_code, buffers[0].second, buffers[1].second};
|
return subprocess_result{exit_code, buffers[0].buffer, buffers[1].buffer};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string subprocess::check_output() const {
|
std::string subprocess::check_output() const {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user