From 727d9c1c0ce8f52d0542ce2d2ec0603a6af5b276 Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 7 Sep 2020 17:47:00 +0200 Subject: [PATCH] Fixes #431: can now cancel creating/updating build folder --- src/cmake.cpp | 40 ++++++++++++++------ src/meson.cpp | 40 ++++++++++++++------ src/terminal.cpp | 97 ++++++++++++++++++++++++------------------------ src/terminal.hpp | 4 +- 4 files changed, 107 insertions(+), 74 deletions(-) diff --git a/src/cmake.cpp b/src/cmake.cpp index 5ae3f00..41c6b36 100644 --- a/src/cmake.cpp +++ b/src/cmake.cpp @@ -60,15 +60,23 @@ bool CMake::update_default_build(const boost::filesystem::path &default_build_pa return true; auto compile_commands_path = default_build_path / "compile_commands.json"; - Dialog::Message message("Creating/updating default build"); + bool canceled = false; + Dialog::Message message("Creating/updating default build", [&canceled] { + canceled = true; + }); std::promise promise; - Terminal::get().async_process(Config::get().project.cmake.command + ' ' + filesystem::escape_argument(project_path.string()) + " -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", - default_build_path, - [&promise](int exit_status) { - promise.set_value(exit_status); - }); + 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, + [&promise](int exit_status) { + promise.set_value(exit_status); + }); auto future = promise.get_future(); + bool killed = false; while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + if(canceled && !killed) { + process->kill(); + killed = true; + } while(Gtk::Main::events_pending()) Gtk::Main::iteration(); } @@ -112,15 +120,23 @@ bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path, if(!force && boost::filesystem::exists(debug_build_path / "CMakeCache.txt", ec)) return true; - Dialog::Message message("Creating/updating debug build"); + bool canceled = false; + Dialog::Message message("Creating/updating debug build", [&canceled] { + canceled = true; + }); std::promise promise; - Terminal::get().async_process(Config::get().project.cmake.command + ' ' + filesystem::escape_argument(project_path.string()) + " -DCMAKE_BUILD_TYPE=Debug", - debug_build_path, - [&promise](int exit_status) { - promise.set_value(exit_status); - }); + auto process = Terminal::get().async_process(Config::get().project.cmake.command + ' ' + filesystem::escape_argument(project_path.string()) + " -DCMAKE_BUILD_TYPE=Debug", + debug_build_path, + [&promise](int exit_status) { + promise.set_value(exit_status); + }); auto future = promise.get_future(); + bool killed = false; while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + if(canceled && !killed) { + process->kill(); + killed = true; + } while(Gtk::Main::events_pending()) Gtk::Main::iteration(); } diff --git a/src/meson.cpp b/src/meson.cpp index 278e3ed..31e4bde 100644 --- a/src/meson.cpp +++ b/src/meson.cpp @@ -60,15 +60,23 @@ bool Meson::update_default_build(const boost::filesystem::path &default_build_pa if(!force && compile_commands_exists) return true; - Dialog::Message message("Creating/updating default build"); + bool canceled = false; + Dialog::Message message("Creating/updating default build", [&canceled] { + canceled = true; + }); std::promise promise; - 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, - [&promise](int exit_status) { - promise.set_value(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()), + default_build_path, + [&promise](int exit_status) { + promise.set_value(exit_status); + }); auto future = promise.get_future(); + bool killed = false; while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + if(canceled && !killed) { + process->kill(); + killed = true; + } while(Gtk::Main::events_pending()) Gtk::Main::iteration(); } @@ -94,15 +102,23 @@ bool Meson::update_debug_build(const boost::filesystem::path &debug_build_path, if(!force && compile_commands_exists) return true; - Dialog::Message message("Creating/updating debug build"); + bool canceled = false; + Dialog::Message message("Creating/updating debug build", [&canceled] { + canceled = true; + }); std::promise promise; - 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, - [&promise](int exit_status) { - promise.set_value(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()), + debug_build_path, + [&promise](int exit_status) { + promise.set_value(exit_status); + }); auto future = promise.get_future(); + bool killed = false; while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + if(canceled && !killed) { + process->kill(); + killed = true; + } while(Gtk::Main::events_pending()) Gtk::Main::iteration(); } diff --git a/src/terminal.cpp b/src/terminal.cpp index 37f44b0..ade5218 100644 --- a/src/terminal.cpp +++ b/src/terminal.cpp @@ -254,61 +254,61 @@ int Terminal::process(std::istream &stdin_stream, std::ostream &stdout_stream, c return process.get_exit_status(); } -void Terminal::async_process(const std::string &command, const boost::filesystem::path &path, const std::function &callback, bool quiet) { - std::thread async_execute_thread([this, command, path, callback, quiet]() { - perform_scroll_to_bottom = true; - LockGuard lock(processes_mutex); - stdin_buffer.clear(); - auto process = std::make_shared(command, path.string(), [this, quiet](const char *bytes, size_t n) { - if(!quiet) { - // Print stdout message sequentially to avoid the GUI becoming unresponsive - std::promise message_printed; - dispatcher.post([message = std::string(bytes, n), &message_printed]() mutable { - Terminal::get().print(std::move(message)); - message_printed.set_value(); - }); - message_printed.get_future().get(); - } - }, [this, quiet](const char *bytes, size_t n) { - if(!quiet) { - // Print stderr message sequentially to avoid the GUI becoming unresponsive - std::promise message_printed; - dispatcher.post([message = std::string(bytes, n), &message_printed]() mutable { - Terminal::get().print(std::move(message), true); - message_printed.set_value(); - }); - message_printed.get_future().get(); - } - }, true); - auto pid = process->get_id(); - if(pid <= 0) { - lock.unlock(); - async_print("Error: failed to run command: " + command + "\n", true); - if(callback) - callback(-1); - return; +std::shared_ptr Terminal::async_process(const std::string &command, const boost::filesystem::path &path, std::function callback, bool quiet) { + stdin_buffer.clear(); + perform_scroll_to_bottom = true; + + auto process = std::make_shared(command, path.string(), [this, quiet](const char *bytes, size_t n) { + if(!quiet) { + // Print stdout message sequentially to avoid the GUI becoming unresponsive + std::promise message_printed; + dispatcher.post([message = std::string(bytes, n), &message_printed]() mutable { + Terminal::get().print(std::move(message)); + message_printed.set_value(); + }); + message_printed.get_future().get(); } - else { - processes.emplace_back(process); - lock.unlock(); + }, [this, quiet](const char *bytes, size_t n) { + if(!quiet) { + // Print stderr message sequentially to avoid the GUI becoming unresponsive + std::promise message_printed; + dispatcher.post([message = std::string(bytes, n), &message_printed]() mutable { + Terminal::get().print(std::move(message), true); + message_printed.set_value(); + }); + message_printed.get_future().get(); } + }, true); - auto exit_status = process->get_exit_status(); + auto pid = process->get_id(); + if(pid <= 0) { + async_print("Error: failed to run command: " + command + "\n", true); + if(callback) + callback(-1); + return process; + } + else { + LockGuard lock(processes_mutex); + processes.emplace_back(process); + } - lock.lock(); - for(auto it = processes.begin(); it != processes.end(); it++) { - if((*it)->get_id() == pid) { - processes.erase(it); - break; + std::thread exit_status_thread([this, process, pid, callback = std::move(callback)]() { + auto exit_status = process->get_exit_status(); + { + LockGuard lock(processes_mutex); + for(auto it = processes.begin(); it != processes.end(); it++) { + if((*it)->get_id() == pid) { + processes.erase(it); + break; + } } } - stdin_buffer.clear(); - lock.unlock(); - if(callback) callback(exit_status); }); - async_execute_thread.detach(); + exit_status_thread.detach(); + + return process; } void Terminal::kill_last_async_process(bool force) { @@ -410,9 +410,10 @@ void Terminal::print(std::string message, bool bold) { if(!parent->is_visible()) parent->show(); - bool expected = true; - if(perform_scroll_to_bottom.compare_exchange_strong(expected, false) && scroll_to_bottom) + if(perform_scroll_to_bottom == true && scroll_to_bottom) { + perform_scroll_to_bottom = false; scroll_to_bottom(); + } } Glib::ustring umessage = std::move(message); diff --git a/src/terminal.hpp b/src/terminal.hpp index 68d95e3..7195a60 100644 --- a/src/terminal.hpp +++ b/src/terminal.hpp @@ -21,7 +21,7 @@ public: 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); - void async_process(const std::string &command, const boost::filesystem::path &path = "", const std::function &callback = nullptr, bool quiet = false); + std::shared_ptr async_process(const std::string &command, const boost::filesystem::path &path = "", std::function callback = nullptr, bool quiet = false); void kill_last_async_process(bool force = false); void kill_async_processes(bool force = false); @@ -61,5 +61,5 @@ private: std::vector> processes GUARDED_BY(processes_mutex); Glib::ustring stdin_buffer; - std::atomic perform_scroll_to_bottom = {false}; + bool perform_scroll_to_bottom = false; };