summaryrefslogtreecommitdiffstats
path: root/src/subprocess-win32.cc
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2011-11-13 05:49:16 (GMT)
committerPeter Collingbourne <peter@pcc.me.uk>2012-02-04 21:46:12 (GMT)
commit85ff781fa30fff63c01ccd30faaad39d766e1505 (patch)
treedc5791da4769c61951735e84febcccfa8acf98d2 /src/subprocess-win32.cc
parentb07e183e0eb6225e34a3d592e3dff63bcf00df81 (diff)
downloadNinja-85ff781fa30fff63c01ccd30faaad39d766e1505.zip
Ninja-85ff781fa30fff63c01ccd30faaad39d766e1505.tar.gz
Ninja-85ff781fa30fff63c01ccd30faaad39d766e1505.tar.bz2
Implement cleanup-on-interrupt
This causes us to clean up by deleting any output files belonging to currently-running commands before we quit if we are interrupted (either by Ctrl-C or by a command failing). Fixes issue #110.
Diffstat (limited to 'src/subprocess-win32.cc')
-rw-r--r--src/subprocess-win32.cc63
1 files changed, 55 insertions, 8 deletions
diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc
index e757f7e..6b7e942 100644
--- a/src/subprocess-win32.cc
+++ b/src/subprocess-win32.cc
@@ -32,6 +32,10 @@ Subprocess::Subprocess() : child_(NULL) , overlapped_() {
}
Subprocess::~Subprocess() {
+ if (pipe_) {
+ if (!CloseHandle(pipe_))
+ Win32Fatal("CloseHandle");
+ }
// Reap child if forgotten.
if (child_)
Finish();
@@ -92,7 +96,7 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) {
// Do not prepend 'cmd /c' on Windows, this breaks command
// lines greater than 8,191 chars.
if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,
- /* inherit handles */ TRUE, 0,
+ /* inherit handles */ TRUE, CREATE_NEW_PROCESS_GROUP,
NULL, NULL,
&startup_info, &process_info)) {
DWORD error = GetLastError();
@@ -149,10 +153,9 @@ void Subprocess::OnPipeReady() {
// function again later and get them at that point.
}
-bool Subprocess::Finish() {
- if (! child_) {
- return false;
- }
+ExitStatus Subprocess::Finish() {
+ if (!child_)
+ return ExitFailure;
// TODO: add error handling for all of these.
WaitForSingleObject(child_, INFINITE);
@@ -163,7 +166,9 @@ bool Subprocess::Finish() {
CloseHandle(child_);
child_ = NULL;
- return exit_code == 0;
+ return exit_code == 0 ? ExitSuccess :
+ exit_code == CONTROL_C_EXIT ? ExitInterrupted :
+ ExitFailure;
}
bool Subprocess::Done() const {
@@ -174,24 +179,47 @@ const string& Subprocess::GetOutput() const {
return buf_;
}
+HANDLE SubprocessSet::ioport_;
+
SubprocessSet::SubprocessSet() {
ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
if (!ioport_)
Win32Fatal("CreateIoCompletionPort");
+ if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE))
+ Win32Fatal("SetConsoleCtrlHandler");
}
SubprocessSet::~SubprocessSet() {
+ Clear();
+
+ SetConsoleCtrlHandler(NotifyInterrupted, FALSE);
CloseHandle(ioport_);
}
-void SubprocessSet::Add(Subprocess* subprocess) {
+BOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) {
+ if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
+ if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL))
+ Win32Fatal("PostQueuedCompletionStatus");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+Subprocess *SubprocessSet::Add(const string &command) {
+ Subprocess *subprocess = new Subprocess;
+ if (!subprocess->Start(this, command)) {
+ delete subprocess;
+ return 0;
+ }
if (subprocess->child_)
running_.push_back(subprocess);
else
finished_.push(subprocess);
+ return subprocess;
}
-void SubprocessSet::DoWork() {
+bool SubprocessSet::DoWork() {
DWORD bytes_read;
Subprocess* subproc;
OVERLAPPED* overlapped;
@@ -202,6 +230,10 @@ void SubprocessSet::DoWork() {
Win32Fatal("GetQueuedCompletionStatus");
}
+ if (!subproc) // A NULL subproc indicates that we were interrupted and is
+ // delivered by NotifyInterrupted above.
+ return true;
+
subproc->OnPipeReady();
if (subproc->Done()) {
@@ -212,6 +244,8 @@ void SubprocessSet::DoWork() {
running_.resize(end - running_.begin());
}
}
+
+ return false;
}
Subprocess* SubprocessSet::NextFinished() {
@@ -221,3 +255,16 @@ Subprocess* SubprocessSet::NextFinished() {
finished_.pop();
return subproc;
}
+
+void SubprocessSet::Clear() {
+ for (vector<Subprocess*>::iterator i = running_.begin();
+ i != running_.end(); ++i) {
+ if ((*i)->child_)
+ if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, GetProcessId((*i)->child_)))
+ Win32Fatal("GenerateConsoleCtrlEvent");
+ }
+ for (vector<Subprocess*>::iterator i = running_.begin();
+ i != running_.end(); ++i)
+ delete *i;
+ running_.clear();
+}