michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "chrome/common/process_watcher.h" michael@0: michael@0: #include "base/message_loop.h" michael@0: #include "base/object_watcher.h" michael@0: #include "base/sys_info.h" michael@0: #include "chrome/common/env_vars.h" michael@0: #include "chrome/common/result_codes.h" michael@0: michael@0: // Maximum amount of time (in milliseconds) to wait for the process to exit. michael@0: static const int kWaitInterval = 2000; michael@0: michael@0: namespace { michael@0: michael@0: class TimerExpiredTask : public Task, public base::ObjectWatcher::Delegate { michael@0: public: michael@0: explicit TimerExpiredTask(base::ProcessHandle process) : process_(process) { michael@0: watcher_.StartWatching(process_, this); michael@0: } michael@0: michael@0: virtual ~TimerExpiredTask() { michael@0: if (process_) { michael@0: KillProcess(); michael@0: DCHECK(!process_) << "Make sure to close the handle."; michael@0: } michael@0: } michael@0: michael@0: // Task --------------------------------------------------------------------- michael@0: michael@0: virtual void Run() { michael@0: if (process_) michael@0: KillProcess(); michael@0: } michael@0: michael@0: // MessageLoop::Watcher ----------------------------------------------------- michael@0: michael@0: virtual void OnObjectSignaled(HANDLE object) { michael@0: // When we're called from KillProcess, the ObjectWatcher may still be michael@0: // watching. the process handle, so make sure it has stopped. michael@0: watcher_.StopWatching(); michael@0: michael@0: base::CloseProcessHandle(process_); michael@0: process_ = NULL; michael@0: } michael@0: michael@0: private: michael@0: void KillProcess() { michael@0: if (base::SysInfo::HasEnvVar(env_vars::kHeadless)) { michael@0: // If running the distributed tests, give the renderer a little time michael@0: // to figure out that the channel is shutdown and unwind. michael@0: if (WaitForSingleObject(process_, kWaitInterval) == WAIT_OBJECT_0) { michael@0: OnObjectSignaled(process_); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // OK, time to get frisky. We don't actually care when the process michael@0: // terminates. We just care that it eventually terminates, and that's what michael@0: // TerminateProcess should do for us. Don't check for the result code since michael@0: // it fails quite often. This should be investigated eventually. michael@0: TerminateProcess(process_, ResultCodes::HUNG); michael@0: michael@0: // Now, just cleanup as if the process exited normally. michael@0: OnObjectSignaled(process_); michael@0: } michael@0: michael@0: // The process that we are watching. michael@0: base::ProcessHandle process_; michael@0: michael@0: base::ObjectWatcher watcher_; michael@0: michael@0: DISALLOW_EVIL_CONSTRUCTORS(TimerExpiredTask); michael@0: }; michael@0: michael@0: } // namespace michael@0: michael@0: // static michael@0: void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process michael@0: , bool force michael@0: ) { michael@0: DCHECK(process != GetCurrentProcess()); michael@0: michael@0: if (!force) { michael@0: WaitForSingleObject(process, INFINITE); michael@0: base::CloseProcessHandle(process); michael@0: return; michael@0: } michael@0: michael@0: // If already signaled, then we are done! michael@0: if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) { michael@0: base::CloseProcessHandle(process); michael@0: return; michael@0: } michael@0: michael@0: MessageLoop::current()->PostDelayedTask(FROM_HERE, michael@0: new TimerExpiredTask(process), michael@0: kWaitInterval); michael@0: }