Re-implement the mux dialog's reading from mkvmerge without threads

Fixes #774.
This commit is contained in:
Moritz Bunkus 2012-07-11 17:03:51 +02:00
parent b83edc91a4
commit dc8b669ed7
2 changed files with 71 additions and 134 deletions

View File

@ -18,13 +18,11 @@
#include <wx/file.h>
#include <wx/confbase.h>
#include <wx/fileconf.h>
#include <wx/notebook.h>
#include <wx/listctrl.h>
#include <wx/statusbr.h>
#include <wx/statline.h>
#include <wx/utils.h>
#include "common/at_scope_exit.h"
#include "common/fs_sys_helpers.h"
#include "common/strings/editing.h"
#include "common/strings/formatting.h"
@ -34,7 +32,6 @@
mux_dialog::mux_dialog(wxWindow *parent)
: wxDialog(parent, -1, Z("mkvmerge is running"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE)
, m_thread{nullptr}
, m_process{nullptr}
, m_pid{0}
#if defined(SYS_WINDOWS)
@ -137,20 +134,21 @@ mux_dialog::run() {
m_pid = wxExecute(wxString::Format(wxT("\"%s\" \"@%s\""), arg_list[0].c_str(), opt_file_name.c_str()), wxEXEC_ASYNC, m_process);
if (0 == m_pid) {
wxCommandEvent evt(mux_thread::event, mux_thread::process_terminated);
wxCommandEvent evt(mux_process::event, mux_process::process_terminated);
evt.SetInt(2);
wxPostEvent(this, evt);
} else {
m_thread = new mux_thread{this, m_process};
m_thread->Create();
m_thread->Run();
m_read_input_timer.SetOwner(this, ID_T_READ_INPUT);
m_read_input_timer.Start(100);
}
ShowModal();
}
mux_dialog::~mux_dialog() {
m_read_input_timer.Stop();
#if defined(SYS_WINDOWS)
if (m_taskbar_progress)
m_taskbar_progress->set_state(TBPF_NOPROGRESS);
@ -159,11 +157,8 @@ mux_dialog::~mux_dialog() {
wxRemoveFile(opt_file_name);
if (m_process) {
wxCriticalSectionLocker locker{m_process->m_cs_dialog};
m_process->m_dialog = nullptr;
m_process->Detach();
}
if (m_process)
m_process->detach_from_dialog();
}
void
@ -298,6 +293,8 @@ mux_dialog::on_output_available(wxCommandEvent &evt) {
void
mux_dialog::on_process_terminated(wxCommandEvent &evt) {
m_read_input_timer.Stop();
m_exit_code = evt.GetInt();
wxString format;
@ -334,104 +331,32 @@ mux_dialog::on_process_terminated(wxCommandEvent &evt) {
b_abort->Enable(false);
#endif // SYS_WINDOWS
wxCriticalSectionLocker thread_locker{m_cs_thread};
if (m_thread) {
wxCriticalSectionLocker process_locker{m_thread->m_cs_process};
m_thread->m_out = nullptr;
}
delete m_process;
m_process = nullptr;
}
void
mux_dialog::add_input(std::string const &input) {
auto lines = split(m_available_input + input, boost::regex("\r|\n"));
m_available_input = lines.back();
lines.pop_back();
for (auto const &line : lines) {
wxCommandEvent evt(mux_process::event, mux_process::output_available);
evt.SetString(wxU(line));
wxPostEvent(this, evt);
}
}
void
mux_dialog::on_read_input(wxTimerEvent &) {
if (m_process)
m_process->process_input();
}
// ------------------------------------------------------------
wxEventType const mux_thread::event = wxNewEventType();
mux_thread::mux_thread(mux_dialog *dialog,
wxProcess *process)
: m_dialog{dialog}
, m_process{process}
, m_out{nullptr}
{
}
void *
mux_thread::Entry() {
at_scope_exit_c unlink_thread{[&]() {
if (!m_dialog)
return;
wxCriticalSectionLocker locker{m_dialog->m_cs_thread};
m_dialog->m_thread = nullptr;
}};
m_out = m_process->GetInputStream();
std::string line;
while (1) {
char c = 0;
bool eof = false;
bool char_read = false;
while (!char_read && !eof) {
if (TestDestroy())
return nullptr;
char_read = read_input(c, eof);
if (!char_read && !eof)
wxMilliSleep(100);
}
if ((c == '\n') || (c == '\r') || eof) {
if (m_dialog) {
// send line to dialog
wxCommandEvent evt(mux_thread::event, mux_thread::output_available);
evt.SetString(wxU(line));
wxPostEvent(m_dialog, evt);
}
line.clear();
} else if (char_read && (static_cast<unsigned char>(c) != 0xff))
line += c;
if (eof)
break;
}
return nullptr;
}
bool
mux_thread::read_input(char &c,
bool &eof) {
eof = false;
if (!m_available_input.empty()) {
c = m_available_input.front();
m_available_input.pop_front();
return true;
}
wxCriticalSectionLocker locker{m_cs_process};
if (!m_out || m_out->Eof()) {
eof = true;
return false;
}
while (m_out->CanRead())
m_available_input.push_back(m_out->GetC());
if (m_available_input.empty())
return false;
c = m_available_input.front();
m_available_input.pop_front();
return true;
}
wxEventType const mux_process::event{wxNewEventType()};
mux_process::mux_process(mux_dialog *dialog)
: wxProcess(wxPROCESS_REDIRECT)
@ -442,21 +367,47 @@ mux_process::mux_process(mux_dialog *dialog)
void
mux_process::OnTerminate(int,
int status) {
wxCriticalSectionLocker locker{m_cs_dialog};
process_input();
if (!m_dialog)
return;
wxCommandEvent evt(mux_thread::event, mux_thread::process_terminated);
wxCommandEvent evt(mux_process::event, mux_process::process_terminated);
evt.SetInt(status);
wxPostEvent(m_dialog, evt);
}
void
mux_process::process_input(bool end_of_process) {
if (!m_dialog)
return;
std::string input;
auto in = GetInputStream();
if (in)
while (in->CanRead())
input += in->GetC();
if (end_of_process)
input += "\n";
if (!input.empty())
m_dialog->add_input(input);
}
void
mux_process::detach_from_dialog() {
m_dialog = nullptr;
Detach();
}
IMPLEMENT_CLASS(mux_dialog, wxDialog);
BEGIN_EVENT_TABLE(mux_dialog, wxDialog)
EVT_BUTTON(ID_B_MUX_OK, mux_dialog::on_ok)
EVT_BUTTON(ID_B_MUX_SAVELOG, mux_dialog::on_save_log)
EVT_BUTTON(ID_B_MUX_ABORT, mux_dialog::on_abort)
EVT_COMMAND(mux_thread::output_available, mux_thread::event, mux_dialog::on_output_available)
EVT_COMMAND(mux_thread::process_terminated, mux_thread::event, mux_dialog::on_process_terminated)
EVT_TIMER(ID_T_READ_INPUT, mux_dialog::on_read_input)
EVT_COMMAND(mux_process::output_available, mux_process::event, mux_dialog::on_output_available)
EVT_COMMAND(mux_process::process_terminated, mux_process::event, mux_dialog::on_process_terminated)
EVT_CLOSE(mux_dialog::on_close)
END_EVENT_TABLE();

View File

@ -27,23 +27,19 @@
#define ID_B_MUX_OK 18000
#define ID_B_MUX_SAVELOG 18001
#define ID_B_MUX_ABORT 18002
#define ID_T_READ_INPUT 18003
extern const wxEventType mux_thread_event;
class mux_thread;
class mux_process;
class mux_dialog: public wxDialog {
DECLARE_CLASS(mux_dialog);
DECLARE_EVENT_TABLE();
protected:
friend class mux_thread;
long pid;
wxStaticText *st_label, *st_remaining_time_label, *st_remaining_time;
wxGauge *g_progress;
wxCriticalSection m_cs_thread;
mux_thread *m_thread;
mux_process *m_process;
int m_pid;
wxString log, opt_file_name;
@ -56,6 +52,10 @@ protected:
int m_exit_code, m_progress;
int64_t m_next_remaining_time_update, m_start_time;
wxTimer m_read_input_timer;
std::string m_available_input;
public:
mux_dialog(wxWindow *parent);
@ -73,6 +73,9 @@ public:
void on_close(wxCloseEvent &evt);
void on_output_available(wxCommandEvent &evt);
void on_process_terminated(wxCommandEvent &evt);
void on_read_input(wxTimerEvent &evt);
void add_input(std::string const &input);
#if defined(SYS_WINDOWS)
void change_abort_button();
@ -80,19 +83,8 @@ public:
};
class mux_process: public wxProcess {
friend class mux_dialog;
private:
mux_dialog *m_dialog;
wxCriticalSection m_cs_dialog;
public:
mux_process(mux_dialog *dialog);
virtual void OnTerminate(int terminated_pid, int status);
};
class mux_thread: public wxThread {
friend class mux_dialog;
public:
static wxEventType const event;
@ -101,18 +93,12 @@ public:
process_terminated,
};
private:
mux_dialog *m_dialog;
wxProcess *m_process;
wxInputStream *m_out;
wxCriticalSection m_cs_process;
std::deque<char> m_available_input;
public:
mux_thread(mux_dialog *dialog, wxProcess *process);
virtual void *Entry();
mux_process(mux_dialog *dialog);
virtual void OnTerminate(int terminated_pid, int status);
bool read_input(char &c, bool &eof);
void process_input(bool end_of_input = false);
void detach_from_dialog();
};
#endif // __MUX_DIALOG_H