Browse Source

Fixed terminal.*. Now kill works without leaving zombies from time to time.

merge-requests/365/head
eidheim 10 years ago
parent
commit
f9416ff975
  1. 109
      src/terminal.cc
  2. 11
      src/terminal.h
  3. 4
      src/window.cc

109
src/terminal.cc

@ -9,25 +9,25 @@
#include <iostream> //TODO: remove #include <iostream> //TODO: remove
using namespace std; //TODO: remove using namespace std; //TODO: remove
#define READ 0
#define WRITE 1
int execute_status=-1; int execute_status=-1;
std::mutex async_and_sync_execute_mutex; std::mutex async_and_sync_execute_mutex;
std::unordered_map<pid_t, std::pair<int, int> > async_execute_descriptors; std::unordered_map<pid_t, std::vector<int> > async_execute_descriptors;
std::unordered_map<pid_t, int> async_execute_status; std::unordered_map<pid_t, int> async_execute_status;
//TODO: Windows... //TODO: Windows...
//Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction //Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction
void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) {
async_and_sync_execute_mutex.lock(); async_and_sync_execute_mutex.lock();
if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) {
close(async_execute_descriptors.at(siginfo->si_pid)[0]);
close(async_execute_descriptors.at(siginfo->si_pid)[1]);
close(async_execute_descriptors.at(siginfo->si_pid)[2]);
}
int status; int status;
while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {} while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {}
if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) { if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) {
async_execute_status[siginfo->si_pid]=status; async_execute_status[siginfo->si_pid]=status;
close(async_execute_descriptors.at(siginfo->si_pid).first);
close(async_execute_descriptors.at(siginfo->si_pid).second);
async_execute_descriptors.erase(siginfo->si_pid); async_execute_descriptors.erase(siginfo->si_pid);
} }
else else
@ -36,12 +36,13 @@ void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) {
} }
//TODO: Windows... //TODO: Windows...
//Copied from http://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2 //TODO: Someone who knows this stuff see if I have done something stupid.
pid_t popen2(const char *command, int *input_descriptor, int *output_descriptor) { //Copied partially from http://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2
pid_t popen3(const char *command, int *input_descriptor, int *output_descriptor, int *error_descriptor) {
pid_t pid; pid_t pid;
int p_stdin[2], p_stdout[2]; int p_stdin[2], p_stdout[2], p_stderr[2];
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0)
return -1; return -1;
pid = fork(); pid = fork();
@ -49,10 +50,12 @@ pid_t popen2(const char *command, int *input_descriptor, int *output_descriptor)
if (pid < 0) if (pid < 0)
return pid; return pid;
else if (pid == 0) { else if (pid == 0) {
close(p_stdin[WRITE]); close(p_stdin[1]);
dup2(p_stdin[READ], READ); dup2(p_stdin[0], 0);
close(p_stdout[READ]); close(p_stdout[0]);
dup2(p_stdout[WRITE], WRITE); dup2(p_stdout[1], 1);
close(p_stderr[0]);
dup2(p_stderr[1], 2);
execl("/bin/sh", "sh", "-c", command, NULL); execl("/bin/sh", "sh", "-c", command, NULL);
perror("execl"); perror("execl");
@ -60,14 +63,19 @@ pid_t popen2(const char *command, int *input_descriptor, int *output_descriptor)
} }
if (input_descriptor == NULL) if (input_descriptor == NULL)
close(p_stdin[WRITE]); close(p_stdin[1]);
else else
*input_descriptor = p_stdin[WRITE]; *input_descriptor = p_stdin[1];
if (output_descriptor == NULL) if (output_descriptor == NULL)
close(p_stdout[READ]); close(p_stdout[0]);
else
*output_descriptor = p_stdout[0];
if (error_descriptor == NULL)
close(p_stderr[0]);
else else
*output_descriptor = p_stdout[READ]; *error_descriptor = p_stderr[0];
return pid; return pid;
} }
@ -123,13 +131,16 @@ Terminal::Terminal() {
text_view.get_buffer()->delete_mark(end); text_view.get_buffer()->delete_mark(end);
}); });
bold_tag=text_view.get_buffer()->create_tag();
bold_tag->property_weight()=PANGO_WEIGHT_BOLD;
async_print_dispatcher.connect([this](){ async_print_dispatcher.connect([this](){
async_print_string_mutex.lock(); async_print_strings_mutex.lock();
if(async_print_string.size()>0) { if(async_print_strings.size()>0) {
print(async_print_string); for(auto &string_bold: async_print_strings)
async_print_string=""; print(string_bold.first, string_bold.second);
async_print_strings.clear();
} }
async_print_string_mutex.unlock(); async_print_strings_mutex.unlock();
}); });
//Coppied from http://www.linuxprogrammingblog.com/code-examples/sigaction //Coppied from http://www.linuxprogrammingblog.com/code-examples/sigaction
@ -182,19 +193,30 @@ void Terminal::async_execute(const std::string &command, const std::string &path
boost_path=boost::filesystem::path(path); boost_path=boost::filesystem::path(path);
//TODO: Windows... //TODO: Windows...
cd_path_and_command="cd "+boost_path.string()+" 2>&1 && exec "+command; cd_path_and_command="cd "+boost_path.string()+" && exec "+command;
} }
else else
cd_path_and_command=command; cd_path_and_command=command;
int input_descriptor, output_descriptor; int input_descriptor, output_descriptor, error_descriptor;
async_and_sync_execute_mutex.lock(); async_and_sync_execute_mutex.lock();
auto pid=popen2(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor); auto pid=popen3(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor, &error_descriptor);
async_execute_descriptors[pid]={input_descriptor, output_descriptor}; async_execute_descriptors[pid]={input_descriptor, output_descriptor, error_descriptor};
async_and_sync_execute_mutex.unlock(); async_and_sync_execute_mutex.unlock();
if (pid<=0) if (pid<=0)
async_print("Error: Failed to run command" + command + "\n"); async_print("Error: Failed to run command" + command + "\n");
else { else {
std::thread error_thread([this, error_descriptor](){
char buffer[1024];
ssize_t n;
while ((n=read(error_descriptor, buffer, 1024)) > 0) {
std::string message;
for(int c=0;c<n;c++)
message+=buffer[c];
async_print(message, true);
}
});
error_thread.detach();
char buffer[1024]; char buffer[1024];
ssize_t n; ssize_t n;
while ((n=read(output_descriptor, buffer, 1024)) > 0) { while ((n=read(output_descriptor, buffer, 1024)) > 0) {
@ -206,6 +228,8 @@ void Terminal::async_execute(const std::string &command, const std::string &path
async_and_sync_execute_mutex.lock(); async_and_sync_execute_mutex.lock();
int exit_code=async_execute_status.at(pid); int exit_code=async_execute_status.at(pid);
async_execute_status.erase(pid); async_execute_status.erase(pid);
if(async_execute_descriptors.find(pid)!=async_execute_descriptors.end()) //cleanup in case signal_execl_exit is not called
async_execute_descriptors.erase(pid);
async_and_sync_execute_mutex.unlock(); async_and_sync_execute_mutex.unlock();
if(callback) if(callback)
callback(exit_code); callback(exit_code);
@ -216,22 +240,35 @@ void Terminal::async_execute(const std::string &command, const std::string &path
void Terminal::kill_executing() { void Terminal::kill_executing() {
async_and_sync_execute_mutex.lock(); async_and_sync_execute_mutex.lock();
for(auto &pid: async_execute_descriptors) for(auto &pid: async_execute_descriptors) {
kill(pid.first, SIGTERM); kill(pid.first, SIGTERM); //signal_execl_exit is not always called after kill! Hence the following lines:
close(async_execute_descriptors.at(pid.first)[0]);
close(async_execute_descriptors.at(pid.first)[1]);
close(async_execute_descriptors.at(pid.first)[2]);
int status;
while (waitpid(pid.first, &status, WNOHANG) > 0) {}
async_execute_status[pid.first]=status;
}
async_and_sync_execute_mutex.unlock(); async_and_sync_execute_mutex.unlock();
} }
int Terminal::print(const std::string &message){ int Terminal::print(const std::string &message, bool bold){
INFO("Terminal: PrintMessage"); INFO("Terminal: PrintMessage");
if(bold)
text_view.get_buffer()->insert_with_tag(text_view.get_buffer()->end(), message, bold_tag);
else
text_view.get_buffer()->insert(text_view.get_buffer()->end(), message); text_view.get_buffer()->insert(text_view.get_buffer()->end(), message);
return text_view.get_buffer()->end().get_line(); return text_view.get_buffer()->end().get_line();
} }
void Terminal::print(int line_nr, const std::string &message){ void Terminal::print(int line_nr, const std::string &message, bool bold){
INFO("Terminal: PrintMessage at line " << line_nr); INFO("Terminal: PrintMessage at line " << line_nr);
auto iter=text_view.get_buffer()->get_iter_at_line(line_nr); auto iter=text_view.get_buffer()->get_iter_at_line(line_nr);
while(!iter.ends_line()) while(!iter.ends_line())
iter++; iter++;
if(bold)
text_view.get_buffer()->insert_with_tag(iter, message, bold_tag);
else
text_view.get_buffer()->insert(iter, message); text_view.get_buffer()->insert(iter, message);
} }
@ -240,13 +277,13 @@ std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string st
return in_progress; return in_progress;
} }
void Terminal::async_print(const std::string &message) { void Terminal::async_print(const std::string &message, bool bold) {
async_print_string_mutex.lock(); async_print_strings_mutex.lock();
bool dispatch=true; bool dispatch=true;
if(async_print_string.size()>0) if(async_print_strings.size()>0)
dispatch=false; dispatch=false;
async_print_string+=message; async_print_strings.emplace_back(message, bold);
async_print_string_mutex.unlock(); async_print_strings_mutex.unlock();
if(dispatch) if(dispatch)
async_print_dispatcher(); async_print_dispatcher();
} }

11
src/terminal.h

@ -29,17 +29,18 @@ public:
void async_execute(const std::string &command, const std::string &path="", std::function<void(int exit_code)> callback=nullptr); void async_execute(const std::string &command, const std::string &path="", std::function<void(int exit_code)> callback=nullptr);
void kill_executing(); void kill_executing();
int print(const std::string &message); int print(const std::string &message, bool bold=false);
void print(int line_nr, const std::string &message); void print(int line_nr, const std::string &message, bool bold=false);
std::shared_ptr<InProgress> print_in_progress(std::string start_msg); std::shared_ptr<InProgress> print_in_progress(std::string start_msg);
void async_print(const std::string &message); void async_print(const std::string &message, bool bold=false);
private: private:
Gtk::TextView text_view; Gtk::TextView text_view;
Gtk::ScrolledWindow scrolled_window; Gtk::ScrolledWindow scrolled_window;
Glib::Dispatcher async_print_dispatcher; Glib::Dispatcher async_print_dispatcher;
std::string async_print_string; std::vector<std::pair<std::string, bool> > async_print_strings;
std::mutex async_print_string_mutex; std::mutex async_print_strings_mutex;
Glib::RefPtr<Gtk::TextTag> bold_tag;
}; };
#endif // JUCI_TERMINAL_H_ #endif // JUCI_TERMINAL_H_

4
src/window.cc

@ -216,12 +216,12 @@ void Window::create_menu() {
compiling=true; compiling=true;
Singleton::terminal()->print("Compiling and executing "+path.string()+"\n"); Singleton::terminal()->print("Compiling and executing "+path.string()+"\n");
//TODO: Windows... //TODO: Windows...
Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](int exit_code){ Singleton::terminal()->async_execute("make", cmake.project_path.string(), [this, path](int exit_code){
compiling=false; compiling=false;
if(exit_code==EXIT_SUCCESS) { if(exit_code==EXIT_SUCCESS) {
compile_success(); compile_success();
//TODO: Windows... //TODO: Windows...
Singleton::terminal()->async_execute(path.string()+" 2>&1", path.parent_path().string(), [this, path](int exit_code){ Singleton::terminal()->async_execute(path.string(), path.parent_path().string(), [this, path](int exit_code){
Singleton::terminal()->async_print(path.string()+" returned: "+std::to_string(exit_code)+'\n'); Singleton::terminal()->async_print(path.string()+" returned: "+std::to_string(exit_code)+'\n');
}); });
} }

Loading…
Cancel
Save