Browse Source

Callback of Terminal::get().async_process is now run in the main thread

pipelines/280567345
eidheim 5 years ago
parent
commit
92b7cbdb2b
  1. 24
      src/cmake.cpp
  2. 24
      src/meson.cpp
  3. 14
      src/project.cpp
  4. 8
      src/terminal.cpp
  5. 1
      src/terminal.hpp
  6. 2
      src/window.cpp
  7. 48
      tests/terminal_test.cpp

24
src/cmake.cpp

@ -64,24 +64,24 @@ bool CMake::update_default_build(const boost::filesystem::path &default_build_pa
Dialog::Message message("Creating/updating default build", [&canceled] { Dialog::Message message("Creating/updating default build", [&canceled] {
canceled = true; canceled = true;
}); });
std::promise<int> promise; boost::optional<int> exit_status;
auto process = Terminal::get().async_process(Config::get().project.cmake.command + ' ' + filesystem::escape_argument(project_path.string()) + " -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", auto process = Terminal::get().async_process(Config::get().project.cmake.command + ' ' + filesystem::escape_argument(project_path.string()) + " -DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
default_build_path, default_build_path,
[&promise](int exit_status) { [&exit_status](int exit_status_) {
promise.set_value(exit_status); exit_status = exit_status_;
}); });
auto future = promise.get_future();
bool killed = false; bool killed = false;
while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { while(!exit_status) {
if(canceled && !killed) { if(canceled && !killed) {
process->kill(); process->kill();
killed = true; killed = true;
} }
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(); Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
message.hide(); message.hide();
if(future.get() == 0) { if(exit_status == 0) {
#ifdef _WIN32 //Temporary fix to MSYS2's libclang #ifdef _WIN32 //Temporary fix to MSYS2's libclang
auto compile_commands_file = filesystem::read(compile_commands_path); auto compile_commands_file = filesystem::read(compile_commands_path);
auto replace_drive = [&compile_commands_file](const std::string &param) { auto replace_drive = [&compile_commands_file](const std::string &param) {
@ -124,24 +124,24 @@ bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path,
Dialog::Message message("Creating/updating debug build", [&canceled] { Dialog::Message message("Creating/updating debug build", [&canceled] {
canceled = true; canceled = true;
}); });
std::promise<int> promise; boost::optional<int> exit_status;
auto process = Terminal::get().async_process(Config::get().project.cmake.command + ' ' + filesystem::escape_argument(project_path.string()) + " -DCMAKE_BUILD_TYPE=Debug", auto process = Terminal::get().async_process(Config::get().project.cmake.command + ' ' + filesystem::escape_argument(project_path.string()) + " -DCMAKE_BUILD_TYPE=Debug",
debug_build_path, debug_build_path,
[&promise](int exit_status) { [&exit_status](int exit_status_) {
promise.set_value(exit_status); exit_status = exit_status_;
}); });
auto future = promise.get_future();
bool killed = false; bool killed = false;
while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { while(!exit_status) {
if(canceled && !killed) { if(canceled && !killed) {
process->kill(); process->kill();
killed = true; killed = true;
} }
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(); Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
message.hide(); message.hide();
return future.get() == 0; return exit_status == 0;
} }
boost::filesystem::path CMake::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { boost::filesystem::path CMake::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {

24
src/meson.cpp

@ -64,24 +64,24 @@ bool Meson::update_default_build(const boost::filesystem::path &default_build_pa
Dialog::Message message("Creating/updating default build", [&canceled] { Dialog::Message message("Creating/updating default build", [&canceled] {
canceled = true; canceled = true;
}); });
std::promise<int> promise; boost::optional<int> exit_status;
auto process = Terminal::get().async_process(Config::get().project.meson.command + ' ' + (compile_commands_exists ? "--internal regenerate " : "") + "--buildtype plain " + filesystem::escape_argument(project_path.string()), auto process = Terminal::get().async_process(Config::get().project.meson.command + ' ' + (compile_commands_exists ? "--internal regenerate " : "") + "--buildtype plain " + filesystem::escape_argument(project_path.string()),
default_build_path, default_build_path,
[&promise](int exit_status) { [&exit_status](int exit_status_) {
promise.set_value(exit_status); exit_status = exit_status_;
}); });
auto future = promise.get_future();
bool killed = false; bool killed = false;
while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { while(!exit_status) {
if(canceled && !killed) { if(canceled && !killed) {
process->kill(); process->kill();
killed = true; killed = true;
} }
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(); Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
message.hide(); message.hide();
return future.get() == 0; return exit_status == 0;
} }
bool Meson::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) { bool Meson::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) {
@ -106,24 +106,24 @@ bool Meson::update_debug_build(const boost::filesystem::path &debug_build_path,
Dialog::Message message("Creating/updating debug build", [&canceled] { Dialog::Message message("Creating/updating debug build", [&canceled] {
canceled = true; canceled = true;
}); });
std::promise<int> promise; boost::optional<int> exit_status;
auto process = Terminal::get().async_process(Config::get().project.meson.command + ' ' + (compile_commands_exists ? "--internal regenerate " : "") + "--buildtype debug " + filesystem::escape_argument(project_path.string()), auto process = Terminal::get().async_process(Config::get().project.meson.command + ' ' + (compile_commands_exists ? "--internal regenerate " : "") + "--buildtype debug " + filesystem::escape_argument(project_path.string()),
debug_build_path, debug_build_path,
[&promise](int exit_status) { [&exit_status](int exit_status_) {
promise.set_value(exit_status); exit_status = exit_status_;
}); });
auto future = promise.get_future();
bool killed = false; bool killed = false;
while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { while(!exit_status) {
if(canceled && !killed) { if(canceled && !killed) {
process->kill(); process->kill();
killed = true; killed = true;
} }
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(); Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
message.hide(); message.hide();
return future.get() == 0; return exit_status == 0;
} }
boost::filesystem::path Meson::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { boost::filesystem::path Meson::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {

14
src/project.cpp

@ -386,7 +386,6 @@ void Project::LLDB::debug_start() {
if(exit_status != EXIT_SUCCESS) if(exit_status != EXIT_SUCCESS)
debugging = false; debugging = false;
else { else {
self->dispatcher.post([self, run_arguments, project_path] {
std::vector<std::pair<boost::filesystem::path, int>> breakpoints; std::vector<std::pair<boost::filesystem::path, int>> breakpoints;
for(size_t c = 0; c < Notebook::get().size(); c++) { for(size_t c = 0; c < Notebook::get().size(); c++) {
auto view = Notebook::get().get_view(c); auto view = Notebook::get().get_view(c);
@ -488,7 +487,6 @@ void Project::LLDB::debug_start() {
} }
} }
Debug::LLDB::get().start(*run_arguments, *project_path, breakpoints, startup_commands, remote_host); Debug::LLDB::get().start(*run_arguments, *project_path, breakpoints, startup_commands, remote_host);
});
} }
}); });
} }
@ -893,7 +891,7 @@ void Project::Clang::compile_and_run() {
compiling = false; compiling = false;
if(exit_status == 0) { if(exit_status == 0) {
Terminal::get().async_process(arguments, project_path, [arguments](int exit_status) { Terminal::get().async_process(arguments, project_path, [arguments](int exit_status) {
Terminal::get().async_print("\e[2m" + arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n"); Terminal::get().print("\e[2m" + arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
}); });
} }
}); });
@ -973,7 +971,7 @@ void Project::Markdown::compile_and_run() {
Terminal::get().async_process( Terminal::get().async_process(
command, "", [command](int exit_status) { command, "", [command](int exit_status) {
if(exit_status == 127) if(exit_status == 127)
Terminal::get().async_print("\e[31mError\e[m: executable not found: " + command + "\n", true); Terminal::get().print("\e[31mError\e[m: executable not found: " + command + "\n", true);
}, },
true); true);
} }
@ -1001,7 +999,7 @@ void Project::Python::compile_and_run() {
Terminal::get().print("\e[2mRunning " + command + "\e[m\n"); Terminal::get().print("\e[2mRunning " + command + "\e[m\n");
Terminal::get().async_process(command, path, [command](int exit_status) { Terminal::get().async_process(command, path, [command](int exit_status) {
Terminal::get().async_print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n"); Terminal::get().print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
}); });
} }
@ -1027,7 +1025,7 @@ void Project::JavaScript::compile_and_run() {
Terminal::get().print("\e[2mRunning " + command + "\e[m\n"); Terminal::get().print("\e[2mRunning " + command + "\e[m\n");
Terminal::get().async_process(command, path, [command](int exit_status) { Terminal::get().async_process(command, path, [command](int exit_status) {
Terminal::get().async_print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n"); Terminal::get().print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
}); });
} }
@ -1040,7 +1038,7 @@ void Project::HTML::compile_and_run() {
Terminal::get().print("\e[2mRunning " + command + "\e[m\n"); Terminal::get().print("\e[2mRunning " + command + "\e[m\n");
Terminal::get().async_process(command, build->project_path, [command](int exit_status) { Terminal::get().async_process(command, build->project_path, [command](int exit_status) {
Terminal::get().async_print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n"); Terminal::get().print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
}); });
} }
else if(auto view = Notebook::get().get_current_view()) else if(auto view = Notebook::get().get_current_view())
@ -1088,7 +1086,7 @@ void Project::Rust::compile_and_run() {
compiling = false; compiling = false;
if(exit_status == 0) { if(exit_status == 0) {
Terminal::get().async_process(arguments, self->build->project_path, [arguments](int exit_status) { Terminal::get().async_process(arguments, self->build->project_path, [arguments](int exit_status) {
Terminal::get().async_print("\e[2m" + arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n"); Terminal::get().print("\e[2m" + arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
}); });
} }
}); });

8
src/terminal.cpp

@ -337,7 +337,7 @@ std::shared_ptr<TinyProcessLib::Process> Terminal::async_process(const std::stri
processes.emplace_back(process); processes.emplace_back(process);
} }
std::thread exit_status_thread([this, process, pid, callback = std::move(callback)]() { std::thread([this, process, pid, callback = std::move(callback)]() mutable {
auto exit_status = process->get_exit_status(); auto exit_status = process->get_exit_status();
{ {
LockGuard lock(processes_mutex); LockGuard lock(processes_mutex);
@ -348,10 +348,12 @@ std::shared_ptr<TinyProcessLib::Process> Terminal::async_process(const std::stri
} }
} }
} }
if(callback) if(callback) {
dispatcher.post([callback = std::move(callback), exit_status] {
callback(exit_status); callback(exit_status);
}); });
exit_status_thread.detach(); }
}).detach();
return process; return process;
} }

