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

Unified Diff: chrome/browser/renderer_host/browser_render_process_host.cc

Issue 397031: Launch processes asynchronously so as not to block the UI thread. For now, re... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: return 0 instead of -1 if zygote couldn't launch renderer Created 11 years, 1 month 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/renderer_host/browser_render_process_host.cc
===================================================================
--- chrome/browser/renderer_host/browser_render_process_host.cc (revision 32255)
+++ chrome/browser/renderer_host/browser_render_process_host.cc (working copy)
@@ -20,6 +20,7 @@
#include "base/field_trial.h"
#include "base/logging.h"
#include "base/process_util.h"
+#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
@@ -47,10 +48,8 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/child_process_info.h"
#include "chrome/common/child_process_host.h"
-#include "chrome/common/chrome_descriptors.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/common/notification_service.h"
-#include "chrome/common/process_watcher.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/result_codes.h"
#include "chrome/renderer/render_process.h"
@@ -63,12 +62,6 @@
#if defined(OS_WIN)
#include "app/win_util.h"
-#include "chrome/browser/sandbox_policy.h"
-#elif defined(OS_LINUX)
-#include "base/singleton.h"
-#include "chrome/browser/crash_handler_host_linux.h"
-#include "chrome/browser/zygote_host_linux.h"
-#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"
#endif
using WebKit::WebCache;
@@ -201,7 +194,6 @@
ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_(
base::TimeDelta::FromSeconds(5),
this, &BrowserRenderProcessHost::ClearTransportDIBCache)),
- zygote_child_(false),
extension_process_(false) {
widget_helper_ = new RenderWidgetHelper();
@@ -239,21 +231,15 @@
// We may have some unsent messages at this point, but that's OK.
channel_.reset();
+ while (!queued_messages_.empty()) {
+ delete queued_messages_.front();
+ queued_messages_.pop();
+ }
// Destroy the AudioRendererHost properly.
if (audio_renderer_host_.get())
audio_renderer_host_->Destroy();
- if (process_.handle() && !run_renderer_in_process()) {
- if (zygote_child_) {
-#if defined(OS_LINUX)
- Singleton<ZygoteHost>()->EnsureProcessTerminated(process_.handle());
-#endif
- } else {
- ProcessWatcher::EnsureProcessTerminated(process_.handle());
- }
- }
-
ClearTransportDIBCache();
NotificationService::current()->Notify(
@@ -313,16 +299,6 @@
// be doing.
channel_->set_sync_messages_with_no_timeout_allowed(false);
- // Build command line for renderer, we have to quote the executable name to
- // deal with spaces.
- CommandLine cmd_line(renderer_path);
- cmd_line.AppendSwitchWithValue(switches::kProcessChannelID,
- ASCIIToWide(channel_id));
- if (is_extensions_process)
- cmd_line.AppendSwitch(switches::kEnableDatabases);
- bool has_cmd_prefix;
- AppendRendererCommandLine(&cmd_line, &has_cmd_prefix);
-
if (run_renderer_in_process()) {
// Crank up a thread and run the initialization there. With the way that
// messages flow between the browser and renderer, this thread is required
@@ -342,49 +318,25 @@
options.message_loop_type = MessageLoop::TYPE_DEFAULT;
#endif
in_process_renderer_->StartWithOptions(options);
+
+ OnProcessLaunched(); // Fake a callback that the process is ready.
} else {
- base::TimeTicks begin_launch_time = base::TimeTicks::Now();
+ // Build command line for renderer, we have to quote the executable name to
+ // deal with spaces.
+ scoped_ptr<CommandLine> cmd_line(new CommandLine(renderer_path));
+ cmd_line->AppendSwitchWithValue(switches::kProcessChannelID,
+ ASCIIToWide(channel_id));
+ if (is_extensions_process)
+ cmd_line->AppendSwitch(switches::kEnableDatabases);
+ AppendRendererCommandLine(cmd_line.get());
- // Actually spawn the child process.
- base::ProcessHandle process = ExecuteRenderer(&cmd_line, has_cmd_prefix);
- if (!process) {
- channel_.reset();
- return false;
- }
- process_.set_handle(process);
- fast_shutdown_started_ = false;
+ // Spawn the child process asynchronously to avoid blocking the UI thread.
+ child_process_.reset(new ChildProcessLauncher(
+ cmd_line.release(), channel_.get(), this));
- // Log the launch time, separating out the first one (which will likely be
- // slower due to the rest of the browser initializing at the same time).
- static bool done_first_launch = false;
- if (done_first_launch) {
- UMA_HISTOGRAM_TIMES("MPArch.RendererLaunchSubsequent",
- base::TimeTicks::Now() - begin_launch_time);
- } else {
- UMA_HISTOGRAM_TIMES("MPArch.RendererLaunchFirst",
- base::TimeTicks::Now() - begin_launch_time);
- done_first_launch = true;
- }
+ fast_shutdown_started_ = false;
}
- // Now that the process is created, set its backgrounding accordingly.
- SetBackgrounded(backgrounded_);
-
- InitVisitedLinks();
- InitUserScripts();
- InitExtensions();
-#if defined(SPELLCHECKER_IN_RENDERER)
- // We don't want to initialize the spellchecker unless SpellCheckHost has been
- // created. In InitSpellChecker(), we know if GetSpellCheckHost() is NULL
- // then the spellchecker has been turned off, but here, we don't know if
- // it's been turned off or just not loaded yet.
- if (profile()->GetSpellCheckHost())
- InitSpellChecker();
-#endif
-
- if (max_page_id_ != -1)
- Send(new ViewMsg_SetNextPageID(max_page_id_ + 1));
-
return true;
}
@@ -404,11 +356,16 @@
bool BrowserRenderProcessHost::WaitForPaintMsg(int render_widget_id,
const base::TimeDelta& max_delay,
IPC::Message* msg) {
+ // The post task to this thread with the process id could be in queue, and we
+ // don't want to dispatch a message before then since it will need the handle.
+ if (child_process_.get() && child_process_->IsStarting())
+ return false;
+
return widget_helper_->WaitForPaintMsg(render_widget_id, max_delay, msg);
}
void BrowserRenderProcessHost::ReceivedBadMessage(uint16 msg_type) {
- BadMessageTerminateProcess(msg_type, process_.handle());
+ BadMessageTerminateProcess(msg_type, GetHandle());
}
void BrowserRenderProcessHost::ViewCreated() {
@@ -446,6 +403,21 @@
}
}
+void BrowserRenderProcessHost::SendVisitedLinkTable(
+ base::SharedMemory* table_memory) {
+ // Check if the process is still starting and we don't have a handle for it
+ // yet, in which case this will happen later when InitVisitedLinks is called.
+ if (!run_renderer_in_process() &&
+ (!child_process_.get() || child_process_->IsStarting())) {
+ return;
+ }
+
+ base::SharedMemoryHandle handle_for_process;
+ table_memory->ShareToProcess(GetHandle(), &handle_for_process);
+ if (base::SharedMemory::IsHandleValid(handle_for_process))
+ Send(new ViewMsg_VisitedLink_NewTable(handle_for_process));
+}
+
void BrowserRenderProcessHost::AddVisitedLinks(
const VisitedLinkCommon::Fingerprints& links) {
visited_link_updater_->AddLinks(links);
@@ -464,8 +436,7 @@
}
void BrowserRenderProcessHost::AppendRendererCommandLine(
- CommandLine* command_line,
- bool* has_cmd_prefix) const {
+ CommandLine* command_line) const {
if (logging::DialogsAreSuppressed())
command_line->AppendSwitch(switches::kNoErrorDialogs);
@@ -497,16 +468,12 @@
// A command prefix is something prepended to the command line of the spawned
// process. It is supported only on POSIX systems.
#if defined(OS_POSIX)
- *has_cmd_prefix =
- browser_command_line.HasSwitch(switches::kRendererCmdPrefix);
- if (*has_cmd_prefix) {
+ if (browser_command_line.HasSwitch(switches::kRendererCmdPrefix)) {
// launch the renderer child with some prefix (usually "gdb --args")
const std::wstring prefix =
browser_command_line.GetSwitchValue(switches::kRendererCmdPrefix);
command_line->PrependWrapper(prefix);
}
-#else
- *has_cmd_prefix = false;
#endif // defined(OS_POSIX)
ChildProcessHost::SetCrashReporterCommandLine(command_line);
@@ -598,88 +565,26 @@
}
}
-#if defined(OS_WIN)
+base::ProcessHandle BrowserRenderProcessHost::GetHandle() {
+ // child_process_ is null either because we're in single process mode, we have
+ // done fast termination, or the process has crashed.
+ if (run_renderer_in_process() || !child_process_.get())
+ return base::Process::Current().handle();
-base::ProcessHandle BrowserRenderProcessHost::ExecuteRenderer(
- CommandLine* cmd_line,
- bool has_cmd_prefix) {
- return sandbox::StartProcess(cmd_line);
-}
-
-#elif defined(OS_POSIX)
-
-base::ProcessHandle BrowserRenderProcessHost::ExecuteRenderer(
- CommandLine* cmd_line,
- bool has_cmd_prefix) {
-#if defined(OS_LINUX)
- // On Linux, normally spawn processes with zygotes. We can't do this when
- // we're spawning child processes through an external program (i.e. there is a
- // command prefix) like GDB so fall through to the POSIX case then.
- if (!has_cmd_prefix) {
- base::GlobalDescriptors::Mapping mapping;
- const int ipcfd = channel_->GetClientFileDescriptor();
- mapping.push_back(std::pair<uint32_t, int>(kPrimaryIPCChannel, ipcfd));
- const int crash_signal_fd =
- Singleton<RendererCrashHandlerHostLinux>()->GetDeathSignalSocket();
- if (crash_signal_fd >= 0) {
- mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal,
- crash_signal_fd));
- }
- zygote_child_ = true;
- return Singleton<ZygoteHost>()->ForkRenderer(cmd_line->argv(), mapping);
+ if (child_process_->IsStarting()) {
+ NOTREACHED() << "BrowserRenderProcessHost::GetHandle() called early!";
+ return base::kNullProcessHandle;
}
-#endif // defined(OS_LINUX)
- // NOTE: This code is duplicated with plugin_process_host.cc, but
- // there's not a good place to de-duplicate it.
- base::file_handle_mapping_vector fds_to_map;
- const int ipcfd = channel_->GetClientFileDescriptor();
- fds_to_map.push_back(std::make_pair(ipcfd, kPrimaryIPCChannel + 3));
-
-#if defined(OS_LINUX)
- // On Linux, we need to add some extra file descriptors for crash handling and
- // the sandbox.
- const int crash_signal_fd =
- Singleton<RendererCrashHandlerHostLinux>()->GetDeathSignalSocket();
- if (crash_signal_fd >= 0) {
- fds_to_map.push_back(std::make_pair(crash_signal_fd,
- kCrashDumpSignal + 3));
- }
- const int sandbox_fd =
- Singleton<RenderSandboxHostLinux>()->GetRendererSocket();
- fds_to_map.push_back(std::make_pair(sandbox_fd, kSandboxIPCChannel + 3));
-#endif // defined(OS_LINUX)
-
- // Actually launch the app.
- zygote_child_ = false;
- base::ProcessHandle process_handle;
- if (!base::LaunchApp(cmd_line->argv(), fds_to_map, false, &process_handle))
- return 0;
- return process_handle;
+ return child_process_->GetHandle();
}
-#endif // defined(OS_POSIX)
-
-base::ProcessHandle BrowserRenderProcessHost::GetHandle() {
- if (run_renderer_in_process())
- return base::Process::Current().handle();
-
- return process_.handle();
-}
-
void BrowserRenderProcessHost::InitVisitedLinks() {
VisitedLinkMaster* visitedlink_master = profile()->GetVisitedLinkMaster();
- if (!visitedlink_master) {
+ if (!visitedlink_master)
return;
- }
- base::SharedMemoryHandle handle_for_process;
- bool r = visitedlink_master->ShareToProcess(GetHandle(), &handle_for_process);
- DCHECK(r);
-
- if (base::SharedMemory::IsHandleValid(handle_for_process)) {
- Send(new ViewMsg_VisitedLink_NewTable(handle_for_process));
- }
+ SendVisitedLinkTable(visitedlink_master->shared_memory());
}
void BrowserRenderProcessHost::InitUserScripts() {
@@ -705,6 +610,11 @@
void BrowserRenderProcessHost::SendUserScriptsUpdate(
base::SharedMemory *shared_memory) {
+ // Process is being started asynchronously. We'll end up calling
+ // InitUserScripts when it's created which will call this again.
+ if (child_process_.get() && child_process_->IsStarting())
+ return;
+
base::SharedMemoryHandle handle_for_process;
if (!shared_memory->ShareToProcess(GetHandle(), &handle_for_process)) {
// This can legitimately fail if the renderer asserts at startup.
@@ -717,11 +627,12 @@
}
bool BrowserRenderProcessHost::FastShutdownIfPossible() {
- if (!process_.handle())
- return false; // Render process is probably crashed.
if (run_renderer_in_process())
return false; // Single process mode can't do fast shutdown.
+ if (!child_process_.get() || child_process_->IsStarting() || !GetHandle())
+ return false; // Render process hasn't started or is probably crashed.
+
// Test if there's an unload listener.
// NOTE: It's possible that an onunload listener may be installed
// while we're shutting down, so there's a small race here. Given that
@@ -748,23 +659,7 @@
iter.Advance();
}
- // Otherwise, we're allowed to just terminate the process. Using exit code 0
- // means that UMA won't treat this as a renderer crash.
- process_.Terminate(ResultCodes::NORMAL_EXIT);
- // On POSIX, we must additionally reap the child.
-#if defined(OS_POSIX)
- if (zygote_child_) {
-#if defined(OS_LINUX)
- // If the renderer was created via a zygote, we have to proxy the reaping
- // through the zygote process.
- Singleton<ZygoteHost>()->EnsureProcessTerminated(process_.handle());
-#endif // defined(OS_LINUX)
- } else {
- ProcessWatcher::EnsureProcessGetsReaped(process_.handle());
- }
-#endif // defined(OS_POSIX)
- process_.Close();
-
+ child_process_.reset();
fast_shutdown_started_ = true;
return true;
}
@@ -831,11 +726,8 @@
}
void BrowserRenderProcessHost::ClearTransportDIBCache() {
- for (std::map<TransportDIB::Id, TransportDIB*>::iterator
- i = cached_dibs_.begin(); i != cached_dibs_.end(); ++i) {
- delete i->second;
- }
-
+ STLDeleteContainerPairSecondPointers(
+ cached_dibs_.begin(), cached_dibs_.end());
cached_dibs_.clear();
}
@@ -844,6 +736,12 @@
delete msg;
return false;
}
+
+ if (child_process_.get() && child_process_->IsStarting()) {
+ queued_messages_.push(msg);
+ return true;
+ }
+
return channel_->Send(msg);
}
@@ -895,49 +793,8 @@
}
void BrowserRenderProcessHost::OnChannelConnected(int32 peer_pid) {
- // process_ is not NULL if we created the renderer process
- if (!process_.handle()) {
- if (fast_shutdown_started_) {
- // We terminated the process, but the ChannelConnected task was still
- // in the queue. We can safely ignore it.
- return;
- } else if (base::GetCurrentProcId() == peer_pid) {
- // We are in single-process mode. In theory we should have access to
- // ourself but it may happen that we don't.
- process_.set_handle(base::GetCurrentProcessHandle());
- } else {
-#if defined(OS_WIN)
- // Request MAXIMUM_ALLOWED to match the access a handle
- // returned by CreateProcess() has to the process object.
- process_.set_handle(OpenProcess(MAXIMUM_ALLOWED, FALSE, peer_pid));
-#else
- NOTREACHED();
-#endif
- DCHECK(process_.handle());
- }
- } else {
- // Need to verify that the peer_pid is actually the process we know, if
- // it is not, we need to panic now. See bug 1002150.
- if (peer_pid != process_.pid()) {
- // This check is invalid on Linux for two reasons:
- // a) If we are running the renderer in a wrapper (with
- // --renderer-cmd-prefix) then the we'll see the PID of the wrapper
- // process, not the renderer itself.
- // b) If we are using the SUID sandbox with CLONE_NEWPID, then the
- // renderer will be in a new PID namespace and will believe that
- // it's PID is 2 or 3.
- // Additionally, this check isn't a security problem on Linux since we
- // don't use the PID as reported by the renderer.
-#if !defined(OS_LINUX)
- CHECK(peer_pid == process_.pid()) << peer_pid << " " << process_.pid();
-#endif
- }
- mark_child_process_activity_time();
- }
-
#if defined(IPC_MESSAGE_LOG_ENABLED)
- bool enabled = IPC::Logging::current()->Enabled();
- Send(new ViewMsg_SetIPCLoggingEnabled(enabled));
+ Send(new ViewMsg_SetIPCLoggingEnabled(IPC::Logging::current()->Enabled()));
#endif
}
@@ -945,7 +802,7 @@
void BrowserRenderProcessHost::BadMessageTerminateProcess(
uint16 msg_type, base::ProcessHandle process) {
LOG(ERROR) << "bad message " << msg_type << " terminating renderer.";
- if (BrowserRenderProcessHost::run_renderer_in_process()) {
+ if (run_renderer_in_process()) {
// In single process mode it is better if we don't suicide but just crash.
CHECK(false);
}
@@ -962,42 +819,17 @@
if (!channel_.get())
return;
- bool child_exited;
- bool did_crash;
- if (!process_.handle()) {
- // The process has been terminated (likely FastShutdownIfPossible).
- did_crash = false;
- child_exited = true;
- } else if (zygote_child_) {
-#if defined(OS_LINUX)
- did_crash = Singleton<ZygoteHost>()->DidProcessCrash(
- process_.handle(), &child_exited);
-#else
- NOTREACHED();
- did_crash = true;
-#endif
- } else {
- did_crash = base::DidProcessCrash(&child_exited, process_.handle());
- }
+ // NULL in single process mode or if fast termination happened.
+ bool did_crash =
+ child_process_.get() ? child_process_->DidProcessCrash() : false;
NotificationService::current()->Notify(
NotificationType::RENDERER_PROCESS_CLOSED,
Source<RenderProcessHost>(this),
Details<bool>(&did_crash));
- // POSIX: If the process crashed, then the kernel closed the socket for it
- // and so the child has already died by the time we get here. Since
- // DidProcessCrash called waitpid with WNOHANG, it'll reap the process.
- // However, if DidProcessCrash didn't reap the child, we'll need to in
- // ~BrowserRenderProcessHost via ProcessWatcher. So we can't close the handle
- // here.
- //
- // This is moot on Windows where |child_exited| will always be true.
- if (child_exited)
- process_.Close();
-
WebCacheManager::GetInstance()->Remove(id());
-
+ child_process_.reset();
channel_.reset();
IDMap<IPC::Channel::Listener>::iterator iter(&listeners_);
@@ -1035,32 +867,27 @@
}
void BrowserRenderProcessHost::SetBackgrounded(bool backgrounded) {
- // If the process_ is NULL, the process hasn't been created yet.
- if (process_.handle()) {
- bool should_set_backgrounded = true;
-
-#if defined(OS_WIN)
- // The cbstext.dll loads as a global GetMessage hook in the browser process
- // and intercepts/unintercepts the kernel32 API SetPriorityClass in a
- // background thread. If the UI thread invokes this API just when it is
- // intercepted the stack is messed up on return from the interceptor
- // which causes random crashes in the browser process. Our hack for now
- // is to not invoke the SetPriorityClass API if the dll is loaded.
- should_set_backgrounded = (GetModuleHandle(L"cbstext.dll") == NULL);
-#endif // OS_WIN
-
- if (should_set_backgrounded) {
- process_.SetProcessBackgrounded(backgrounded);
- }
- }
-
// Note: we always set the backgrounded_ value. If the process is NULL
// (and hence hasn't been created yet), we will set the process priority
// later when we create the process.
backgrounded_ = backgrounded;
+ if (!child_process_.get() || child_process_->IsStarting())
+ return;
+
+#if defined(OS_WIN)
+ // The cbstext.dll loads as a global GetMessage hook in the browser process
+ // and intercepts/unintercepts the kernel32 API SetPriorityClass in a
+ // background thread. If the UI thread invokes this API just when it is
+ // intercepted the stack is messed up on return from the interceptor
+ // which causes random crashes in the browser process. Our hack for now
+ // is to not invoke the SetPriorityClass API if the dll is loaded.
+ if (GetModuleHandle(L"cbstext.dll"))
+ return;
+#endif // OS_WIN
+
+ child_process_->SetProcessBackgrounded(backgrounded);
}
-// NotificationObserver implementation.
void BrowserRenderProcessHost::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -1103,6 +930,35 @@
}
}
+void BrowserRenderProcessHost::OnProcessLaunched() {
+ // Now that the process is created, set its backgrounding accordingly.
+ SetBackgrounded(backgrounded_);
+
+ InitVisitedLinks();
+ InitUserScripts();
+ InitExtensions();
+#if defined(SPELLCHECKER_IN_RENDERER)
+ // We don't want to initialize the spellchecker unless SpellCheckHost has been
+ // created. In InitSpellChecker(), we know if GetSpellCheckHost() is NULL
+ // then the spellchecker has been turned off, but here, we don't know if
+ // it's been turned off or just not loaded yet.
+ if (profile()->GetSpellCheckHost())
+ InitSpellChecker();
+#endif
+
+ if (max_page_id_ != -1)
+ Send(new ViewMsg_SetNextPageID(max_page_id_ + 1));
+
+ while (!queued_messages_.empty()) {
+ Send(queued_messages_.front());
+ queued_messages_.pop();
+ }
+
+ NotificationService::current()->Notify(
+ NotificationType::RENDERER_PROCESS_CREATED,
+ Source<RenderProcessHost>(this), NotificationService::NoDetails());
+}
+
void BrowserRenderProcessHost::OnExtensionAddListener(
const std::string& event_name) {
if (profile()->GetExtensionMessageService()) {
« no previous file with comments | « chrome/browser/renderer_host/browser_render_process_host.h ('k') | chrome/browser/renderer_host/mock_render_process_host.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698