diff --git a/pcsx2/Linux/LnxConsolePipe.cpp b/pcsx2/Linux/LnxConsolePipe.cpp index 30b8abd..8b7b130 100644 --- a/pcsx2/Linux/LnxConsolePipe.cpp +++ b/pcsx2/Linux/LnxConsolePipe.cpp @@ -17,6 +17,7 @@ #include "App.h" #include "ConsoleLogger.h" +#include #include using namespace Threading; @@ -27,38 +28,91 @@ class LinuxPipeThread : public pxThread typedef pxThread _parent; protected: - const int& m_read_fd; + FILE* m_stdstream; + FILE* m_fp; const ConsoleColors m_color; + int m_pipe_fd[2]; + std::atomic_bool m_exec_finished; void ExecuteTaskInThread(); public: - LinuxPipeThread(const int& read_fd, ConsoleColors color); + LinuxPipeThread(FILE* stdstream); virtual ~LinuxPipeThread() noexcept; + }; -LinuxPipeThread::LinuxPipeThread(const int& read_fd, ConsoleColors color) - : pxThread(color == Color_Red ? L"Redirect_Stderr" : L"Redirect_Stdout") - , m_read_fd(read_fd) - , m_color(color) +LinuxPipeThread::LinuxPipeThread(FILE* stdstream) + : pxThread(stdstream == stderr? L"Redirect_Stderr" : L"Redirect_Stdout") + , m_stdstream(stdstream) + , m_fp(nullptr) + , m_color(stdstream == stderr? Color_Red : Color_Black) + , m_pipe_fd{-1, -1} + , m_exec_finished(false) { + const wxChar* stream_name = stdstream == stderr? L"stderr" : L"stdout"; + + int stdstream_fd = fileno(stdstream); + + // Save the original stdout/stderr file descriptor... + int dup_fd = dup(stdstream_fd); + if (dup_fd == -1) + throw Exception::RuntimeError().SetDiagMsg(wxString::Format( + L"Redirect %s failed: dup: %s", stream_name, strerror(errno))); + m_fp = fdopen(dup_fd, "w"); + if (m_fp == nullptr) { + close(dup_fd); + throw Exception::RuntimeError().SetDiagMsg(wxString::Format( + L"Redirect %s failed: fdopen: %s", stream_name, strerror(errno))); + } + if (pipe(m_pipe_fd) == -1) { + fclose(m_fp); + throw Exception::RuntimeError().SetDiagMsg(wxString::Format( + L"Redirect %s failed: pipe: %s", stream_name, strerror(errno))); + } + } LinuxPipeThread::~LinuxPipeThread() noexcept { - _parent::Cancel(); + dup2(fileno(m_fp), m_pipe_fd[1]); + + if (m_stdstream == stdout) + Console_SetStdout(stdout); + + fclose(m_fp); + + struct timespec sleep_interval = {.tv_sec = 0, .tv_nsec = 100000}; + while (m_exec_finished.load() != true) { + nanosleep(&sleep_interval, nullptr); + } } void LinuxPipeThread::ExecuteTaskInThread() { + const wxChar* stream_name = m_stdstream == stderr? L"stderr" : L"stdout"; + char buffer[2049]; - while (ssize_t bytes_read = read(m_read_fd, buffer, sizeof(buffer) - 1)) { - TestCancel(); + // Redirect stdout/stderr + int stdstream_fd = fileno(m_stdstream); + if (dup2(m_pipe_fd[1], stdstream_fd) != stdstream_fd) { + Console.Error(wxString::Format(L"Redirect %s failed: dup2: %s", + stream_name, strerror(errno))); + return; + } + close(m_pipe_fd[1]); + m_pipe_fd[1] = stdstream_fd; + + // And send the final console output to the original stdout, + // otherwise we'll have an infinite data loop. + if (m_stdstream == stdout) + Console_SetStdout(m_fp); + + while (ssize_t bytes_read = read(m_pipe_fd[0], buffer, sizeof(buffer) - 1)) { if (bytes_read == -1) { if (errno == EINTR) { continue; - } - else { + } else { // Should never happen. Console.Error(wxString::Format(L"Redirect %s failed: read: %s", m_color == Color_Red ? L"stderr" : L"stdout", strerror(errno))); @@ -72,9 +126,9 @@ void LinuxPipeThread::ExecuteTaskInThread() ConsoleColorScope cs(m_color); Console.WriteRaw(fromUTF8(buffer)); } - - TestCancel(); } + close(m_pipe_fd[0]); + m_exec_finished.store(true); } // Redirects data sent to stdout/stderr into a pipe, and sets up a thread to @@ -85,11 +139,8 @@ class LinuxPipeRedirection : public PipeRedirectionBase protected: FILE* m_stdstream; - FILE* m_fp; - int m_pipe_fd[2]; LinuxPipeThread m_thread; - void Cleanup() noexcept; public: LinuxPipeRedirection(FILE* stdstream); virtual ~LinuxPipeRedirection() noexcept; @@ -97,86 +148,21 @@ public: LinuxPipeRedirection::LinuxPipeRedirection(FILE* stdstream) : m_stdstream(stdstream) - , m_fp(nullptr) - , m_pipe_fd{-1, -1} - , m_thread(m_pipe_fd[0], stdstream == stderr ? Color_Red : Color_Black) + , m_thread(stdstream) { pxAssert((stdstream == stderr) || (stdstream == stdout)); - const wxChar* stream_name = stdstream == stderr? L"stderr" : L"stdout"; - try { - int stdstream_fd = fileno(stdstream); - - // Save the original stdout/stderr file descriptor... - int dup_fd = dup(stdstream_fd); - if (dup_fd == -1) - throw Exception::RuntimeError().SetDiagMsg(wxString::Format( - L"Redirect %s failed: dup: %s", stream_name, strerror(errno))); - m_fp = fdopen(dup_fd, "w"); - if (m_fp == nullptr) - throw Exception::RuntimeError().SetDiagMsg(wxString::Format( - L"Redirect %s failed: fdopen: %s", stream_name, strerror(errno))); - - // and now redirect stdout/stderr. - if (pipe(m_pipe_fd) == -1) - throw Exception::RuntimeError().SetDiagMsg(wxString::Format( - L"Redirect %s failed: pipe: %s", stream_name, strerror(errno))); - if (dup2(m_pipe_fd[1], stdstream_fd) != stdstream_fd) - throw Exception::RuntimeError().SetDiagMsg(wxString::Format( - L"Redirect %s failed: dup2: %s", stream_name, strerror(errno))); - close(m_pipe_fd[1]); - m_pipe_fd[1] = stdstream_fd; - - // And send the final console output goes to the original stdout, - // otherwise we'll have an infinite data loop. - if (stdstream == stdout) - Console_SetStdout(m_fp); - m_thread.Start(); } catch (Exception::BaseThreadError& ex) { // thread object will become invalid because of scoping after we leave // the constructor, so re-pack a new exception: - Cleanup(); throw Exception::RuntimeError().SetDiagMsg(ex.FormatDiagnosticMessage()); - } catch (...) { - Cleanup(); - throw; } } LinuxPipeRedirection::~LinuxPipeRedirection() noexcept { - Cleanup(); -} - -void LinuxPipeRedirection::Cleanup() noexcept -{ - // Restore stdout/stderr file descriptor - mostly useful if the thread - // fails to start, but then you have bigger issues to worry about. - if (m_pipe_fd[1] != -1) { - if (m_pipe_fd[1] == fileno(m_stdstream)) { - // FIXME: Use lock for better termination. - // The redirect thread is most likely waiting at read(). Send a - // newline so it returns and the thread begins to terminate. - fflush(m_stdstream); - m_thread.Cancel(); - fputc('\n', m_stdstream); - fflush(m_stdstream); - dup2(fileno(m_fp), fileno(m_stdstream)); - } else { - close(m_pipe_fd[1]); - } - } - - if (m_fp != nullptr) { - if (m_stdstream == stdout) - Console_SetStdout(stdout); - fclose(m_fp); - } - - if (m_pipe_fd[0] != -1) - close(m_pipe_fd[0]); } PipeRedirectionBase* NewPipeRedir(FILE* stdstream)