1
src/terminal.hpp

@ -21,6 +21,7 @@ public:
int process(const std::string &command, const boost::filesystem::path &path = "", bool use_pipes = true); int process(const std::string &command, const boost::filesystem::path &path = "", bool use_pipes = true);
int process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path = "", std::ostream *stderr_stream = nullptr); int process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path = "", std::ostream *stderr_stream = nullptr);
/// The callback is run in the main thread.
std::shared_ptr<TinyProcessLib::Process> async_process(const std::string &command, const boost::filesystem::path &path = "", std::function<void(int exit_status)> callback = nullptr, bool quiet = false); std::shared_ptr<TinyProcessLib::Process> async_process(const std::string &command, const boost::filesystem::path &path = "", std::function<void(int exit_status)> callback = nullptr, bool quiet = false);
void kill_last_async_process(bool force = false); void kill_last_async_process(bool force = false);
void kill_async_processes(bool force = false); void kill_async_processes(bool force = false);

2
src/window.cpp

@ -1391,7 +1391,7 @@ void Window::set_menu_actions() {
Terminal::get().async_print("\e[2mRunning: " + content + "\e[m\n"); Terminal::get().async_print("\e[2mRunning: " + content + "\e[m\n");
Terminal::get().async_process(content, directory_folder, [content](int exit_status) { Terminal::get().async_process(content, directory_folder, [content](int exit_status) {
Terminal::get().async_print("\e[2m" + content + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n"); Terminal::get().print("\e[2m" + content + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
}); });
} }
if(Config::get().terminal.hide_entry_on_run_command) if(Config::get().terminal.hide_entry_on_run_command)

48
tests/terminal_test.cpp

@ -281,68 +281,68 @@ int main() {
// async_process tests // async_process tests
{ {
terminal.clear(); terminal.clear();
std::promise<int> done; boost::optional<int> exit_status;
terminal.async_process("echo test", "", [&done](int exit_status) { terminal.async_process("echo test", "", [&exit_status](int exit_status_) {
done.set_value(exit_status); exit_status = exit_status_;
}); });
auto future = done.get_future(); while(!exit_status) {
while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) {
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(); Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
assert(future.get() == 0); assert(exit_status == 0);
assert(buffer->get_text() == "test\n"); assert(buffer->get_text() == "test\n");
assert(!buffer->begin().starts_tag(terminal.bold_tag)); assert(!buffer->begin().starts_tag(terminal.bold_tag));
assert(!buffer->end().ends_tag(terminal.bold_tag)); assert(!buffer->end().ends_tag(terminal.bold_tag));
} }
{ {
terminal.clear(); terminal.clear();
std::promise<int> done; boost::optional<int> exit_status;
terminal.async_process( terminal.async_process(
"echo test", "", [&done](int exit_status) { "echo test", "", [&exit_status](int exit_status_) {
done.set_value(exit_status); exit_status = exit_status_;
}, },
true); true);
auto future = done.get_future(); while(!exit_status) {
while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) {
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(); Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
assert(future.get() == 0); assert(exit_status == 0);
assert(buffer->get_text() == ""); assert(buffer->get_text() == "");
} }
{ {
terminal.clear(); terminal.clear();
std::promise<int> done; boost::optional<int> exit_status;
terminal.async_process("testing_invalid_command", "", [&done](int exit_status) { terminal.async_process("testing_invalid_command", "", [&exit_status](int exit_status_) {
done.set_value(exit_status); exit_status = exit_status_;
}); });
auto future = done.get_future(); while(!exit_status) {
while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) {
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(); Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
assert(future.get() != 0); assert(exit_status != 0);
assert(buffer->begin().starts_tag(terminal.bold_tag)); assert(buffer->begin().starts_tag(terminal.bold_tag));
assert(buffer->end().ends_tag(terminal.bold_tag)); assert(buffer->end().ends_tag(terminal.bold_tag));
assert(buffer->get_text() != ""); assert(buffer->get_text() != "");
} }
{ {
terminal.clear(); terminal.clear();
std::promise<int> done; boost::optional<int> exit_status;
terminal.async_process( terminal.async_process(
"testing_invalid_command", "", [&done](int exit_status) { "testing_invalid_command", "", [&exit_status](int exit_status_) {
done.set_value(exit_status); exit_status = exit_status_;
}, },
true); true);
auto future = done.get_future(); while(!exit_status) {
while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) {
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(); Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
assert(future.get() != 0); assert(exit_status != 0);
assert(buffer->get_text() == ""); assert(buffer->get_text() == "");
} }

Loading…
Cancel
Save