OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/child/child_thread.h" | |
6 | |
7 #include <signal.h> | |
8 | |
9 #include <string> | |
10 | |
11 #include "base/allocator/allocator_extension.h" | |
12 #include "base/base_switches.h" | |
13 #include "base/basictypes.h" | |
14 #include "base/command_line.h" | |
15 #include "base/debug/leak_annotations.h" | |
16 #include "base/lazy_instance.h" | |
17 #include "base/logging.h" | |
18 #include "base/message_loop/message_loop.h" | |
19 #include "base/message_loop/timer_slack.h" | |
20 #include "base/process/kill.h" | |
21 #include "base/process/process_handle.h" | |
22 #include "base/strings/string_number_conversions.h" | |
23 #include "base/strings/string_util.h" | |
24 #include "base/synchronization/condition_variable.h" | |
25 #include "base/synchronization/lock.h" | |
26 #include "base/threading/thread_local.h" | |
27 #include "base/tracked_objects.h" | |
28 #include "components/tracing/child_trace_message_filter.h" | |
29 #include "content/child/bluetooth/bluetooth_message_filter.h" | |
30 #include "content/child/child_discardable_shared_memory_manager.h" | |
31 #include "content/child/child_gpu_memory_buffer_manager.h" | |
32 #include "content/child/child_histogram_message_filter.h" | |
33 #include "content/child/child_process.h" | |
34 #include "content/child/child_resource_message_filter.h" | |
35 #include "content/child/child_shared_bitmap_manager.h" | |
36 #include "content/child/fileapi/file_system_dispatcher.h" | |
37 #include "content/child/fileapi/webfilesystem_impl.h" | |
38 #include "content/child/geofencing/geofencing_message_filter.h" | |
39 #include "content/child/mojo/mojo_application.h" | |
40 #include "content/child/navigator_connect/navigator_connect_dispatcher.h" | |
41 #include "content/child/notifications/notification_dispatcher.h" | |
42 #include "content/child/power_monitor_broadcast_source.h" | |
43 #include "content/child/push_messaging/push_dispatcher.h" | |
44 #include "content/child/quota_dispatcher.h" | |
45 #include "content/child/quota_message_filter.h" | |
46 #include "content/child/resource_dispatcher.h" | |
47 #include "content/child/service_worker/service_worker_message_filter.h" | |
48 #include "content/child/thread_safe_sender.h" | |
49 #include "content/child/websocket_dispatcher.h" | |
50 #include "content/common/child_process_messages.h" | |
51 #include "content/public/common/content_switches.h" | |
52 #include "ipc/ipc_logging.h" | |
53 #include "ipc/ipc_switches.h" | |
54 #include "ipc/ipc_sync_channel.h" | |
55 #include "ipc/ipc_sync_message_filter.h" | |
56 #include "ipc/mojo/ipc_channel_mojo.h" | |
57 | |
58 #if defined(OS_WIN) | |
59 #include "content/common/handle_enumerator_win.h" | |
60 #endif | |
61 | |
62 #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) | |
63 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" | |
64 #endif | |
65 | |
66 using tracked_objects::ThreadData; | |
67 | |
68 namespace content { | |
69 namespace { | |
70 | |
71 // How long to wait for a connection to the browser process before giving up. | |
72 const int kConnectionTimeoutS = 15; | |
73 | |
74 base::LazyInstance<base::ThreadLocalPointer<ChildThread> > g_lazy_tls = | |
75 LAZY_INSTANCE_INITIALIZER; | |
76 | |
77 // This isn't needed on Windows because there the sandbox's job object | |
78 // terminates child processes automatically. For unsandboxed processes (i.e. | |
79 // plugins), PluginThread has EnsureTerminateMessageFilter. | |
80 #if defined(OS_POSIX) | |
81 | |
82 // TODO(earthdok): Re-enable on CrOS http://crbug.com/360622 | |
83 #if (defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ | |
84 defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ | |
85 defined(UNDEFINED_SANITIZER)) && !defined(OS_CHROMEOS) | |
86 // A thread delegate that waits for |duration| and then exits the process with | |
87 // _exit(0). | |
88 class WaitAndExitDelegate : public base::PlatformThread::Delegate { | |
89 public: | |
90 explicit WaitAndExitDelegate(base::TimeDelta duration) | |
91 : duration_(duration) {} | |
92 | |
93 void ThreadMain() override { | |
94 base::PlatformThread::Sleep(duration_); | |
95 _exit(0); | |
96 } | |
97 | |
98 private: | |
99 const base::TimeDelta duration_; | |
100 DISALLOW_COPY_AND_ASSIGN(WaitAndExitDelegate); | |
101 }; | |
102 | |
103 bool CreateWaitAndExitThread(base::TimeDelta duration) { | |
104 scoped_ptr<WaitAndExitDelegate> delegate(new WaitAndExitDelegate(duration)); | |
105 | |
106 const bool thread_created = | |
107 base::PlatformThread::CreateNonJoinable(0, delegate.get()); | |
108 if (!thread_created) | |
109 return false; | |
110 | |
111 // A non joinable thread has been created. The thread will either terminate | |
112 // the process or will be terminated by the process. Therefore, keep the | |
113 // delegate object alive for the lifetime of the process. | |
114 WaitAndExitDelegate* leaking_delegate = delegate.release(); | |
115 ANNOTATE_LEAKING_OBJECT_PTR(leaking_delegate); | |
116 ignore_result(leaking_delegate); | |
117 return true; | |
118 } | |
119 #endif | |
120 | |
121 class SuicideOnChannelErrorFilter : public IPC::MessageFilter { | |
122 public: | |
123 // IPC::MessageFilter | |
124 void OnChannelError() override { | |
125 // For renderer/worker processes: | |
126 // On POSIX, at least, one can install an unload handler which loops | |
127 // forever and leave behind a renderer process which eats 100% CPU forever. | |
128 // | |
129 // This is because the terminate signals (ViewMsg_ShouldClose and the error | |
130 // from the IPC sender) are routed to the main message loop but never | |
131 // processed (because that message loop is stuck in V8). | |
132 // | |
133 // One could make the browser SIGKILL the renderers, but that leaves open a | |
134 // large window where a browser failure (or a user, manually terminating | |
135 // the browser because "it's stuck") will leave behind a process eating all | |
136 // the CPU. | |
137 // | |
138 // So, we install a filter on the sender so that we can process this event | |
139 // here and kill the process. | |
140 // TODO(earthdok): Re-enable on CrOS http://crbug.com/360622 | |
141 #if (defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ | |
142 defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ | |
143 defined(UNDEFINED_SANITIZER)) && !defined(OS_CHROMEOS) | |
144 // Some sanitizer tools rely on exit handlers (e.g. to run leak detection, | |
145 // or dump code coverage data to disk). Instead of exiting the process | |
146 // immediately, we give it 60 seconds to run exit handlers. | |
147 CHECK(CreateWaitAndExitThread(base::TimeDelta::FromSeconds(60))); | |
148 #if defined(LEAK_SANITIZER) | |
149 // Invoke LeakSanitizer early to avoid detecting shutdown-only leaks. If | |
150 // leaks are found, the process will exit here. | |
151 __lsan_do_leak_check(); | |
152 #endif | |
153 #else | |
154 _exit(0); | |
155 #endif | |
156 } | |
157 | |
158 protected: | |
159 ~SuicideOnChannelErrorFilter() override {} | |
160 }; | |
161 | |
162 #endif // OS(POSIX) | |
163 | |
164 #if defined(OS_ANDROID) | |
165 ChildThread* g_child_thread = NULL; | |
166 | |
167 // A lock protects g_child_thread. | |
168 base::LazyInstance<base::Lock> g_lazy_child_thread_lock = | |
169 LAZY_INSTANCE_INITIALIZER; | |
170 | |
171 // base::ConditionVariable has an explicit constructor that takes | |
172 // a base::Lock pointer as parameter. The base::DefaultLazyInstanceTraits | |
173 // doesn't handle the case. Thus, we need our own class here. | |
174 struct CondVarLazyInstanceTraits { | |
175 static const bool kRegisterOnExit = true; | |
176 #ifndef NDEBUG | |
177 static const bool kAllowedToAccessOnNonjoinableThread = false; | |
178 #endif | |
179 | |
180 static base::ConditionVariable* New(void* instance) { | |
181 return new (instance) base::ConditionVariable( | |
182 g_lazy_child_thread_lock.Pointer()); | |
183 } | |
184 static void Delete(base::ConditionVariable* instance) { | |
185 instance->~ConditionVariable(); | |
186 } | |
187 }; | |
188 | |
189 // A condition variable that synchronize threads initializing and waiting | |
190 // for g_child_thread. | |
191 base::LazyInstance<base::ConditionVariable, CondVarLazyInstanceTraits> | |
192 g_lazy_child_thread_cv = LAZY_INSTANCE_INITIALIZER; | |
193 | |
194 void QuitMainThreadMessageLoop() { | |
195 base::MessageLoop::current()->Quit(); | |
196 } | |
197 | |
198 #endif | |
199 | |
200 } // namespace | |
201 | |
202 ChildThread::Options::Options() | |
203 : channel_name(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
204 switches::kProcessChannelID)), | |
205 use_mojo_channel(false), | |
206 in_browser_process(false) { | |
207 } | |
208 | |
209 ChildThread::Options::Options(bool mojo) | |
210 : channel_name(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
211 switches::kProcessChannelID)), | |
212 use_mojo_channel(mojo), | |
213 in_browser_process(true) { | |
214 } | |
215 | |
216 ChildThread::Options::Options(std::string name, bool mojo) | |
217 : channel_name(name), use_mojo_channel(mojo), in_browser_process(true) { | |
218 } | |
219 | |
220 ChildThread::Options::~Options() { | |
221 } | |
222 | |
223 ChildThread::ChildThreadMessageRouter::ChildThreadMessageRouter( | |
224 IPC::Sender* sender) | |
225 : sender_(sender) {} | |
226 | |
227 bool ChildThread::ChildThreadMessageRouter::Send(IPC::Message* msg) { | |
228 return sender_->Send(msg); | |
229 } | |
230 | |
231 ChildThread::ChildThread() | |
232 : router_(this), | |
233 in_browser_process_(false), | |
234 channel_connected_factory_(this) { | |
235 Init(Options()); | |
236 } | |
237 | |
238 ChildThread::ChildThread(const Options& options) | |
239 : router_(this), | |
240 in_browser_process_(options.in_browser_process), | |
241 channel_connected_factory_(this) { | |
242 Init(options); | |
243 } | |
244 | |
245 void ChildThread::ConnectChannel(bool use_mojo_channel) { | |
246 bool create_pipe_now = true; | |
247 if (use_mojo_channel) { | |
248 VLOG(1) << "Mojo is enabled on child"; | |
249 channel_->Init(IPC::ChannelMojo::CreateClientFactory(channel_name_), | |
250 create_pipe_now); | |
251 return; | |
252 } | |
253 | |
254 VLOG(1) << "Mojo is disabled on child"; | |
255 channel_->Init(channel_name_, IPC::Channel::MODE_CLIENT, create_pipe_now); | |
256 } | |
257 | |
258 void ChildThread::Init(const Options& options) { | |
259 channel_name_ = options.channel_name; | |
260 | |
261 g_lazy_tls.Pointer()->Set(this); | |
262 on_channel_error_called_ = false; | |
263 message_loop_ = base::MessageLoop::current(); | |
264 #ifdef IPC_MESSAGE_LOG_ENABLED | |
265 // We must make sure to instantiate the IPC Logger *before* we create the | |
266 // channel, otherwise we can get a callback on the IO thread which creates | |
267 // the logger, and the logger does not like being created on the IO thread. | |
268 IPC::Logging::GetInstance(); | |
269 #endif | |
270 channel_ = IPC::SyncChannel::Create( | |
271 this, ChildProcess::current()->io_message_loop_proxy(), | |
272 ChildProcess::current()->GetShutDownEvent()); | |
273 #ifdef IPC_MESSAGE_LOG_ENABLED | |
274 if (!in_browser_process_) | |
275 IPC::Logging::GetInstance()->SetIPCSender(this); | |
276 #endif | |
277 | |
278 mojo_application_.reset(new MojoApplication); | |
279 | |
280 sync_message_filter_ = | |
281 new IPC::SyncMessageFilter(ChildProcess::current()->GetShutDownEvent()); | |
282 thread_safe_sender_ = new ThreadSafeSender( | |
283 base::MessageLoopProxy::current().get(), sync_message_filter_.get()); | |
284 | |
285 resource_dispatcher_.reset(new ResourceDispatcher( | |
286 this, message_loop()->task_runner())); | |
287 websocket_dispatcher_.reset(new WebSocketDispatcher); | |
288 file_system_dispatcher_.reset(new FileSystemDispatcher()); | |
289 | |
290 histogram_message_filter_ = new ChildHistogramMessageFilter(); | |
291 resource_message_filter_ = | |
292 new ChildResourceMessageFilter(resource_dispatcher()); | |
293 | |
294 service_worker_message_filter_ = | |
295 new ServiceWorkerMessageFilter(thread_safe_sender_.get()); | |
296 | |
297 quota_message_filter_ = | |
298 new QuotaMessageFilter(thread_safe_sender_.get()); | |
299 quota_dispatcher_.reset(new QuotaDispatcher(thread_safe_sender_.get(), | |
300 quota_message_filter_.get())); | |
301 geofencing_message_filter_ = | |
302 new GeofencingMessageFilter(thread_safe_sender_.get()); | |
303 bluetooth_message_filter_ = | |
304 new BluetoothMessageFilter(thread_safe_sender_.get()); | |
305 notification_dispatcher_ = | |
306 new NotificationDispatcher(thread_safe_sender_.get()); | |
307 push_dispatcher_ = new PushDispatcher(thread_safe_sender_.get()); | |
308 navigator_connect_dispatcher_ = | |
309 new NavigatorConnectDispatcher(thread_safe_sender_.get()); | |
310 | |
311 channel_->AddFilter(histogram_message_filter_.get()); | |
312 channel_->AddFilter(sync_message_filter_.get()); | |
313 channel_->AddFilter(resource_message_filter_.get()); | |
314 channel_->AddFilter(quota_message_filter_->GetFilter()); | |
315 channel_->AddFilter(notification_dispatcher_->GetFilter()); | |
316 channel_->AddFilter(push_dispatcher_->GetFilter()); | |
317 channel_->AddFilter(service_worker_message_filter_->GetFilter()); | |
318 channel_->AddFilter(geofencing_message_filter_->GetFilter()); | |
319 channel_->AddFilter(bluetooth_message_filter_->GetFilter()); | |
320 channel_->AddFilter(navigator_connect_dispatcher_->GetFilter()); | |
321 | |
322 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | |
323 switches::kSingleProcess)) { | |
324 // In single process mode, browser-side tracing will cover the whole | |
325 // process including renderers. | |
326 channel_->AddFilter(new tracing::ChildTraceMessageFilter( | |
327 ChildProcess::current()->io_message_loop_proxy())); | |
328 } | |
329 | |
330 // In single process mode we may already have a power monitor | |
331 if (!base::PowerMonitor::Get()) { | |
332 scoped_ptr<PowerMonitorBroadcastSource> power_monitor_source( | |
333 new PowerMonitorBroadcastSource()); | |
334 channel_->AddFilter(power_monitor_source->GetMessageFilter()); | |
335 | |
336 power_monitor_.reset(new base::PowerMonitor( | |
337 power_monitor_source.Pass())); | |
338 } | |
339 | |
340 #if defined(OS_POSIX) | |
341 // Check that --process-type is specified so we don't do this in unit tests | |
342 // and single-process mode. | |
343 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessType)) | |
344 channel_->AddFilter(new SuicideOnChannelErrorFilter()); | |
345 #endif | |
346 | |
347 // Add filters passed here via options. | |
348 for (auto startup_filter : options.startup_filters) { | |
349 channel_->AddFilter(startup_filter); | |
350 } | |
351 | |
352 ConnectChannel(options.use_mojo_channel); | |
353 | |
354 int connection_timeout = kConnectionTimeoutS; | |
355 std::string connection_override = | |
356 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
357 switches::kIPCConnectionTimeout); | |
358 if (!connection_override.empty()) { | |
359 int temp; | |
360 if (base::StringToInt(connection_override, &temp)) | |
361 connection_timeout = temp; | |
362 } | |
363 | |
364 base::MessageLoop::current()->PostDelayedTask( | |
365 FROM_HERE, | |
366 base::Bind(&ChildThread::EnsureConnected, | |
367 channel_connected_factory_.GetWeakPtr()), | |
368 base::TimeDelta::FromSeconds(connection_timeout)); | |
369 | |
370 #if defined(OS_ANDROID) | |
371 { | |
372 base::AutoLock lock(g_lazy_child_thread_lock.Get()); | |
373 g_child_thread = this; | |
374 } | |
375 // Signalling without locking is fine here because only | |
376 // one thread can wait on the condition variable. | |
377 g_lazy_child_thread_cv.Get().Signal(); | |
378 #endif | |
379 | |
380 #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) | |
381 trace_memory_controller_.reset(new base::debug::TraceMemoryController( | |
382 message_loop_->message_loop_proxy(), | |
383 ::HeapProfilerWithPseudoStackStart, | |
384 ::HeapProfilerStop, | |
385 ::GetHeapProfile)); | |
386 #endif | |
387 | |
388 shared_bitmap_manager_.reset( | |
389 new ChildSharedBitmapManager(thread_safe_sender())); | |
390 | |
391 gpu_memory_buffer_manager_.reset( | |
392 new ChildGpuMemoryBufferManager(thread_safe_sender())); | |
393 | |
394 discardable_shared_memory_manager_.reset( | |
395 new ChildDiscardableSharedMemoryManager(thread_safe_sender())); | |
396 } | |
397 | |
398 ChildThread::~ChildThread() { | |
399 #ifdef IPC_MESSAGE_LOG_ENABLED | |
400 IPC::Logging::GetInstance()->SetIPCSender(NULL); | |
401 #endif | |
402 | |
403 channel_->RemoveFilter(histogram_message_filter_.get()); | |
404 channel_->RemoveFilter(sync_message_filter_.get()); | |
405 | |
406 // The ChannelProxy object caches a pointer to the IPC thread, so need to | |
407 // reset it as it's not guaranteed to outlive this object. | |
408 // NOTE: this also has the side-effect of not closing the main IPC channel to | |
409 // the browser process. This is needed because this is the signal that the | |
410 // browser uses to know that this process has died, so we need it to be alive | |
411 // until this process is shut down, and the OS closes the handle | |
412 // automatically. We used to watch the object handle on Windows to do this, | |
413 // but it wasn't possible to do so on POSIX. | |
414 channel_->ClearIPCTaskRunner(); | |
415 g_lazy_tls.Pointer()->Set(NULL); | |
416 } | |
417 | |
418 void ChildThread::Shutdown() { | |
419 // Delete objects that hold references to blink so derived classes can | |
420 // safely shutdown blink in their Shutdown implementation. | |
421 file_system_dispatcher_.reset(); | |
422 quota_dispatcher_.reset(); | |
423 WebFileSystemImpl::DeleteThreadSpecificInstance(); | |
424 } | |
425 | |
426 void ChildThread::OnChannelConnected(int32 peer_pid) { | |
427 channel_connected_factory_.InvalidateWeakPtrs(); | |
428 } | |
429 | |
430 void ChildThread::OnChannelError() { | |
431 set_on_channel_error_called(true); | |
432 base::MessageLoop::current()->Quit(); | |
433 } | |
434 | |
435 bool ChildThread::Send(IPC::Message* msg) { | |
436 DCHECK(base::MessageLoop::current() == message_loop()); | |
437 if (!channel_) { | |
438 delete msg; | |
439 return false; | |
440 } | |
441 | |
442 return channel_->Send(msg); | |
443 } | |
444 | |
445 MessageRouter* ChildThread::GetRouter() { | |
446 DCHECK(base::MessageLoop::current() == message_loop()); | |
447 return &router_; | |
448 } | |
449 | |
450 scoped_ptr<base::SharedMemory> ChildThread::AllocateSharedMemory( | |
451 size_t buf_size) { | |
452 DCHECK(base::MessageLoop::current() == message_loop()); | |
453 return AllocateSharedMemory(buf_size, this); | |
454 } | |
455 | |
456 // static | |
457 scoped_ptr<base::SharedMemory> ChildThread::AllocateSharedMemory( | |
458 size_t buf_size, | |
459 IPC::Sender* sender) { | |
460 scoped_ptr<base::SharedMemory> shared_buf; | |
461 #if defined(OS_WIN) | |
462 shared_buf.reset(new base::SharedMemory); | |
463 if (!shared_buf->CreateAnonymous(buf_size)) { | |
464 NOTREACHED(); | |
465 return NULL; | |
466 } | |
467 #else | |
468 // On POSIX, we need to ask the browser to create the shared memory for us, | |
469 // since this is blocked by the sandbox. | |
470 base::SharedMemoryHandle shared_mem_handle; | |
471 if (sender->Send(new ChildProcessHostMsg_SyncAllocateSharedMemory( | |
472 buf_size, &shared_mem_handle))) { | |
473 if (base::SharedMemory::IsHandleValid(shared_mem_handle)) { | |
474 shared_buf.reset(new base::SharedMemory(shared_mem_handle, false)); | |
475 } else { | |
476 NOTREACHED() << "Browser failed to allocate shared memory"; | |
477 return NULL; | |
478 } | |
479 } else { | |
480 NOTREACHED() << "Browser allocation request message failed"; | |
481 return NULL; | |
482 } | |
483 #endif | |
484 return shared_buf; | |
485 } | |
486 | |
487 bool ChildThread::OnMessageReceived(const IPC::Message& msg) { | |
488 if (mojo_application_->OnMessageReceived(msg)) | |
489 return true; | |
490 | |
491 // Resource responses are sent to the resource dispatcher. | |
492 if (resource_dispatcher_->OnMessageReceived(msg)) | |
493 return true; | |
494 if (websocket_dispatcher_->OnMessageReceived(msg)) | |
495 return true; | |
496 if (file_system_dispatcher_->OnMessageReceived(msg)) | |
497 return true; | |
498 | |
499 bool handled = true; | |
500 IPC_BEGIN_MESSAGE_MAP(ChildThread, msg) | |
501 IPC_MESSAGE_HANDLER(ChildProcessMsg_Shutdown, OnShutdown) | |
502 #if defined(IPC_MESSAGE_LOG_ENABLED) | |
503 IPC_MESSAGE_HANDLER(ChildProcessMsg_SetIPCLoggingEnabled, | |
504 OnSetIPCLoggingEnabled) | |
505 #endif | |
506 IPC_MESSAGE_HANDLER(ChildProcessMsg_SetProfilerStatus, | |
507 OnSetProfilerStatus) | |
508 IPC_MESSAGE_HANDLER(ChildProcessMsg_GetChildProfilerData, | |
509 OnGetChildProfilerData) | |
510 IPC_MESSAGE_HANDLER(ChildProcessMsg_DumpHandles, OnDumpHandles) | |
511 IPC_MESSAGE_HANDLER(ChildProcessMsg_SetProcessBackgrounded, | |
512 OnProcessBackgrounded) | |
513 #if defined(USE_TCMALLOC) | |
514 IPC_MESSAGE_HANDLER(ChildProcessMsg_GetTcmallocStats, OnGetTcmallocStats) | |
515 #endif | |
516 IPC_MESSAGE_UNHANDLED(handled = false) | |
517 IPC_END_MESSAGE_MAP() | |
518 | |
519 if (handled) | |
520 return true; | |
521 | |
522 if (msg.routing_id() == MSG_ROUTING_CONTROL) | |
523 return OnControlMessageReceived(msg); | |
524 | |
525 return router_.OnMessageReceived(msg); | |
526 } | |
527 | |
528 bool ChildThread::OnControlMessageReceived(const IPC::Message& msg) { | |
529 return false; | |
530 } | |
531 | |
532 void ChildThread::OnShutdown() { | |
533 base::MessageLoop::current()->Quit(); | |
534 } | |
535 | |
536 #if defined(IPC_MESSAGE_LOG_ENABLED) | |
537 void ChildThread::OnSetIPCLoggingEnabled(bool enable) { | |
538 if (enable) | |
539 IPC::Logging::GetInstance()->Enable(); | |
540 else | |
541 IPC::Logging::GetInstance()->Disable(); | |
542 } | |
543 #endif // IPC_MESSAGE_LOG_ENABLED | |
544 | |
545 void ChildThread::OnSetProfilerStatus(ThreadData::Status status) { | |
546 ThreadData::InitializeAndSetTrackingStatus(status); | |
547 } | |
548 | |
549 void ChildThread::OnGetChildProfilerData(int sequence_number) { | |
550 tracked_objects::ProcessDataSnapshot process_data; | |
551 ThreadData::Snapshot(false, &process_data); | |
552 | |
553 Send(new ChildProcessHostMsg_ChildProfilerData(sequence_number, | |
554 process_data)); | |
555 } | |
556 | |
557 void ChildThread::OnDumpHandles() { | |
558 #if defined(OS_WIN) | |
559 scoped_refptr<HandleEnumerator> handle_enum( | |
560 new HandleEnumerator( | |
561 base::CommandLine::ForCurrentProcess()->HasSwitch( | |
562 switches::kAuditAllHandles))); | |
563 handle_enum->EnumerateHandles(); | |
564 Send(new ChildProcessHostMsg_DumpHandlesDone); | |
565 #else | |
566 NOTIMPLEMENTED(); | |
567 #endif | |
568 } | |
569 | |
570 #if defined(USE_TCMALLOC) | |
571 void ChildThread::OnGetTcmallocStats() { | |
572 std::string result; | |
573 char buffer[1024 * 32]; | |
574 base::allocator::GetStats(buffer, sizeof(buffer)); | |
575 result.append(buffer); | |
576 Send(new ChildProcessHostMsg_TcmallocStats(result)); | |
577 } | |
578 #endif | |
579 | |
580 ChildThread* ChildThread::current() { | |
581 return g_lazy_tls.Pointer()->Get(); | |
582 } | |
583 | |
584 #if defined(OS_ANDROID) | |
585 // The method must NOT be called on the child thread itself. | |
586 // It may block the child thread if so. | |
587 void ChildThread::ShutdownThread() { | |
588 DCHECK(!ChildThread::current()) << | |
589 "this method should NOT be called from child thread itself"; | |
590 { | |
591 base::AutoLock lock(g_lazy_child_thread_lock.Get()); | |
592 while (!g_child_thread) | |
593 g_lazy_child_thread_cv.Get().Wait(); | |
594 } | |
595 DCHECK_NE(base::MessageLoop::current(), g_child_thread->message_loop()); | |
596 g_child_thread->message_loop()->PostTask( | |
597 FROM_HERE, base::Bind(&QuitMainThreadMessageLoop)); | |
598 } | |
599 #endif | |
600 | |
601 void ChildThread::OnProcessFinalRelease() { | |
602 if (on_channel_error_called_) { | |
603 base::MessageLoop::current()->Quit(); | |
604 return; | |
605 } | |
606 | |
607 // The child process shutdown sequence is a request response based mechanism, | |
608 // where we send out an initial feeler request to the child process host | |
609 // instance in the browser to verify if it's ok to shutdown the child process. | |
610 // The browser then sends back a response if it's ok to shutdown. This avoids | |
611 // race conditions if the process refcount is 0 but there's an IPC message | |
612 // inflight that would addref it. | |
613 Send(new ChildProcessHostMsg_ShutdownRequest); | |
614 } | |
615 | |
616 void ChildThread::EnsureConnected() { | |
617 VLOG(0) << "ChildThread::EnsureConnected()"; | |
618 base::KillProcess(base::GetCurrentProcessHandle(), 0, false); | |
619 } | |
620 | |
621 void ChildThread::OnProcessBackgrounded(bool background) { | |
622 // Set timer slack to maximum on main thread when in background. | |
623 base::TimerSlack timer_slack = base::TIMER_SLACK_NONE; | |
624 if (background) | |
625 timer_slack = base::TIMER_SLACK_MAXIMUM; | |
626 base::MessageLoop::current()->SetTimerSlack(timer_slack); | |
627 } | |
628 | |
629 } // namespace content | |
OLD | NEW |