| Index: apps/app_host/operation_launcher.cc
|
| diff --git a/apps/app_host/operation_launcher.cc b/apps/app_host/operation_launcher.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5493917b6d5c1c3b767a708ba404a90d3b015d89
|
| --- /dev/null
|
| +++ b/apps/app_host/operation_launcher.cc
|
| @@ -0,0 +1,135 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +//
|
| +// Creates an anonymous pipe for communicating an exit code from an operation
|
| +// implemented by one or more subprocesses. If the original subprocess exits
|
| +// without writing its result and without duplicating its output HANDLE into
|
| +// some other process a failure is assumed to have occurred. Otherwise the
|
| +// client closes its copy of the output HANDLE and the operation is considered
|
| +// to be in progress until either its result has been read or no more valid
|
| +// output HANDLEs remain.
|
| +
|
| +#include "operation_launcher.h"
|
| +
|
| +#include "base/command_line.h"
|
| +#include "base/compiler_specific.h"
|
| +#include "base/logging.h"
|
| +#include "base/process_util.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/synchronization/lock.h"
|
| +#include "base/threading/platform_thread.h"
|
| +#include "base/win/scoped_handle.h"
|
| +#include "chrome/common/chrome_switches.h"
|
| +
|
| +namespace app_host {
|
| +
|
| +namespace {
|
| +
|
| +// Implements a background thread to read the exit code of the child process.
|
| +class ExitCodeReader : public base::PlatformThread::Delegate {
|
| + public:
|
| + ExitCodeReader(base::win::ScopedHandle exit_code_read)
|
| + : success_(false), exit_code_(0) {
|
| + exit_code_read_ = exit_code_read.Pass();
|
| + }
|
| +
|
| + virtual void ThreadMain() OVERRIDE {
|
| + DWORD bytes_read = 0;
|
| + BOOL read_success = ::ReadFile(
|
| + exit_code_read_, &exit_code_, sizeof(exit_code_), &bytes_read, NULL);
|
| + if (!read_success || bytes_read != sizeof(exit_code_)) {
|
| + DLOG(ERROR) << "Failed to read exit code.";
|
| + } else {
|
| + success_ = true;
|
| + }
|
| + }
|
| +
|
| + // Only call these accessors after the ExitCodeReader thread has completed.
|
| + DWORD exit_code() { return exit_code_; }
|
| + bool success() { return success_; }
|
| +
|
| + private:
|
| + bool success_;
|
| + DWORD exit_code_;
|
| +
|
| + base::win::ScopedHandle exit_code_read_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +bool LaunchOperation(const CommandLine& command_line,
|
| + HANDLE output_write,
|
| + DWORD* exit_code) {
|
| + base::win::ScopedHandle exit_code_read;
|
| + base::win::ScopedHandle exit_code_write;
|
| +
|
| + if (::CreatePipe(exit_code_read.Receive(), exit_code_write.Receive(),
|
| + NULL, 0) == 0) {
|
| + DPLOG(ERROR) << "CreatePipe failed.";
|
| + return false;
|
| + }
|
| +
|
| + CommandLine full_command_line(command_line);
|
| + full_command_line.AppendSwitchASCII(
|
| + switches::kTaskOutputHandle,
|
| + base::UintToString(reinterpret_cast<int>(output_write)));
|
| + full_command_line.AppendSwitchASCII(
|
| + switches::kTaskResultHandle,
|
| + base::UintToString(reinterpret_cast<int>(exit_code_write.Get())));
|
| + full_command_line.AppendSwitchASCII(
|
| + switches::kTaskRemoteProcessId,
|
| + base::UintToString(::GetCurrentProcessId()));
|
| +
|
| + base::win::ScopedHandle child_process;
|
| + base::win::ScopedHandle background_thread;
|
| +
|
| + bool launch_result = base::LaunchProcess(full_command_line,
|
| + base::LaunchOptions(),
|
| + child_process.Receive());
|
| + if (!launch_result) {
|
| + DLOG(INFO) << "Failed to execute "
|
| + << full_command_line.GetCommandLineString();
|
| + return false;
|
| + }
|
| +
|
| + DLOG(INFO) << "Executed " << full_command_line.GetCommandLineString();
|
| +
|
| + ExitCodeReader exit_code_reader(exit_code_read.Pass());
|
| + base::PlatformThread::Create(
|
| + 0, &exit_code_reader, background_thread.Receive());
|
| +
|
| + HANDLE handles[] = {child_process.Get(), background_thread.Get()};
|
| + DWORD wait_result = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
|
| + switch (wait_result) {
|
| + case WAIT_FAILED:
|
| + DLOG(ERROR) << "Wait for operation failed.";
|
| + break;
|
| + case WAIT_OBJECT_0:
|
| + // The child process exited. If the operation is still in progress
|
| + // (delegated to a tertiary process) that process is required to have
|
| + // duplicated the write handles by now; the closing of all outstanding
|
| + // handles to exit_code_write will be our signal that the operation has
|
| + // completed.
|
| + exit_code_write.Close();
|
| + break;
|
| + case WAIT_OBJECT_0 + 1:
|
| + // The reader thread has completed. We have read (or permanently failed to
|
| + // read) the operation exit code. The child process may or may not exit at
|
| + // this point, but we already have everything we need.
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| +
|
| + // Whether or not the thread is still running, allow Join to do whatever
|
| + // cleanup is required.
|
| + base::PlatformThread::Join(background_thread.Take());
|
| +
|
| + if (exit_code_reader.success())
|
| + *exit_code = exit_code_reader.exit_code();
|
| + return exit_code_reader.success();
|
| +}
|
| +
|
| +} // namespace app_host
|
|
|