Browse Source

More robust process functions.

merge-requests/365/head
eidheim 10 years ago
parent
commit
ffc6c3819b
  1. 11
      src/process.cpp
  2. 21
      src/process.hpp
  3. 28
      src/process_unix.cpp
  4. 49
      src/process_win.cpp
  5. 4
      src/terminal.cc

11
src/process.cpp

@ -7,14 +7,17 @@ Process::Process(const std::string &command, const std::string &path,
std::function<void(const char* bytes, size_t n)> read_stdout,
std::function<void(const char* bytes, size_t n)> read_stderr,
bool open_stdin, size_t buffer_size):
read_stdout(read_stdout), read_stderr(read_stderr), open_stdin(open_stdin), buffer_size(buffer_size) {
id=open(command, path);
if(id>0)
closed(true), read_stdout(read_stdout), read_stderr(read_stderr), open_stdin(open_stdin), buffer_size(buffer_size) {
open(command, path);
async_read();
}
Process::~Process() {
close_all();
close_fds();
}
Process::id_type Process::get_id() {
return data.id;
}
bool Process::write(const std::string &data) {

21
src/process.hpp

@ -6,6 +6,7 @@
#include <vector>
#include <mutex>
#include <thread>
#include <mutex>
#ifdef _WIN32
#include <windows.h>
#else
@ -26,6 +27,16 @@ public:
typedef pid_t id_type;
typedef int fd_type;
#endif
private:
class Data {
public:
Data();
id_type id;
#ifdef _WIN32
HANDLE handle;
#endif
};
public:
Process(const std::string &command, const std::string &path=std::string(),
std::function<void(const char *bytes, size_t n)> read_stdout=nullptr,
std::function<void(const char *bytes, size_t n)> read_stderr=nullptr,
@ -34,7 +45,7 @@ public:
~Process();
///Get the process id of the started process.
id_type get_id() {return id;}
id_type get_id();
///Wait until process is finished, and return exit status.
int get_exit_status();
///Write to stdin.
@ -44,10 +55,15 @@ public:
///Close stdin. If the process takes parameters from stdin, use this to notify that all parameters have been sent.
void close_stdin();
///Kill the process.
void kill(bool force=false);
///Kill a given process id.
static void kill(id_type id, bool force=false);
private:
Data data;
bool closed;
std::mutex close_mutex;
std::function<void(const char* bytes, size_t n)> read_stdout;
std::function<void(const char* bytes, size_t n)> read_stderr;
std::thread stdout_thread, stderr_thread;
@ -58,9 +74,8 @@ private:
std::unique_ptr<fd_type> stdout_fd, stderr_fd, stdin_fd;
id_type open(const std::string &command, const std::string &path);
id_type id;
void async_read();
void close_all();
void close_fds();
};
#endif // TINY_PROCESS_LIBRARY_HPP_

28
src/process_unix.cpp

@ -6,6 +6,8 @@
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
Process::Data::Data(): id(-1) {}
Process::id_type Process::open(const std::string &command, const std::string &path) {
if(open_stdin)
stdin_fd=std::unique_ptr<fd_type>(new fd_type);
@ -78,10 +80,14 @@ Process::id_type Process::open(const std::string &command, const std::string &pa
if(stdout_fd) *stdout_fd = stdout_p[0];
if(stderr_fd) *stderr_fd = stderr_p[0];
closed=false;
data.id=pid;
return pid;
}
void Process::async_read() {
if(data.id<=0)
return;
if(stdout_fd) {
stdout_thread=std::thread([this](){
char buffer[buffer_size];
@ -101,17 +107,20 @@ void Process::async_read() {
}
int Process::get_exit_status() {
if(id<=0)
if(data.id<=0)
return -1;
int exit_status;
waitpid(id, &exit_status, 0);
waitpid(data.id, &exit_status, 0);
close_mutex.lock();
closed=true;
close_mutex.unlock();
close_all();
close_fds();
return exit_status;
}
void Process::close_all() {
void Process::close_fds() {
if(stdout_thread.joinable())
stdout_thread.join();
if(stderr_thread.joinable())
@ -154,6 +163,17 @@ void Process::close_stdin() {
stdin_mutex.unlock();
}
void Process::kill(bool force) {
close_mutex.lock();
if(data.id>0 && !closed) {
if(force)
::kill(-data.id, SIGTERM);
else
::kill(-data.id, SIGINT);
}
close_mutex.unlock();
}
void Process::kill(id_type id, bool force) {
if(id<=0)
return;

49
src/process_win.cpp

@ -5,6 +5,8 @@
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
Process::Data::Data(): id(0), handle(NULL) {}
//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx.
//Note: on Windows it seems impossible to specify which pipes to use.
//Thus, if read_stdout==nullptr, read_stderr==nullptr and open_stdin==false, the stdout, stderr and stdin are sent to the parent process instead.
@ -138,10 +140,15 @@ Process::id_type Process::open(const std::string &command, const std::string &pa
if(stdout_fd) *stdout_fd=g_hChildStd_OUT_Rd;
if(stderr_fd) *stderr_fd=g_hChildStd_ERR_Rd;
closed=false;
data.id=process_info.dwProcessId;
data.handle=process_info.hProcess;
return process_info.dwProcessId;
}
void Process::async_read() {
if(data.id==0)
return;
if(stdout_fd) {
stdout_thread=std::thread([this](){
DWORD n;
@ -169,24 +176,23 @@ void Process::async_read() {
}
int Process::get_exit_status() {
if(id==0)
if(data.id==0)
return -1;
DWORD exit_status;
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
if(process_handle) {
WaitForSingleObject(process_handle, INFINITE);
GetExitCodeProcess(process_handle, &exit_status);
CloseHandle(process_handle);
}
else
WaitForSingleObject(data.handle, INFINITE);
if(!GetExitCodeProcess(data.handle, &exit_status))
exit_status=-1;
close_mutex.lock();
CloseHandle(data.handle);
closed=true;
close_mutex.unlock();
close_all();
close_fds();
return static_cast<int>(exit_status);
}
void Process::close_all() {
void Process::close_fds() {
if(stdout_thread.joinable())
stdout_thread.join();
if(stderr_thread.joinable())
@ -231,6 +237,29 @@ void Process::close_stdin() {
stdin_mutex.unlock();
}
//Based on http://stackoverflow.com/a/1173396
void Process::kill(bool force) {
close_mutex.lock();
if(data.id>0 && !closed) {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(snapshot) {
PROCESSENTRY32 process;
ZeroMemory(&process, sizeof(process));
process.dwSize = sizeof(process);
if(Process32First(snapshot, &process)) {
do {
if(process.th32ParentProcessID==data.id) {
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
if(process_handle) TerminateProcess(process_handle, 2);
}
} while (Process32Next(snapshot, &process));
}
}
TerminateProcess(data.handle, 2);
}
close_mutex.unlock();
}
//Based on http://stackoverflow.com/a/1173396
void Process::kill(id_type id, bool force) {
if(id==0)

4
src/terminal.cc

@ -166,14 +166,14 @@ void Terminal::async_process(const std::string &command, const boost::filesystem
void Terminal::kill_last_async_process(bool force) {
processes_mutex.lock();
if(processes.size()>0)
Process::kill(processes.back()->get_id(), force);
processes.back()->kill(force);
processes_mutex.unlock();
}
void Terminal::kill_async_processes(bool force) {
processes_mutex.lock();
for(auto &process: processes)
Process::kill(process->get_id(), force);
process->kill(force);
processes_mutex.unlock();
}

Loading…
Cancel
Save