Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(534)

Side by Side Diff: chromeos/process_proxy/process_proxy.cc

Issue 1258193002: User MessageLoopForIO::WatchFileDescriptor in proces_output_watcher (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chromeos/process_proxy/process_proxy.h ('k') | chromeos/process_proxy/process_proxy_registry.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chromeos/process_proxy/process_proxy.h" 5 #include "chromeos/process_proxy/process_proxy.h"
6 6
7 #include <stdlib.h> 7 #include <stdlib.h>
8 #include <sys/ioctl.h> 8 #include <sys/ioctl.h>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/command_line.h" 11 #include "base/command_line.h"
12 #include "base/files/file_util.h" 12 #include "base/files/file_util.h"
13 #include "base/location.h" 13 #include "base/location.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/posix/eintr_wrapper.h" 15 #include "base/posix/eintr_wrapper.h"
16 #include "base/process/launch.h" 16 #include "base/process/launch.h"
17 #include "base/process/process.h" 17 #include "base/process/process.h"
18 #include "base/single_thread_task_runner.h" 18 #include "base/single_thread_task_runner.h"
19 #include "base/thread_task_runner_handle.h" 19 #include "base/thread_task_runner_handle.h"
20 #include "base/threading/thread.h"
21 #include "third_party/cros_system_api/switches/chrome_switches.h" 20 #include "third_party/cros_system_api/switches/chrome_switches.h"
22 21
23 namespace { 22 namespace {
24 23
25 enum PipeEnd {
26 PIPE_END_READ,
27 PIPE_END_WRITE
28 };
29
30 enum PseudoTerminalFd { 24 enum PseudoTerminalFd {
31 PT_MASTER_FD, 25 PT_MASTER_FD,
32 PT_SLAVE_FD 26 PT_SLAVE_FD
33 }; 27 };
34 28
35 const int kInvalidFd = -1; 29 const int kInvalidFd = -1;
36 30
31 void StopOutputWatcher(scoped_ptr<chromeos::ProcessOutputWatcher> watcher) {
32 // Just deleting |watcher| if sufficient to stop it.
33 }
34
37 } // namespace 35 } // namespace
38 36
39 namespace chromeos { 37 namespace chromeos {
40 38
41 ProcessProxy::ProcessProxy(): process_launched_(false), 39 ProcessProxy::ProcessProxy() : process_launched_(false), callback_set_(false) {
42 callback_set_(false),
43 watcher_started_(false) {
44 // Set pipes to initial, invalid value so we can easily know if a pipe was 40 // Set pipes to initial, invalid value so we can easily know if a pipe was
45 // opened by us. 41 // opened by us.
46 ClearAllFdPairs(); 42 ClearFdPair(pt_pair_);
47 } 43 }
48 44
49 bool ProcessProxy::Open(const std::string& command, pid_t* pid) { 45 bool ProcessProxy::Open(const std::string& command, pid_t* pid) {
50 if (process_launched_) 46 if (process_launched_)
51 return false; 47 return false;
52 48
53 if (!CreatePseudoTerminalPair(pt_pair_)) { 49 if (!CreatePseudoTerminalPair(pt_pair_)) {
54 return false; 50 return false;
55 } 51 }
56 52
57 process_launched_ = LaunchProcess(command, pt_pair_[PT_SLAVE_FD], &pid_); 53 process_launched_ = LaunchProcess(command, pt_pair_[PT_SLAVE_FD], &pid_);
58 54
59 if (process_launched_) { 55 if (process_launched_) {
60 // We won't need these anymore. These will be used by the launched process. 56 // We won't need these anymore. These will be used by the launched process.
61 CloseFd(&pt_pair_[PT_SLAVE_FD]); 57 CloseFd(&pt_pair_[PT_SLAVE_FD]);
62 *pid = pid_; 58 *pid = pid_;
63 LOG(WARNING) << "Process launched: " << pid_; 59 LOG(WARNING) << "Process launched: " << pid_;
64 } else { 60 } else {
65 CloseFdPair(pt_pair_); 61 CloseFdPair(pt_pair_);
66 } 62 }
67 return process_launched_; 63 return process_launched_;
68 } 64 }
69 65
70 bool ProcessProxy::StartWatchingOnThread( 66 bool ProcessProxy::StartWatchingOutput(
71 base::Thread* watch_thread, 67 const scoped_refptr<base::SingleThreadTaskRunner>& watcher_runner,
72 const ProcessOutputCallback& callback) { 68 const ProcessOutputCallback& callback) {
73 DCHECK(process_launched_); 69 DCHECK(process_launched_);
74 if (watcher_started_) 70 CHECK(!output_watcher_.get());
75 return false;
76 if (pipe(shutdown_pipe_) ||
77 !ProcessOutputWatcher::VerifyFileDescriptor(
78 shutdown_pipe_[PIPE_END_READ])) {
79 return false;
80 }
81 71
82 // We give ProcessOutputWatcher a copy of master to make life easier during 72 // We give ProcessOutputWatcher a copy of master to make life easier during
83 // tear down. 73 // tear down.
84 // TODO(tbarzic): improve fd managment. 74 // TODO(tbarzic): improve fd managment.
85 int master_copy = HANDLE_EINTR(dup(pt_pair_[PT_MASTER_FD])); 75 int master_copy = HANDLE_EINTR(dup(pt_pair_[PT_MASTER_FD]));
86 if (!ProcessOutputWatcher::VerifyFileDescriptor(master_copy)) 76 if (master_copy < 0)
87 return false; 77 return false;
88 78
89 callback_set_ = true; 79 callback_set_ = true;
90 callback_ = callback; 80 callback_ = callback;
91 callback_runner_ = base::ThreadTaskRunnerHandle::Get(); 81 callback_runner_ = base::ThreadTaskRunnerHandle::Get();
82 watcher_runner_ = watcher_runner;
92 83
93 // This object will delete itself once watching is stopped. 84 // This object will delete itself once watching is stopped.
94 // It also takes ownership of the passed fds. 85 // It also takes ownership of the passed fds.
95 ProcessOutputWatcher* output_watcher = 86 output_watcher_.reset(new ProcessOutputWatcher(
96 new ProcessOutputWatcher(master_copy, 87 master_copy, base::Bind(&ProcessProxy::OnProcessOutput, this)));
97 shutdown_pipe_[PIPE_END_READ],
98 base::Bind(&ProcessProxy::OnProcessOutput,
99 this));
100 88
101 // Output watcher took ownership of the read end of shutdown pipe. 89 watcher_runner_->PostTask(
102 shutdown_pipe_[PIPE_END_READ] = -1; 90 FROM_HERE, base::Bind(&ProcessOutputWatcher::Start,
91 base::Unretained(output_watcher_.get())));
103 92
104 // |watch| thread is blocked by |output_watcher| from now on.
105 watch_thread->task_runner()->PostTask(
106 FROM_HERE, base::Bind(&ProcessOutputWatcher::Start,
107 base::Unretained(output_watcher)));
108 watcher_started_ = true;
109 return true; 93 return true;
110 } 94 }
111 95
112 void ProcessProxy::OnProcessOutput(ProcessOutputType type, 96 void ProcessProxy::OnProcessOutput(ProcessOutputType type,
113 const std::string& output) { 97 const std::string& output) {
114 if (!callback_runner_.get()) 98 if (!callback_runner_.get())
115 return; 99 return;
116 100
117 callback_runner_->PostTask( 101 callback_runner_->PostTask(
118 FROM_HERE, 102 FROM_HERE,
119 base::Bind(&ProcessProxy::CallOnProcessOutputCallback, 103 base::Bind(&ProcessProxy::CallOnProcessOutputCallback,
120 this, type, output)); 104 this, type, output));
121 } 105 }
122 106
123 void ProcessProxy::CallOnProcessOutputCallback(ProcessOutputType type, 107 void ProcessProxy::CallOnProcessOutputCallback(ProcessOutputType type,
124 const std::string& output) { 108 const std::string& output) {
125 // We may receive some output even after Close was called (crosh process does 109 // We may receive some output even after Close was called (crosh process does
126 // not have to quit instantly, or there may be some trailing data left in 110 // not have to quit instantly, or there may be some trailing data left in
127 // output stream fds). In that case owner of the callback may be gone so we 111 // output stream fds). In that case owner of the callback may be gone so we
128 // don't want to send it anything. |callback_set_| is reset when this gets 112 // don't want to send it anything. |callback_set_| is reset when this gets
129 // closed. 113 // closed.
130 if (callback_set_) 114 if (callback_set_)
131 callback_.Run(type, output); 115 callback_.Run(type, output);
132 } 116 }
133 117
134 bool ProcessProxy::StopWatching() { 118 void ProcessProxy::StopWatching() {
135 if (!watcher_started_) 119 if (!output_watcher_.get())
136 return true; 120 return;
137 // Signal Watcher that we are done. We use self-pipe trick to unblock watcher. 121
138 // Anything may be written to the pipe. 122 watcher_runner_->PostTask(
139 const char message[] = "q"; 123 FROM_HERE,
140 return base::WriteFileDescriptor(shutdown_pipe_[PIPE_END_WRITE], 124 base::Bind(&StopOutputWatcher, base::Passed(&output_watcher_)));
141 message, sizeof(message));
142 } 125 }
143 126
144 void ProcessProxy::Close() { 127 void ProcessProxy::Close() {
145 if (!process_launched_) 128 if (!process_launched_)
146 return; 129 return;
147 130
148 process_launched_ = false; 131 process_launched_ = false;
149 callback_set_ = false; 132 callback_set_ = false;
150 callback_ = ProcessOutputCallback(); 133 callback_ = ProcessOutputCallback();
151 callback_runner_ = NULL; 134 callback_runner_ = NULL;
152 135
153 base::Process process = base::Process::DeprecatedGetProcessFromHandle(pid_); 136 base::Process process = base::Process::DeprecatedGetProcessFromHandle(pid_);
154 process.Terminate(0, true /* wait */); 137 process.Terminate(0, true /* wait */);
155 138
156 // TODO(tbarzic): What if this fails?
157 StopWatching(); 139 StopWatching();
158 140 CloseFdPair(pt_pair_);
159 CloseAllFdPairs();
160 } 141 }
161 142
162 bool ProcessProxy::Write(const std::string& text) { 143 bool ProcessProxy::Write(const std::string& text) {
163 if (!process_launched_) 144 if (!process_launched_)
164 return false; 145 return false;
165 146
166 // We don't want to write '\0' to the pipe. 147 // We don't want to write '\0' to the pipe.
167 size_t data_size = text.length() * sizeof(*text.c_str()); 148 size_t data_size = text.length() * sizeof(*text.c_str());
168 return base::WriteFileDescriptor( 149 return base::WriteFileDescriptor(
169 pt_pair_[PT_MASTER_FD], text.c_str(), data_size); 150 pt_pair_[PT_MASTER_FD], text.c_str(), data_size);
170 } 151 }
171 152
172 bool ProcessProxy::OnTerminalResize(int width, int height) { 153 bool ProcessProxy::OnTerminalResize(int width, int height) {
173 if (width < 0 || height < 0) 154 if (width < 0 || height < 0)
174 return false; 155 return false;
175 156
176 winsize ws; 157 winsize ws;
177 // Number of rows. 158 // Number of rows.
178 ws.ws_row = height; 159 ws.ws_row = height;
179 // Number of columns. 160 // Number of columns.
180 ws.ws_col = width; 161 ws.ws_col = width;
181 162
182 return (HANDLE_EINTR(ioctl(pt_pair_[PT_MASTER_FD], TIOCSWINSZ, &ws)) != -1); 163 return (HANDLE_EINTR(ioctl(pt_pair_[PT_MASTER_FD], TIOCSWINSZ, &ws)) != -1);
183 } 164 }
184 165
185 ProcessProxy::~ProcessProxy() { 166 ProcessProxy::~ProcessProxy() {
186 // In case watcher did not started, we may get deleted without calling Close. 167 Close();
187 // In that case we have to clean up created pipes. If watcher had been
188 // started, there will be a callback with our reference owned by
189 // process_output_watcher until Close is called, so we know Close has been
190 // called by now (and pipes have been cleaned).
191 if (!watcher_started_)
192 CloseAllFdPairs();
193 } 168 }
194 169
195 bool ProcessProxy::CreatePseudoTerminalPair(int *pt_pair) { 170 bool ProcessProxy::CreatePseudoTerminalPair(int *pt_pair) {
196 ClearFdPair(pt_pair); 171 ClearFdPair(pt_pair);
197 172
198 // Open Master. 173 // Open Master.
199 pt_pair[PT_MASTER_FD] = HANDLE_EINTR(posix_openpt(O_RDWR | O_NOCTTY)); 174 pt_pair[PT_MASTER_FD] = HANDLE_EINTR(posix_openpt(O_RDWR | O_NOCTTY));
200 if (pt_pair[PT_MASTER_FD] == -1) 175 if (pt_pair[PT_MASTER_FD] == -1)
201 return false; 176 return false;
202 177
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 base::Process process = 214 base::Process process =
240 base::LaunchProcess(base::CommandLine(base::FilePath(command)), options); 215 base::LaunchProcess(base::CommandLine(base::FilePath(command)), options);
241 216
242 // TODO(rvargas) crbug/417532: This is somewhat wrong but the interface of 217 // TODO(rvargas) crbug/417532: This is somewhat wrong but the interface of
243 // Open vends pid_t* so ownership is quite vague anyway, and Process::Close 218 // Open vends pid_t* so ownership is quite vague anyway, and Process::Close
244 // doesn't do much in POSIX. 219 // doesn't do much in POSIX.
245 *pid = process.Pid(); 220 *pid = process.Pid();
246 return process.IsValid(); 221 return process.IsValid();
247 } 222 }
248 223
249 void ProcessProxy::CloseAllFdPairs() {
250 CloseFdPair(pt_pair_);
251 CloseFdPair(shutdown_pipe_);
252 }
253
254 void ProcessProxy::CloseFdPair(int* pipe) { 224 void ProcessProxy::CloseFdPair(int* pipe) {
255 CloseFd(&(pipe[PIPE_END_READ])); 225 CloseFd(&(pipe[PT_MASTER_FD]));
256 CloseFd(&(pipe[PIPE_END_WRITE])); 226 CloseFd(&(pipe[PT_SLAVE_FD]));
257 } 227 }
258 228
259 void ProcessProxy::CloseFd(int* fd) { 229 void ProcessProxy::CloseFd(int* fd) {
260 if (*fd != kInvalidFd) { 230 if (*fd != kInvalidFd) {
261 if (IGNORE_EINTR(close(*fd)) != 0) 231 if (IGNORE_EINTR(close(*fd)) != 0)
262 DPLOG(WARNING) << "close fd failed."; 232 DPLOG(WARNING) << "close fd failed.";
263 } 233 }
264 *fd = kInvalidFd; 234 *fd = kInvalidFd;
265 } 235 }
266 236
267 void ProcessProxy::ClearAllFdPairs() {
268 ClearFdPair(pt_pair_);
269 ClearFdPair(shutdown_pipe_);
270 }
271
272 void ProcessProxy::ClearFdPair(int* pipe) { 237 void ProcessProxy::ClearFdPair(int* pipe) {
273 pipe[PIPE_END_READ] = kInvalidFd; 238 pipe[PT_MASTER_FD] = kInvalidFd;
274 pipe[PIPE_END_WRITE] = kInvalidFd; 239 pipe[PT_SLAVE_FD] = kInvalidFd;
275 } 240 }
276 241
277 } // namespace chromeos 242 } // namespace chromeos
OLDNEW
« no previous file with comments | « chromeos/process_proxy/process_proxy.h ('k') | chromeos/process_proxy/process_proxy_registry.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698