Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 // | |
| 5 // Creates an anonymous pipe for communicating an exit code from an operation | |
| 6 // implemented by one or more subprocesses. If the original subprocess exits | |
| 7 // without writing its result and without duplicating its output HANDLE into | |
| 8 // some other process a failure is assumed to have occurred. Otherwise the | |
| 9 // client closes its copy of the output HANDLE and the operation is considered | |
| 10 // to be in progress until either its result has been read or no more valid | |
| 11 // output HANDLEs remain. | |
| 12 | |
| 13 #include "operation_launcher.h" | |
| 14 | |
| 15 #include "base/command_line.h" | |
| 16 #include "base/compiler_specific.h" | |
| 17 #include "base/logging.h" | |
| 18 #include "base/process_util.h" | |
| 19 #include "base/strings/string_number_conversions.h" | |
| 20 #include "base/synchronization/lock.h" | |
| 21 #include "base/threading/platform_thread.h" | |
| 22 #include "base/win/scoped_handle.h" | |
| 23 #include "chrome/common/chrome_switches.h" | |
| 24 | |
| 25 namespace app_host { | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 // Implements a background thread to read the exit code of the child process. | |
| 30 class PipeReader : public base::PlatformThread::Delegate { | |
|
gab
2013/03/28 03:06:06
**I have no idea what I'm doing** (never wrote a b
gab
2013/03/28 03:06:06
PipeReader sounds a bit too generic for a class wi
erikwright (departed)
2013/04/18 17:43:04
That's a good warning, but all classes exist to be
| |
| 31 public: | |
| 32 PipeReader(base::win::ScopedHandle exit_code_read) | |
|
gab
2013/03/28 03:06:06
"read" feels like the past-tense of "read" (aaah E
| |
| 33 : success_(false), exit_code_(0) { | |
| 34 exit_code_read_ = exit_code_read.Pass(); | |
|
gab
2013/03/28 03:06:06
Why can't this also be done in the initializer lis
erikwright (departed)
2013/04/18 17:43:04
It doesn't compile. I forget the exact reason why,
| |
| 35 } | |
| 36 | |
| 37 virtual void ThreadMain() OVERRIDE { | |
| 38 DWORD bytes_read = 0; | |
| 39 BOOL read_success = ::ReadFile( | |
| 40 exit_code_read_, &exit_code_, sizeof(exit_code_), &bytes_read, NULL); | |
| 41 if (!read_success || bytes_read != sizeof(exit_code_)) { | |
| 42 LOG(ERROR) << "Failed to read exit code."; | |
| 43 } else { | |
| 44 success_ = true; | |
| 45 } | |
| 46 } | |
| 47 | |
| 48 // Only call these accessors after the PipeReader thread has completed. | |
| 49 | |
| 50 DWORD exit_code() { | |
| 51 return exit_code_; | |
|
gab
2013/03/28 03:06:06
Such simple getters are usually written on a singl
erikwright (departed)
2013/04/18 17:43:04
Done.
| |
| 52 } | |
| 53 | |
| 54 bool success() { | |
| 55 return success_; | |
| 56 } | |
| 57 | |
| 58 private: | |
| 59 bool success_; | |
| 60 DWORD exit_code_; | |
| 61 | |
| 62 base::win::ScopedHandle exit_code_read_; | |
| 63 }; | |
| 64 | |
| 65 } // namespace | |
| 66 | |
| 67 bool LaunchOperation(const CommandLine& command_line, | |
| 68 HANDLE output_write, | |
| 69 DWORD* exit_code) { | |
| 70 base::win::ScopedHandle exit_code_read; | |
| 71 base::win::ScopedHandle exit_code_write; | |
|
gab
2013/03/28 03:06:06
How about exit_code_in/exit_code_out (see comment
| |
| 72 | |
| 73 if (::CreatePipe(exit_code_read.Receive(), exit_code_write.Receive(), | |
| 74 NULL, 0) == 0) { | |
|
robertshield
2013/03/28 02:38:03
I'm curious: how does this work? I had understood
erikwright (departed)
2013/03/28 02:49:53
They are not inherited. Making them inherited woul
gab
2013/03/28 03:06:06
How about using '!' instead of '== 0' check? Feels
robertshield
2013/03/28 03:39:34
Ah, sorry should have read the other end first ;-)
erikwright (departed)
2013/04/18 17:43:04
I would defer to a senior Windows developer on thi
| |
| 75 PLOG(ERROR) << "CreatePipe failed."; | |
|
gab
2013/03/28 03:06:06
DPLOG (avoids shipping a ton of log strings in the
erikwright (departed)
2013/04/18 17:43:04
Done.
| |
| 76 return false; | |
| 77 } | |
| 78 | |
| 79 CommandLine full_command_line(command_line); | |
| 80 full_command_line.AppendSwitchASCII( | |
| 81 switches::kTaskOutputHandle, | |
| 82 base::UintToString(reinterpret_cast<int>(output_write))); | |
| 83 full_command_line.AppendSwitchASCII( | |
| 84 switches::kTaskResultHandle, | |
| 85 base::UintToString(reinterpret_cast<int>(exit_code_write.Get()))); | |
| 86 full_command_line.AppendSwitchASCII( | |
| 87 switches::kTaskRemoteProcessId, | |
| 88 base::UintToString(::GetCurrentProcessId())); | |
| 89 | |
| 90 base::win::ScopedHandle child_process; | |
| 91 base::win::ScopedHandle background_thread; | |
| 92 | |
| 93 bool launch_result = base::LaunchProcess(full_command_line, | |
| 94 base::LaunchOptions(), | |
| 95 child_process.Receive()); | |
| 96 if (!launch_result) { | |
| 97 LOG(INFO) << "Failed to execute " | |
| 98 << full_command_line.GetCommandLineString(); | |
| 99 return false; | |
| 100 } | |
| 101 | |
| 102 LOG(INFO) << "Executed " << full_command_line.GetCommandLineString(); | |
| 103 | |
| 104 PipeReader pipe_reader(exit_code_read.Pass()); | |
| 105 base::PlatformThread::Create(0, &pipe_reader, background_thread.Receive()); | |
| 106 | |
| 107 HANDLE handles[] = {child_process.Get(), background_thread.Get()}; | |
| 108 DWORD wait_result = WaitForMultipleObjects(2, handles, FALSE, INFINITE); | |
| 109 switch (wait_result) { | |
| 110 case WAIT_FAILED: | |
| 111 LOG(ERROR) << "Wait for operation failed."; | |
| 112 break; | |
| 113 case WAIT_OBJECT_0: | |
| 114 // The child process exited. If the operation is still in progress | |
| 115 // (delegated to a tertiary process) that process is required to have | |
| 116 // duplicated the write handles by now; the closing of all outstanding | |
| 117 // handles to exit_code_write will be our signal that the operation has | |
| 118 // completed. | |
| 119 exit_code_write.Close(); | |
|
robertshield
2013/03/28 02:38:03
Why do we Close() here, rather than doing so when
erikwright (departed)
2013/03/28 02:49:53
We will detect the termination of the operation wh
| |
| 120 break; | |
| 121 case WAIT_OBJECT_0 + 1: | |
| 122 // The reader thread has completed. We have read (or permanently failed to | |
| 123 // read) the operation exit code. The child process may or may not exit at | |
| 124 // this point, but we already have everything we need. | |
| 125 break; | |
| 126 default: | |
| 127 NOTREACHED(); | |
| 128 return false; | |
| 129 break; | |
|
gab
2013/03/28 03:06:06
Remove break;, already preceded by a return statem
erikwright (departed)
2013/04/18 17:43:04
Done.
| |
| 130 } | |
| 131 | |
| 132 // Whether or not the thread is still running, allow Join to do whatever | |
| 133 // cleanup is required. | |
| 134 base::PlatformThread::Join(background_thread.Take()); | |
| 135 | |
| 136 if (pipe_reader.success()) | |
| 137 *exit_code = pipe_reader.exit_code(); | |
| 138 return pipe_reader.success(); | |
| 139 } | |
| 140 | |
| 141 } // namespace app_host | |
| OLD | NEW |