Revert "Merged all Chromoting Host code into remoting_core.dll (Windows)."
[chromium-blink-merge.git] / remoting / host / win / worker_process_launcher.cc
blob6ca563cf8939e86084b4f42474b1eae250510f3b
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
3 // found in the LICENSE file.
5 #include "remoting/host/win/worker_process_launcher.h"
7 #include "base/logging.h"
8 #include "base/single_thread_task_runner.h"
9 #include "base/time.h"
10 #include "base/timer.h"
11 #include "base/win/object_watcher.h"
12 #include "base/win/windows_version.h"
13 #include "ipc/ipc_listener.h"
14 #include "ipc/ipc_message.h"
15 #include "net/base/backoff_entry.h"
16 #include "remoting/host/worker_process_ipc_delegate.h"
18 using base::TimeDelta;
19 using base::win::ScopedHandle;
21 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
22 // Number of initial errors (in sequence) to ignore before applying
23 // exponential back-off rules.
26 // Initial delay for exponential back-off in ms.
27 1000,
29 // Factor by which the waiting time will be multiplied.
32 // Fuzzing percentage. ex: 10% will spread requests randomly
33 // between 90%-100% of the calculated time.
36 // Maximum amount of time we are willing to delay our request in ms.
37 60000,
39 // Time to keep an entry from being discarded even when it
40 // has no significant state, -1 to never discard.
41 -1,
43 // Don't use initial delay unless the last request was an error.
44 false,
48 namespace remoting {
50 // Launches a worker process that is controlled via an IPC channel. All
51 // interaction with the spawned process is through the IPC::Listener and Send()
52 // method. In case of error the channel is closed and the worker process is
53 // terminated.
54 class WorkerProcessLauncher::Core
55 : public base::RefCountedThreadSafe<WorkerProcessLauncher::Core>,
56 public base::win::ObjectWatcher::Delegate,
57 public IPC::Listener {
58 public:
59 // Creates the launcher that will use |launcher_delegate| to manage the worker
60 // process and |worker_delegate| to handle IPCs. The caller must ensure that
61 // |worker_delegate| remains valid until Stoppable::Stop() method has been
62 // called.
64 // The caller should call all the methods on this class on
65 // the |caller_task_runner| thread. Methods of both delegate interfaces are
66 // called on the |caller_task_runner| thread as well.
67 Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
68 scoped_ptr<WorkerProcessLauncher::Delegate> launcher_delegate,
69 WorkerProcessIpcDelegate* worker_delegate);
71 // Launches the worker process.
72 void Start();
74 // Stops the worker process asynchronously. The caller can drop the reference
75 // to |this| as soon as Stop() returns.
76 void Stop();
78 // Sends an IPC message to the worker process. The message will be silently
79 // dropped if Send() is called before Start() or after stutdown has been
80 // initiated.
81 void Send(IPC::Message* message);
83 // base::win::ObjectWatcher::Delegate implementation.
84 virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
86 // IPC::Listener implementation.
87 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
88 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
89 virtual void OnChannelError() OVERRIDE;
91 private:
92 friend class base::RefCountedThreadSafe<Core>;
93 virtual ~Core();
95 // Attempts to launch the worker process. Schedules next launch attempt if
96 // creation of the process fails.
97 void LaunchWorker();
99 // Records a successful launch attempt.
100 void RecordSuccessfulLaunch();
102 // Stops the worker process asynchronously and schedules next launch attempt
103 // unless Stop() has been called already.
104 void StopWorker();
106 // All public methods are called on the |caller_task_runner| thread.
107 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
109 // Implements specifics of launching a worker process.
110 scoped_ptr<WorkerProcessLauncher::Delegate> launcher_delegate_;
112 // Handles IPC messages sent by the worker process.
113 WorkerProcessIpcDelegate* worker_delegate_;
115 // Pointer to GetNamedPipeClientProcessId() API if it is available.
116 typedef BOOL (WINAPI * GetNamedPipeClientProcessIdFn)(HANDLE, DWORD*);
117 GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_;
119 // True if IPC messages should be passed to |worker_delegate_|.
120 bool ipc_enabled_;
122 // The timer used to delay termination of the process in the case of an IPC
123 // error.
124 scoped_ptr<base::OneShotTimer<Core> > ipc_error_timer_;
126 // Launch backoff state.
127 net::BackoffEntry launch_backoff_;
129 // Timer used to delay recording a successfull launch.
130 scoped_ptr<base::OneShotTimer<Core> > launch_success_timer_;
132 // Timer used to schedule the next attempt to launch the process.
133 scoped_ptr<base::OneShotTimer<Core> > launch_timer_;
135 // Used to determine when the launched process terminates.
136 base::win::ObjectWatcher process_watcher_;
138 // A waiting handle that becomes signalled once the launched process has
139 // been terminated.
140 ScopedHandle process_exit_event_;
142 // True when Stop() has been called.
143 bool stopping_;
145 DISALLOW_COPY_AND_ASSIGN(Core);
148 WorkerProcessLauncher::Delegate::~Delegate() {
151 WorkerProcessLauncher::Core::Core(
152 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
153 scoped_ptr<WorkerProcessLauncher::Delegate> launcher_delegate,
154 WorkerProcessIpcDelegate* worker_delegate)
155 : caller_task_runner_(caller_task_runner),
156 launcher_delegate_(launcher_delegate.Pass()),
157 worker_delegate_(worker_delegate),
158 get_named_pipe_client_pid_(NULL),
159 ipc_enabled_(false),
160 launch_backoff_(&kDefaultBackoffPolicy),
161 stopping_(false) {
162 DCHECK(caller_task_runner_->BelongsToCurrentThread());
164 // base::OneShotTimer must be destroyed on the same thread it was created on.
165 ipc_error_timer_.reset(new base::OneShotTimer<Core>());
166 launch_success_timer_.reset(new base::OneShotTimer<Core>());
167 launch_timer_.reset(new base::OneShotTimer<Core>());
170 void WorkerProcessLauncher::Core::Start() {
171 DCHECK(caller_task_runner_->BelongsToCurrentThread());
172 DCHECK(!stopping_);
174 LaunchWorker();
177 void WorkerProcessLauncher::Core::Stop() {
178 DCHECK(caller_task_runner_->BelongsToCurrentThread());
180 if (!stopping_) {
181 stopping_ = true;
182 worker_delegate_ = NULL;
183 StopWorker();
187 void WorkerProcessLauncher::Core::Send(IPC::Message* message) {
188 DCHECK(caller_task_runner_->BelongsToCurrentThread());
190 if (ipc_enabled_) {
191 launcher_delegate_->Send(message);
192 } else {
193 delete message;
197 void WorkerProcessLauncher::Core::OnObjectSignaled(HANDLE object) {
198 DCHECK(caller_task_runner_->BelongsToCurrentThread());
199 DCHECK(process_watcher_.GetWatchedObject() == NULL);
201 StopWorker();
204 bool WorkerProcessLauncher::Core::OnMessageReceived(
205 const IPC::Message& message) {
206 DCHECK(caller_task_runner_->BelongsToCurrentThread());
208 if (!ipc_enabled_)
209 return false;
211 return worker_delegate_->OnMessageReceived(message);
214 void WorkerProcessLauncher::Core::OnChannelConnected(int32 peer_pid) {
215 DCHECK(caller_task_runner_->BelongsToCurrentThread());
217 if (!ipc_enabled_)
218 return;
220 // Verify |peer_pid| because it is controlled by the client and cannot be
221 // trusted.
222 DWORD actual_pid = launcher_delegate_->GetProcessId();
223 if (peer_pid != static_cast<int32>(actual_pid)) {
224 LOG(ERROR) << "The actual client PID " << actual_pid
225 << " does not match the one reported by the client: "
226 << peer_pid;
227 StopWorker();
228 return;
231 // This can result in |this| being deleted, so this call must be the last in
232 // this method.
233 worker_delegate_->OnChannelConnected(peer_pid);
236 void WorkerProcessLauncher::Core::OnChannelError() {
237 DCHECK(caller_task_runner_->BelongsToCurrentThread());
239 // Schedule a delayed termination of the worker process. Usually, the pipe is
240 // disconnected when the worker process is about to exit. Waiting a little bit
241 // here allows the worker to exit completely and so, notify
242 // |process_watcher_|. As the result KillProcess() will not be called and
243 // the original exit code reported by the worker process will be retrieved.
244 ipc_error_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(5),
245 this, &Core::StopWorker);
248 WorkerProcessLauncher::Core::~Core() {
249 DCHECK(stopping_);
252 void WorkerProcessLauncher::Core::LaunchWorker() {
253 DCHECK(caller_task_runner_->BelongsToCurrentThread());
254 DCHECK(!ipc_enabled_);
255 DCHECK(!launch_success_timer_->IsRunning());
256 DCHECK(!launch_timer_->IsRunning());
257 DCHECK(!process_exit_event_.IsValid());
258 DCHECK(process_watcher_.GetWatchedObject() == NULL);
260 // Launch the process and attach an object watcher to the returned process
261 // handle so that we get notified if the process terminates.
262 if (launcher_delegate_->LaunchProcess(this, &process_exit_event_)) {
263 if (process_watcher_.StartWatching(process_exit_event_, this)) {
264 ipc_enabled_ = true;
265 // Record a successful launch once the process has been running for at
266 // least two seconds.
267 launch_success_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(2),
268 this, &Core::RecordSuccessfulLaunch);
269 return;
272 launcher_delegate_->KillProcess(CONTROL_C_EXIT);
275 launch_backoff_.InformOfRequest(false);
276 StopWorker();
279 void WorkerProcessLauncher::Core::RecordSuccessfulLaunch() {
280 DCHECK(caller_task_runner_->BelongsToCurrentThread());
282 launch_backoff_.InformOfRequest(true);
285 void WorkerProcessLauncher::Core::StopWorker() {
286 DCHECK(caller_task_runner_->BelongsToCurrentThread());
288 // Keep the object alive in case one of delegates decides to delete |this|.
289 scoped_refptr<Core> self = this;
291 // Record a launch failure if the process exited too soon.
292 if (launch_success_timer_->IsRunning()) {
293 launch_success_timer_->Stop();
294 launch_backoff_.InformOfRequest(false);
297 // Ignore any remaining IPC messages.
298 ipc_enabled_ = false;
300 // Kill the process if it has been started already.
301 if (process_watcher_.GetWatchedObject() != NULL) {
302 launcher_delegate_->KillProcess(CONTROL_C_EXIT);
304 // Wait until the process is actually stopped if the caller keeps
305 // a reference to |this|. Otherwise terminate everything right now - there
306 // won't be a second chance.
307 if (!stopping_)
308 return;
310 process_watcher_.StopWatching();
313 ipc_error_timer_->Stop();
314 process_exit_event_.Close();
316 // Do not relaunch the worker process if the caller has asked us to stop.
317 if (stopping_) {
318 ipc_error_timer_.reset();
319 launch_timer_.reset();
320 return;
323 if (launcher_delegate_->IsPermanentError(launch_backoff_.failure_count())) {
324 if (!stopping_)
325 worker_delegate_->OnPermanentError();
326 } else {
327 // Schedule the next attempt to launch the worker process.
328 launch_timer_->Start(FROM_HERE, launch_backoff_.GetTimeUntilRelease(),
329 this, &Core::LaunchWorker);
333 WorkerProcessLauncher::WorkerProcessLauncher(
334 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
335 scoped_ptr<Delegate> launcher_delegate,
336 WorkerProcessIpcDelegate* worker_delegate) {
337 core_ = new Core(caller_task_runner, launcher_delegate.Pass(),
338 worker_delegate);
339 core_->Start();
342 WorkerProcessLauncher::~WorkerProcessLauncher() {
343 core_->Stop();
344 core_ = NULL;
347 void WorkerProcessLauncher::Send(IPC::Message* message) {
348 core_->Send(message);
351 } // namespace remoting