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 |