diff options
author | Peter Collingbourne <peter@pcc.me.uk> | 2011-11-13 05:49:16 (GMT) |
---|---|---|
committer | Peter Collingbourne <peter@pcc.me.uk> | 2012-02-04 21:46:12 (GMT) |
commit | 85ff781fa30fff63c01ccd30faaad39d766e1505 (patch) | |
tree | dc5791da4769c61951735e84febcccfa8acf98d2 /src/subprocess-win32.cc | |
parent | b07e183e0eb6225e34a3d592e3dff63bcf00df81 (diff) | |
download | Ninja-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.cc | 63 |
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(); +} |