diff --git a/src/cmake.cpp b/src/cmake.cpp index 8d11a1b..c6bac87 100644 --- a/src/cmake.cpp +++ b/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] { canceled = true; }); - std::promise promise; + boost::optional 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); + [&exit_status](int exit_status_) { + exit_status = exit_status_; }); - auto future = promise.get_future(); bool killed = false; - while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + while(!exit_status) { if(canceled && !killed) { process->kill(); killed = true; } while(Gtk::Main::events_pending()) Gtk::Main::iteration(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } message.hide(); - if(future.get() == 0) { + if(exit_status == 0) { #ifdef _WIN32 //Temporary fix to MSYS2's libclang auto compile_commands_file = filesystem::read(compile_commands_path); auto replace_drive = [&compile_commands_file](const std::string ¶m) { @@ -124,24 +124,24 @@ bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path, Dialog::Message message("Creating/updating debug build", [&canceled] { canceled = true; }); - std::promise promise; + boost::optional 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); + [&exit_status](int exit_status_) { + exit_status = exit_status_; }); - auto future = promise.get_future(); bool killed = false; - while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + while(!exit_status) { if(canceled && !killed) { process->kill(); killed = true; } while(Gtk::Main::events_pending()) Gtk::Main::iteration(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } 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) { diff --git a/src/meson.cpp b/src/meson.cpp index a85afb1..a2a6bfa 100644 --- a/src/meson.cpp +++ b/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] { canceled = true; }); - std::promise promise; + boost::optional 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); + [&exit_status](int exit_status_) { + exit_status = exit_status_; }); - auto future = promise.get_future(); bool killed = false; - while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + while(!exit_status) { if(canceled && !killed) { process->kill(); killed = true; } while(Gtk::Main::events_pending()) Gtk::Main::iteration(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } message.hide(); - return future.get() == 0; + return exit_status == 0; } 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] { canceled = true; }); - std::promise promise; + boost::optional 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); + [&exit_status](int exit_status_) { + exit_status = exit_status_; }); - auto future = promise.get_future(); bool killed = false; - while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + while(!exit_status) { if(canceled && !killed) { process->kill(); killed = true; } while(Gtk::Main::events_pending()) Gtk::Main::iteration(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } 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) { diff --git a/src/project.cpp b/src/project.cpp index 7e6ac9a..e9753d3 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -386,109 +386,107 @@ void Project::LLDB::debug_start() { if(exit_status != EXIT_SUCCESS) debugging = false; else { - self->dispatcher.post([self, run_arguments, project_path] { - std::vector> breakpoints; - for(size_t c = 0; c < Notebook::get().size(); c++) { - auto view = Notebook::get().get_view(c); - if(filesystem::file_in_path(view->file_path, *project_path)) { - auto iter = view->get_buffer()->begin(); - if(view->get_source_buffer()->get_source_marks_at_iter(iter, "debug_breakpoint").size() > 0) - breakpoints.emplace_back(view->file_path, iter.get_line() + 1); - while(view->get_source_buffer()->forward_iter_to_source_mark(iter, "debug_breakpoint")) - breakpoints.emplace_back(view->file_path, iter.get_line() + 1); - } + std::vector> breakpoints; + for(size_t c = 0; c < Notebook::get().size(); c++) { + auto view = Notebook::get().get_view(c); + if(filesystem::file_in_path(view->file_path, *project_path)) { + auto iter = view->get_buffer()->begin(); + if(view->get_source_buffer()->get_source_marks_at_iter(iter, "debug_breakpoint").size() > 0) + breakpoints.emplace_back(view->file_path, iter.get_line() + 1); + while(view->get_source_buffer()->forward_iter_to_source_mark(iter, "debug_breakpoint")) + breakpoints.emplace_back(view->file_path, iter.get_line() + 1); } + } - std::string remote_host; - auto debug_run_arguments_it = debug_run_arguments.find(project_path->string()); - if(debug_run_arguments_it != debug_run_arguments.end() && debug_run_arguments_it->second.remote_enabled) - remote_host = debug_run_arguments_it->second.remote_host_port; - - static auto on_exit_it = Debug::LLDB::get().on_exit.end(); - if(on_exit_it != Debug::LLDB::get().on_exit.end()) - Debug::LLDB::get().on_exit.erase(on_exit_it); - Debug::LLDB::get().on_exit.emplace_back([self, run_arguments](int exit_status) { - debugging = false; - Terminal::get().async_print("\e[2m" + *run_arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n"); - self->dispatcher.post([] { - debug_update_status(""); - }); + std::string remote_host; + auto debug_run_arguments_it = debug_run_arguments.find(project_path->string()); + if(debug_run_arguments_it != debug_run_arguments.end() && debug_run_arguments_it->second.remote_enabled) + remote_host = debug_run_arguments_it->second.remote_host_port; + + static auto on_exit_it = Debug::LLDB::get().on_exit.end(); + if(on_exit_it != Debug::LLDB::get().on_exit.end()) + Debug::LLDB::get().on_exit.erase(on_exit_it); + Debug::LLDB::get().on_exit.emplace_back([self, run_arguments](int exit_status) { + debugging = false; + Terminal::get().async_print("\e[2m" + *run_arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n"); + self->dispatcher.post([] { + debug_update_status(""); }); - on_exit_it = std::prev(Debug::LLDB::get().on_exit.end()); - - static auto on_event_it = Debug::LLDB::get().on_event.end(); - if(on_event_it != Debug::LLDB::get().on_event.end()) - Debug::LLDB::get().on_event.erase(on_event_it); - Debug::LLDB::get().on_event.emplace_back([self](const lldb::SBEvent &event) { - std::string status; - boost::filesystem::path stop_path; - unsigned stop_line = 0, stop_column = 0; - - LockGuard lock(Debug::LLDB::get().mutex); - auto process = lldb::SBProcess::GetProcessFromEvent(event); - auto state = lldb::SBProcess::GetStateFromEvent(event); - lldb::SBStream stream; - event.GetDescription(stream); - std::string event_desc = stream.GetData(); - event_desc.pop_back(); - auto pos = event_desc.rfind(" = "); - if(pos != std::string::npos && pos + 3 < event_desc.size()) - status = event_desc.substr(pos + 3); - if(state == lldb::StateType::eStateStopped) { - char buffer[100]; - auto thread = process.GetSelectedThread(); - auto n = thread.GetStopDescription(buffer, 100); // Returns number of bytes read. Might include null termination... Although maybe on newer versions only. - if(n > 0) - status += " (" + std::string(buffer, n <= 100 ? (buffer[n - 1] == '\0' ? n - 1 : n) : 100) + ")"; - auto line_entry = thread.GetSelectedFrame().GetLineEntry(); - if(line_entry.IsValid()) { - lldb::SBStream stream; - line_entry.GetFileSpec().GetDescription(stream); - auto line = line_entry.GetLine(); - status += " " + boost::filesystem::path(stream.GetData()).filename().string() + ":" + std::to_string(line); - auto column = line_entry.GetColumn(); - if(column == 0) - column = 1; - stop_path = filesystem::get_normal_path(stream.GetData()); - stop_line = line - 1; - stop_column = column - 1; - } + }); + on_exit_it = std::prev(Debug::LLDB::get().on_exit.end()); + + static auto on_event_it = Debug::LLDB::get().on_event.end(); + if(on_event_it != Debug::LLDB::get().on_event.end()) + Debug::LLDB::get().on_event.erase(on_event_it); + Debug::LLDB::get().on_event.emplace_back([self](const lldb::SBEvent &event) { + std::string status; + boost::filesystem::path stop_path; + unsigned stop_line = 0, stop_column = 0; + + LockGuard lock(Debug::LLDB::get().mutex); + auto process = lldb::SBProcess::GetProcessFromEvent(event); + auto state = lldb::SBProcess::GetStateFromEvent(event); + lldb::SBStream stream; + event.GetDescription(stream); + std::string event_desc = stream.GetData(); + event_desc.pop_back(); + auto pos = event_desc.rfind(" = "); + if(pos != std::string::npos && pos + 3 < event_desc.size()) + status = event_desc.substr(pos + 3); + if(state == lldb::StateType::eStateStopped) { + char buffer[100]; + auto thread = process.GetSelectedThread(); + auto n = thread.GetStopDescription(buffer, 100); // Returns number of bytes read. Might include null termination... Although maybe on newer versions only. + if(n > 0) + status += " (" + std::string(buffer, n <= 100 ? (buffer[n - 1] == '\0' ? n - 1 : n) : 100) + ")"; + auto line_entry = thread.GetSelectedFrame().GetLineEntry(); + if(line_entry.IsValid()) { + lldb::SBStream stream; + line_entry.GetFileSpec().GetDescription(stream); + auto line = line_entry.GetLine(); + status += " " + boost::filesystem::path(stream.GetData()).filename().string() + ":" + std::to_string(line); + auto column = line_entry.GetColumn(); + if(column == 0) + column = 1; + stop_path = filesystem::get_normal_path(stream.GetData()); + stop_line = line - 1; + stop_column = column - 1; } + } - self->dispatcher.post([status = std::move(status), stop_path = std::move(stop_path), stop_line, stop_column] { - debug_update_status(status); - Project::debug_stop.first = stop_path; - Project::debug_stop.second.first = stop_line; - Project::debug_stop.second.second = stop_column; - debug_update_stop(); - - if(Config::get().source.debug_place_cursor_at_stop && !stop_path.empty()) { - if(Notebook::get().open(stop_path)) { - auto view = Notebook::get().get_current_view(); - view->place_cursor_at_line_index(stop_line, stop_column); - view->scroll_to_cursor_delayed(true, false); - } + self->dispatcher.post([status = std::move(status), stop_path = std::move(stop_path), stop_line, stop_column] { + debug_update_status(status); + Project::debug_stop.first = stop_path; + Project::debug_stop.second.first = stop_line; + Project::debug_stop.second.second = stop_column; + debug_update_stop(); + + if(Config::get().source.debug_place_cursor_at_stop && !stop_path.empty()) { + if(Notebook::get().open(stop_path)) { + auto view = Notebook::get().get_current_view(); + view->place_cursor_at_line_index(stop_line, stop_column); + view->scroll_to_cursor_delayed(true, false); } - else if(auto view = Notebook::get().get_current_view()) - view->get_buffer()->place_cursor(view->get_buffer()->get_insert()->get_iter()); - }); - }); - on_event_it = std::prev(Debug::LLDB::get().on_event.end()); - - std::vector startup_commands; - if(dynamic_cast(self->build.get())) { - std::stringstream istream, ostream; - if(Terminal::get().process(istream, ostream, "rustc --print sysroot") == 0) { - auto sysroot = ostream.str(); - while(!sysroot.empty() && (sysroot.back() == '\n' || sysroot.back() == '\r')) - sysroot.pop_back(); - startup_commands.emplace_back("command script import \"" + sysroot + "/lib/rustlib/etc/lldb_rust_formatters.py\""); - startup_commands.emplace_back("type summary add --no-value --python-function lldb_rust_formatters.print_val -x \".*\" --category Rust"); - startup_commands.emplace_back("type category enable Rust"); } - } - Debug::LLDB::get().start(*run_arguments, *project_path, breakpoints, startup_commands, remote_host); + else if(auto view = Notebook::get().get_current_view()) + view->get_buffer()->place_cursor(view->get_buffer()->get_insert()->get_iter()); + }); }); + on_event_it = std::prev(Debug::LLDB::get().on_event.end()); + + std::vector startup_commands; + if(dynamic_cast(self->build.get())) { + std::stringstream istream, ostream; + if(Terminal::get().process(istream, ostream, "rustc --print sysroot") == 0) { + auto sysroot = ostream.str(); + while(!sysroot.empty() && (sysroot.back() == '\n' || sysroot.back() == '\r')) + sysroot.pop_back(); + startup_commands.emplace_back("command script import \"" + sysroot + "/lib/rustlib/etc/lldb_rust_formatters.py\""); + startup_commands.emplace_back("type summary add --no-value --python-function lldb_rust_formatters.print_val -x \".*\" --category Rust"); + startup_commands.emplace_back("type category enable Rust"); + } + } + 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; if(exit_status == 0) { 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( command, "", [command](int exit_status) { 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); } @@ -1001,7 +999,7 @@ void Project::Python::compile_and_run() { Terminal::get().print("\e[2mRunning " + command + "\e[m\n"); 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().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().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()) @@ -1088,7 +1086,7 @@ void Project::Rust::compile_and_run() { compiling = false; if(exit_status == 0) { 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"); }); } }); diff --git a/src/terminal.cpp b/src/terminal.cpp index 748c737..3cc0f12 100644 --- a/src/terminal.cpp +++ b/src/terminal.cpp @@ -337,7 +337,7 @@ std::shared_ptr Terminal::async_process(const std::stri 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(); { LockGuard lock(processes_mutex); @@ -348,10 +348,12 @@ std::shared_ptr Terminal::async_process(const std::stri } } } - if(callback) - callback(exit_status); - }); - exit_status_thread.detach(); + if(callback) { + dispatcher.post([callback = std::move(callback), exit_status] { + callback(exit_status); + }); + } + }).detach(); return process; } diff --git a/src/terminal.hpp b/src/terminal.hpp index a89673b..409cb5f 100644 --- a/src/terminal.hpp +++ b/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(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 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); diff --git a/src/window.cpp b/src/window.cpp index 14fdfa8..e51c222 100644 --- a/src/window.cpp +++ b/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_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) diff --git a/tests/terminal_test.cpp b/tests/terminal_test.cpp index 72e829a..d0e367f 100644 --- a/tests/terminal_test.cpp +++ b/tests/terminal_test.cpp @@ -281,68 +281,68 @@ int main() { // async_process tests { terminal.clear(); - std::promise done; - terminal.async_process("echo test", "", [&done](int exit_status) { - done.set_value(exit_status); + boost::optional exit_status; + terminal.async_process("echo test", "", [&exit_status](int exit_status_) { + exit_status = exit_status_; }); - auto future = done.get_future(); - while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + while(!exit_status) { while(Gtk::Main::events_pending()) 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->begin().starts_tag(terminal.bold_tag)); assert(!buffer->end().ends_tag(terminal.bold_tag)); } { terminal.clear(); - std::promise done; + boost::optional exit_status; terminal.async_process( - "echo test", "", [&done](int exit_status) { - done.set_value(exit_status); + "echo test", "", [&exit_status](int exit_status_) { + exit_status = exit_status_; }, true); - auto future = done.get_future(); - while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + while(!exit_status) { while(Gtk::Main::events_pending()) Gtk::Main::iteration(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } - assert(future.get() == 0); + assert(exit_status == 0); assert(buffer->get_text() == ""); } { terminal.clear(); - std::promise done; - terminal.async_process("testing_invalid_command", "", [&done](int exit_status) { - done.set_value(exit_status); + boost::optional exit_status; + terminal.async_process("testing_invalid_command", "", [&exit_status](int exit_status_) { + exit_status = exit_status_; }); - auto future = done.get_future(); - while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + while(!exit_status) { while(Gtk::Main::events_pending()) 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->end().ends_tag(terminal.bold_tag)); assert(buffer->get_text() != ""); } { terminal.clear(); - std::promise done; + boost::optional exit_status; terminal.async_process( - "testing_invalid_command", "", [&done](int exit_status) { - done.set_value(exit_status); + "testing_invalid_command", "", [&exit_status](int exit_status_) { + exit_status = exit_status_; }, true); - auto future = done.get_future(); - while(future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { + while(!exit_status) { while(Gtk::Main::events_pending()) Gtk::Main::iteration(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } - assert(future.get() != 0); + assert(exit_status != 0); assert(buffer->get_text() == ""); }