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/location.h"
8 #include "base/logging.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/time.h"
11 #include "base/win/windows_version.h"
12 #include "ipc/ipc_message.h"
13 #include "remoting/host/chromoting_messages.h"
14 #include "remoting/host/host_exit_codes.h"
15 #include "remoting/host/worker_process_ipc_delegate.h"
17 using base::TimeDelta
;
18 using base::win::ScopedHandle
;
20 const net::BackoffEntry::Policy kDefaultBackoffPolicy
= {
21 // Number of initial errors (in sequence) to ignore before applying
22 // exponential back-off rules.
25 // Initial delay for exponential back-off in ms.
28 // Factor by which the waiting time will be multiplied.
31 // Fuzzing percentage. ex: 10% will spread requests randomly
32 // between 90%-100% of the calculated time.
35 // Maximum amount of time we are willing to delay our request in ms.
38 // Time to keep an entry from being discarded even when it
39 // has no significant state, -1 to never discard.
42 // Don't use initial delay unless the last request was an error.
46 const int kKillProcessTimeoutSeconds
= 5;
47 const int kLaunchResultTimeoutSeconds
= 5;
51 WorkerProcessLauncher::Delegate::~Delegate() {
54 WorkerProcessLauncher::WorkerProcessLauncher(
55 scoped_ptr
<WorkerProcessLauncher::Delegate
> launcher_delegate
,
56 WorkerProcessIpcDelegate
* ipc_handler
)
57 : ipc_handler_(ipc_handler
),
58 launcher_delegate_(launcher_delegate
.Pass()),
59 exit_code_(CONTROL_C_EXIT
),
61 kill_process_timeout_(
62 base::TimeDelta::FromSeconds(kKillProcessTimeoutSeconds
)),
63 launch_backoff_(&kDefaultBackoffPolicy
) {
64 DCHECK(ipc_handler_
!= NULL
);
69 WorkerProcessLauncher::~WorkerProcessLauncher() {
70 DCHECK(CalledOnValidThread());
76 void WorkerProcessLauncher::Crash(
77 const tracked_objects::Location
& location
) {
78 DCHECK(CalledOnValidThread());
80 // Ask the worker process to crash voluntarily if it is still connected.
82 Send(new ChromotingDaemonMsg_Crash(location
.function_name(),
84 location
.line_number()));
87 // Close the channel and ignore any not yet processed messages.
88 launcher_delegate_
->CloseChannel();
91 // Give the worker process some time to crash.
92 if (!kill_process_timer_
.IsRunning()) {
93 kill_process_timer_
.Start(FROM_HERE
, kill_process_timeout_
, this,
94 &WorkerProcessLauncher::StopWorker
);
98 void WorkerProcessLauncher::Send(IPC::Message
* message
) {
99 DCHECK(CalledOnValidThread());
102 launcher_delegate_
->Send(message
);
108 void WorkerProcessLauncher::OnProcessLaunched(
109 base::win::ScopedHandle worker_process
) {
110 DCHECK(CalledOnValidThread());
111 DCHECK(!ipc_enabled_
);
112 DCHECK(!launch_timer_
.IsRunning());
113 DCHECK(!process_watcher_
.GetWatchedObject());
114 DCHECK(!worker_process_
.IsValid());
116 if (!process_watcher_
.StartWatching(worker_process
, this)) {
122 worker_process_
= worker_process
.Pass();
125 void WorkerProcessLauncher::OnFatalError() {
126 DCHECK(CalledOnValidThread());
131 bool WorkerProcessLauncher::OnMessageReceived(
132 const IPC::Message
& message
) {
133 DCHECK(CalledOnValidThread());
138 return ipc_handler_
->OnMessageReceived(message
);
141 void WorkerProcessLauncher::OnChannelConnected(int32 peer_pid
) {
142 DCHECK(CalledOnValidThread());
147 // This can result in |this| being deleted, so this call must be the last in
149 ipc_handler_
->OnChannelConnected(peer_pid
);
152 void WorkerProcessLauncher::OnChannelError() {
153 DCHECK(CalledOnValidThread());
155 // Schedule a delayed termination of the worker process. Usually, the pipe is
156 // disconnected when the worker process is about to exit. Waiting a little bit
157 // here allows the worker to exit completely and so, notify
158 // |process_watcher_|. As the result KillProcess() will not be called and
159 // the original exit code reported by the worker process will be retrieved.
160 if (!kill_process_timer_
.IsRunning()) {
161 kill_process_timer_
.Start(FROM_HERE
, kill_process_timeout_
, this,
162 &WorkerProcessLauncher::StopWorker
);
166 void WorkerProcessLauncher::OnObjectSignaled(HANDLE object
) {
167 DCHECK(CalledOnValidThread());
168 DCHECK(!process_watcher_
.GetWatchedObject());
169 DCHECK_EQ(exit_code_
, CONTROL_C_EXIT
);
170 DCHECK_EQ(worker_process_
, object
);
172 // Get exit code of the worker process if it is available.
173 if (!::GetExitCodeProcess(worker_process_
, &exit_code_
)) {
174 LOG_GETLASTERROR(INFO
)
175 << "Failed to query the exit code of the worker process";
176 exit_code_
= CONTROL_C_EXIT
;
179 worker_process_
.Close();
183 void WorkerProcessLauncher::LaunchWorker() {
184 DCHECK(CalledOnValidThread());
185 DCHECK(!ipc_enabled_
);
186 DCHECK(!kill_process_timer_
.IsRunning());
187 DCHECK(!launch_timer_
.IsRunning());
188 DCHECK(!process_watcher_
.GetWatchedObject());
189 DCHECK(!launch_result_timer_
.IsRunning());
191 exit_code_
= CONTROL_C_EXIT
;
193 // Make sure launching a process will not take forever.
194 launch_result_timer_
.Start(
195 FROM_HERE
, base::TimeDelta::FromSeconds(kLaunchResultTimeoutSeconds
),
196 this, &WorkerProcessLauncher::RecordLaunchResult
);
198 launcher_delegate_
->LaunchProcess(this);
201 void WorkerProcessLauncher::RecordLaunchResult() {
202 DCHECK(CalledOnValidThread());
204 if (!worker_process_
.IsValid()) {
205 LOG(WARNING
) << "A worker process failed to start within "
206 << kLaunchResultTimeoutSeconds
<< " seconds.";
208 launch_backoff_
.InformOfRequest(false);
213 // Assume success if the worker process has been running for a few seconds.
214 launch_backoff_
.InformOfRequest(true);
217 void WorkerProcessLauncher::RecordSuccessfulLaunchForTest() {
218 DCHECK(CalledOnValidThread());
220 if (launch_result_timer_
.IsRunning()) {
221 launch_result_timer_
.Stop();
222 RecordLaunchResult();
226 void WorkerProcessLauncher::SetKillProcessTimeoutForTest(
227 const base::TimeDelta
& timeout
) {
228 DCHECK(CalledOnValidThread());
230 kill_process_timeout_
= timeout
;
233 void WorkerProcessLauncher::StopWorker() {
234 DCHECK(CalledOnValidThread());
236 // Record a launch failure if the process exited too soon.
237 if (launch_result_timer_
.IsRunning()) {
238 launch_backoff_
.InformOfRequest(false);
239 launch_result_timer_
.Stop();
242 // Ignore any remaining IPC messages.
243 ipc_enabled_
= false;
245 // Stop monitoring the worker process.
246 process_watcher_
.StopWatching();
247 worker_process_
.Close();
249 kill_process_timer_
.Stop();
250 launcher_delegate_
->KillProcess();
252 // Do not relaunch the worker process if the caller has asked us to stop.
256 // Stop trying to restart the worker process if it exited due to
258 if (kMinPermanentErrorExitCode
<= exit_code_
&&
259 exit_code_
<= kMaxPermanentErrorExitCode
) {
260 ipc_handler_
->OnPermanentError();
264 // Schedule the next attempt to launch the worker process.
265 launch_timer_
.Start(FROM_HERE
, launch_backoff_
.GetTimeUntilRelease(), this,
266 &WorkerProcessLauncher::LaunchWorker
);
269 } // namespace remoting