From 15044b2583c893f0ae1fd898d0b081c63d4700be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Slavi=CC=81k?= Date: Fri, 21 Feb 2025 18:10:11 +0100 Subject: [PATCH] Read subprocess pipes in background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Async use of wxExecute requires that user code periodically checks the streams and reads their content. This is particularly important on platforms with tiny buffers, such as… Windows. --- src/subprocess.cpp | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/subprocess.cpp b/src/subprocess.cpp index e4928c389f..9591576be7 100644 --- a/src/subprocess.cpp +++ b/src/subprocess.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -44,34 +45,52 @@ namespace class Process : public wxProcess { public: - Process() = default; + Process() : m_timer(this) + { + } + + void watch_pipes() + { + Bind(wxEVT_TIMER, &Process::on_timer, this); + m_timer.Start(100/*ms*/); + } subprocess::Output make_output(int status) { - return {status, extract_stream(GetInputStream()), extract_stream(GetErrorStream())}; + read_available_output(); + return {status, m_stdout.str(), m_stderr.str()}; } protected: - std::string extract_stream(wxInputStream *s) + void read_available_output(std::ostringstream& oss, wxInputStream *s) { if (!s || !s->CanRead()) - return std::string(); + return; - // the stream could be already at EOF or in wxSTREAM_BROKEN_PIPE state - s->Reset(); - std::ostringstream oss; char buffer[4096]; while (true) { s->Read(buffer, sizeof(buffer)); size_t bytesRead = s->LastRead(); if (!bytesRead) - break; // EOF + break; // EOF or temporarily no more data oss.write(buffer, bytesRead); } + } - return oss.str(); + void read_available_output() + { + read_available_output(m_stdout, GetInputStream()); + read_available_output(m_stderr, GetErrorStream()); + } + + void on_timer(wxTimerEvent&) + { + read_available_output(); } + + wxTimer m_timer; + std::ostringstream m_stdout, m_stderr; }; @@ -197,6 +216,8 @@ dispatch::future Runner::do_run_async(Arguments&& argv) wxLogTrace("poedit.execute", " failed to launch child process(%d): %s", (int)retval, argv.pretty_print()); BOOST_THROW_EXCEPTION(Exception(wxString::Format(_("Cannot execute program: %s"), argv.pretty_print()))); } + + process->watch_pipes(); } catch (...) {