diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-18 02:41:26 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-18 02:41:26 +0000 |
commit | 9610ef248891f4be129eb915a43d226350569910 (patch) | |
tree | e0d45f8c90d82ec4b95120b2a6926e5b1e4dfd80 | |
parent | f7906654d9ffbf1385843b4c7457797564273a56 (diff) | |
download | chromium_src-9610ef248891f4be129eb915a43d226350569910.zip chromium_src-9610ef248891f4be129eb915a43d226350569910.tar.gz chromium_src-9610ef248891f4be129eb915a43d226350569910.tar.bz2 |
Launch processes asynchronously so as not to block the UI thread. For now, renderer only, I'll take care of plugin/worker/utility processes in a followup change. (relanding 32203)
BUG=6844
Review URL: http://codereview.chromium.org/397031
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@32264 0039d316-1c4b-4281-b951-d872f2087c98
19 files changed, 488 insertions, 336 deletions
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index bfcd739..1148f6e 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -126,11 +126,9 @@ ProcessId GetProcId(ProcessHandle process) { // entry structure. Ignores specified exit_code; posix can't force that. // Returns true if this is successful, false otherwise. bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) { - DCHECK(process_id > 1); - if (process_id <= 1) { - LOG(ERROR) << "tried to kill process_id " << process_id; + DCHECK_GT(process_id, 1) << " tried to kill invalid process_id"; + if (process_id <= 1) return false; - } bool result = kill(process_id, SIGTERM) == 0; diff --git a/chrome/browser/child_process_launcher.cc b/chrome/browser/child_process_launcher.cc new file mode 100644 index 0000000..a028492 --- /dev/null +++ b/chrome/browser/child_process_launcher.cc @@ -0,0 +1,238 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/child_process_launcher.h" + +#include "base/command_line.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/thread.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/common/chrome_descriptors.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/process_watcher.h" +#include "chrome/common/result_codes.h" +#include "ipc/ipc_sync_channel.h" + +#if defined(OS_WIN) +#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 + +namespace { + +class LauncherThread : public base::Thread { + public: + LauncherThread() : base::Thread("LauncherThread") { } +}; + +static base::LazyInstance<LauncherThread> launcher(base::LINKER_INITIALIZED); +} + +// Having the functionality of ChildProcessLauncher be in an internal +// ref counted object allows us to automatically terminate the process when the +// parent class destructs, while still holding on to state that we need. +class ChildProcessLauncher::Context + : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> { + public: + Context() : starting_(true), zygote_(false) {} + + void Launch(CommandLine* cmd_line, int ipcfd, Client* client) { + client_ = client; + + CHECK(ChromeThread::GetCurrentThreadIdentifier(&client_thread_id_)); + if (!launcher.Get().message_loop()) + launcher.Get().Start(); + + launcher.Get().message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod( + this, &Context::LaunchInternal, ipcfd, cmd_line)); + } + + void ResetClient() { + // No need for locking as this function gets called on the same thread that + // client_ would be used. + CHECK(ChromeThread::CurrentlyOn(client_thread_id_)); + client_ = NULL; + } + + private: + friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>; + friend class ChildProcessLauncher; + + ~Context() { + Terminate(); + } + + void LaunchInternal(int ipcfd, CommandLine* cmd_line) { + scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); + base::ProcessHandle handle = base::kNullProcessHandle; + bool zygote = false; +#if defined(OS_WIN) + handle = sandbox::StartProcess(cmd_line); +#elif defined(OS_POSIX) + +#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 (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kRendererCmdPrefix)) { + zygote = true; + + base::GlobalDescriptors::Mapping mapping; + 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)); + } + handle = Singleton<ZygoteHost>()->ForkRenderer(cmd_line->argv(), mapping); + } +#endif + + if (!zygote) { + base::file_handle_mapping_vector fds_to_map; + 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. + if (!base::LaunchApp(cmd_line->argv(), fds_to_map, false, &handle)) + handle = base::kNullProcessHandle; + } +#endif + + ChromeThread::PostTask( + client_thread_id_, FROM_HERE, + NewRunnableMethod( + this, &ChildProcessLauncher::Context::Notify, handle, zygote)); + } + + void Notify(base::ProcessHandle handle, bool zygote) { + starting_ = false; + process_.set_handle(handle); + zygote_ = zygote; + if (client_) { + client_->OnProcessLaunched(); + } else { + Terminate(); + } + } + + void Terminate() { + if (!process_.handle()) + return; + + // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So + // don't this on the UI/IO threads. + launcher.Get().message_loop()->PostTask( + FROM_HERE, + NewRunnableFunction( + &ChildProcessLauncher::Context::TerminateInternal, + process_.handle(), zygote_)); + process_.set_handle(base::kNullProcessHandle); + } + + static void TerminateInternal(base::ProcessHandle handle, bool zygote) { + base::Process process(handle); + // Client has gone away, so just kill the process. Using exit code 0 + // means that UMA won't treat this as a crash. + process.Terminate(ResultCodes::NORMAL_EXIT); + // On POSIX, we must additionally reap the child. +#if defined(OS_POSIX) + if (zygote) { +#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(handle); +#endif // defined(OS_LINUX) + } else { + ProcessWatcher::EnsureProcessTerminated(handle); + } +#endif + process.Close(); + } + + Client* client_; + ChromeThread::ID client_thread_id_; + base::Process process_; + bool starting_; + bool zygote_; +}; + + +ChildProcessLauncher::ChildProcessLauncher(CommandLine* cmd_line, + IPC::SyncChannel* channel, + Client* client) { + context_ = new Context(); + + int ipcfd = 0; +#if defined(OS_POSIX) + ipcfd = channel->GetClientFileDescriptor(); +#endif + context_->Launch(cmd_line, ipcfd, client); +} + +ChildProcessLauncher::~ChildProcessLauncher() { + context_->ResetClient(); +} + +bool ChildProcessLauncher::IsStarting() { + return context_->starting_; +} + +base::ProcessHandle ChildProcessLauncher::GetHandle() { + DCHECK(!context_->starting_); + return context_->process_.handle(); +} + +bool ChildProcessLauncher::DidProcessCrash() { + bool did_crash, child_exited; + base::ProcessHandle handle = context_->process_.handle(); +#if defined(OS_LINUX) + if (context_->zygote_) { + did_crash = Singleton<ZygoteHost>()->DidProcessCrash(handle, &child_exited); + } else +#endif + { + did_crash = base::DidProcessCrash(&child_exited, handle); + } + + // 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 + // Terminate 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) + context_->process_.Close(); + + return did_crash; +} + +void ChildProcessLauncher::SetProcessBackgrounded(bool background) { + DCHECK(!context_->starting_); + context_->process_.SetProcessBackgrounded(background); +} diff --git a/chrome/browser/child_process_launcher.h b/chrome/browser/child_process_launcher.h new file mode 100644 index 0000000..7c4202f --- /dev/null +++ b/chrome/browser/child_process_launcher.h @@ -0,0 +1,61 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHILD_PROCESS_LAUNCHER_H_ +#define CHROME_BROWSER_CHILD_PROCESS_LAUNCHER_H_ + +#include "base/basictypes.h" +#include "base/process.h" +#include "base/ref_counted.h" + +class CommandLine; + +namespace IPC { +class SyncChannel; +} + +// Launches a process asynchronously and notifies the client of the process +// handle when it's available. It's used to avoid blocking the calling thread +// on the OS since often it can take > 100 ms to create the process. +class ChildProcessLauncher { + public: + class Client { + public: + // Will be called on the thread that the ChildProcessLauncher was + // constructed on. + virtual void OnProcessLaunched() = 0; + }; + + // Launches the process asynchronously, calling the client when the result is + // ready. Deleting this object before the process is created is safe, since + // the callback won't be called. If the process is still running by the time + // this object destructs, it will be terminated. + // Takes ownership of cmd_line. + ChildProcessLauncher(CommandLine* cmd_line, + IPC::SyncChannel* channel, + Client* client); + ~ChildProcessLauncher(); + + // True if the process is being launched and so the handle isn't available. + bool IsStarting(); + + // Getter for the process handle. Only call after the process has started. + base::ProcessHandle GetHandle(); + + // Call this when the process exits to know if a process crashed or not. + bool DidProcessCrash(); + + // Changes whether the process runs in the background or not. Only call + // this after the process has started. + void SetProcessBackgrounded(bool background); + + private: + class Context; + + scoped_refptr<Context> context_; + + DISALLOW_COPY_AND_ASSIGN(ChildProcessLauncher); +}; + +#endif // CHROME_BROWSER_CHILD_PROCESS_LAUNCHER_H_ diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index 1d3b5a9..0fca208 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -116,15 +116,10 @@ bool ExtensionBrowserTest::InstallOrUpdateExtension( } void ExtensionBrowserTest::ReloadExtension(const std::string& extension_id) { - NotificationRegistrar registrar; - registrar.Add(this, NotificationType::EXTENSION_LOADED, - NotificationService::AllSources()); - ExtensionsService* service = browser()->profile()->GetExtensionsService(); service->ReloadExtension(extension_id); - MessageLoop::current()->PostDelayedTask( - FROM_HERE, new MessageLoop::QuitTask, kTimeoutMs); - ui_test_utils::RunMessageLoop(); + ui_test_utils::RegisterAndWait(NotificationType::EXTENSION_PROCESS_CREATED, + this, kTimeoutMs); } void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) { @@ -259,6 +254,11 @@ void ExtensionBrowserTest::Observe(NotificationType type, MessageLoopForUI::current()->Quit(); break; + case NotificationType::EXTENSION_PROCESS_CREATED: + std::cout << "Got EXTENSION_PROCESS_CREATED notification.\n"; + MessageLoopForUI::current()->Quit(); + break; + default: NOTREACHED(); break; diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index 85f48de5..b0771ad 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -124,6 +124,11 @@ ExtensionHost::ExtensionHost(Extension* extension, SiteInstance* site_instance, render_view_host_->AllowBindings(BindingsPolicy::EXTENSION); if (enable_dom_automation_) render_view_host_->AllowBindings(BindingsPolicy::DOM_AUTOMATION); + + // Listen for when the render process' handle is available so we can add it + // to the task manager then. + registrar_.Add(this, NotificationType::RENDERER_PROCESS_CREATED, + Source<RenderProcessHost>(render_process_host())); } ExtensionHost::~ExtensionHost() { @@ -175,11 +180,6 @@ void ExtensionHost::CreateRenderViewNow() { render_view_host_->CreateRenderView(profile_->GetRequestContext()); NavigateToURL(url_); DCHECK(IsRenderViewLive()); - LOG(INFO) << "Sending EXTENSION_PROCESS_CREATED"; - NotificationService::current()->Notify( - NotificationType::EXTENSION_PROCESS_CREATED, - Source<Profile>(profile_), - Details<ExtensionHost>(this)); } void ExtensionHost::NavigateToURL(const GURL& url) { @@ -216,6 +216,12 @@ void ExtensionHost::Observe(NotificationType type, NavigateToURL(url_); } else if (type == NotificationType::BROWSER_THEME_CHANGED) { InsertThemeCSS(); + } else if (type == NotificationType::RENDERER_PROCESS_CREATED) { + LOG(INFO) << "Sending EXTENSION_PROCESS_CREATED"; + NotificationService::current()->Notify( + NotificationType::EXTENSION_PROCESS_CREATED, + Source<Profile>(profile_), + Details<ExtensionHost>(this)); } else { NOTREACHED(); } diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 896b2fd..1f630af 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -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 @@ BrowserRenderProcessHost::BrowserRenderProcessHost(Profile* profile) 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 @@ BrowserRenderProcessHost::~BrowserRenderProcessHost() { // 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 @@ bool BrowserRenderProcessHost::Init(bool is_extensions_process, // 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 @@ bool BrowserRenderProcessHost::Init(bool is_extensions_process, 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()); + + // Spawn the child process asynchronously to avoid blocking the UI thread. + child_process_.reset(new ChildProcessLauncher( + cmd_line.release(), channel_.get(), this)); - // 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; - - // 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; - } } - // 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 @@ void BrowserRenderProcessHost::CrossSiteClosePageACK( 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::AddWord(const string16& word) { } } +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::ResetVisitedLinks() { } 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 @@ void BrowserRenderProcessHost::AppendRendererCommandLine( // 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 @@ void BrowserRenderProcessHost::PropogateBrowserCommandLineToRenderer( } } -#if defined(OS_WIN) - -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); - } -#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; -} - -#endif // defined(OS_POSIX) - base::ProcessHandle BrowserRenderProcessHost::GetHandle() { - if (run_renderer_in_process()) + // 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(); - return process_.handle(); + if (child_process_->IsStarting()) { + NOTREACHED() << "BrowserRenderProcessHost::GetHandle() called early!"; + return base::kNullProcessHandle; + } + + return child_process_->GetHandle(); } 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::InitExtensions() { 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 @@ void BrowserRenderProcessHost::SendUserScriptsUpdate( } 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 @@ bool BrowserRenderProcessHost::FastShutdownIfPossible() { 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 @@ TransportDIB* BrowserRenderProcessHost::GetTransportDIB( } 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 @@ bool BrowserRenderProcessHost::Send(IPC::Message* msg) { 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::OnMessageReceived(const IPC::Message& msg) { } 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::OnChannelConnected(int32 peer_pid) { 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 @@ void BrowserRenderProcessHost::OnChannelError() { 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::SuddenTerminationChanged(bool enabled) { } 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::Observe(NotificationType type, } } +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()) { diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h index caf55d7..4ab6c73 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.h +++ b/chrome/browser/renderer_host/browser_render_process_host.h @@ -8,6 +8,7 @@ #include "build/build_config.h" #include <map> +#include <queue> #include <string> #include "base/process.h" @@ -17,6 +18,7 @@ #include "base/string16.h" #include "base/timer.h" #include "chrome/common/transport_dib.h" +#include "chrome/browser/child_process_launcher.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/common/notification_registrar.h" #include "third_party/WebKit/WebKit/chromium/public/WebCache.h" @@ -49,7 +51,8 @@ class Size; // are correlated with IDs. This way, the Views and the corresponding ViewHosts // communicate through the two process objects. class BrowserRenderProcessHost : public RenderProcessHost, - public NotificationObserver { + public NotificationObserver, + public ChildProcessLauncher::Client { public: explicit BrowserRenderProcessHost(Profile* profile); ~BrowserRenderProcessHost(); @@ -68,6 +71,7 @@ class BrowserRenderProcessHost : public RenderProcessHost, virtual void WidgetHidden(); virtual void ViewCreated(); virtual void AddWord(const string16& word); + virtual void SendVisitedLinkTable(base::SharedMemory* table_memory); virtual void AddVisitedLinks(const VisitedLinkCommon::Fingerprints& links); virtual void ResetVisitedLinks(); virtual bool FastShutdownIfPossible(); @@ -94,6 +98,9 @@ class BrowserRenderProcessHost : public RenderProcessHost, const NotificationSource& source, const NotificationDetails& details); + // ChildProcessLauncher::Client implementation. + virtual void OnProcessLaunched(); + private: friend class VisitRelayingRenderProcessHost; @@ -122,11 +129,8 @@ class BrowserRenderProcessHost : public RenderProcessHost, void SendUserScriptsUpdate(base::SharedMemory* shared_memory); // Generates a command line to be used to spawn a renderer and appends the - // results to |*command_line|. |*has_cmd_prefix| will be set if the renderer - // command line specifies a prefix which is another program that will actually - // execute the renderer (like gdb). - void AppendRendererCommandLine(CommandLine* command_line, - bool* has_cmd_prefix) const; + // results to |*command_line|. + void AppendRendererCommandLine(CommandLine* command_line) const; // Copies applicable command line switches from the given |browser_cmd| line // flags to the output |renderer_cmd| line flags. Not all switches will be @@ -134,13 +138,6 @@ class BrowserRenderProcessHost : public RenderProcessHost, void PropogateBrowserCommandLineToRenderer(const CommandLine& browser_cmd, CommandLine* renderer_cmd) const; - // Spawns the renderer process, returning the new handle on success, or 0 on - // failure. The renderer command line is given in the first argument, and - // whether a command prefix was used when generating the command line is - // speficied in the second. - base::ProcessHandle ExecuteRenderer(CommandLine* cmd_line, - bool has_cmd_prefix); - // Callers can reduce the RenderProcess' priority. // Returns true if the priority is backgrounded; false otherwise. void SetBackgrounded(bool boost); @@ -201,15 +198,18 @@ class BrowserRenderProcessHost : public RenderProcessHost, // Buffer visited links and send them to to renderer. scoped_ptr<VisitedLinkUpdater> visited_link_updater_; - // True iff the renderer is a child of a zygote process. - bool zygote_child_; - // True iff this process is being used as an extension process. Not valid // when running in single-process mode. bool extension_process_; - base::Process process_; + // Usedt to launch and terminate the process without blocking the UI thread. + scoped_ptr<ChildProcessLauncher> child_process_; + // Messages we queue while waiting for the process handle. We queue them here + // instead of in the channel so that we ensure they're sent after init related + // messages that are sent once the process handle is available. This is + // because the queued messages may have dependencies on the init messages. + std::queue<IPC::Message*> queued_messages_; DISALLOW_COPY_AND_ASSIGN(BrowserRenderProcessHost); }; diff --git a/chrome/browser/renderer_host/mock_render_process_host.cc b/chrome/browser/renderer_host/mock_render_process_host.cc index 7ce8c66..e2aaa13 100644 --- a/chrome/browser/renderer_host/mock_render_process_host.cc +++ b/chrome/browser/renderer_host/mock_render_process_host.cc @@ -53,6 +53,10 @@ void MockRenderProcessHost::ViewCreated() { void MockRenderProcessHost::AddWord(const string16& word) { } +void MockRenderProcessHost::SendVisitedLinkTable( + base::SharedMemory* table_memory) { +} + void MockRenderProcessHost::AddVisitedLinks( const VisitedLinkCommon::Fingerprints& links) { } diff --git a/chrome/browser/renderer_host/mock_render_process_host.h b/chrome/browser/renderer_host/mock_render_process_host.h index f9e3143..4550512 100644 --- a/chrome/browser/renderer_host/mock_render_process_host.h +++ b/chrome/browser/renderer_host/mock_render_process_host.h @@ -43,6 +43,7 @@ class MockRenderProcessHost : public RenderProcessHost { virtual void WidgetHidden(); virtual void ViewCreated(); virtual void AddWord(const string16& word); + virtual void SendVisitedLinkTable(base::SharedMemory* table_memory); virtual void AddVisitedLinks( const VisitedLinkCommon::Fingerprints& visited_links); virtual void ResetVisitedLinks(); diff --git a/chrome/browser/renderer_host/render_process_host.h b/chrome/browser/renderer_host/render_process_host.h index 1b2d9ef..0914a52 100644 --- a/chrome/browser/renderer_host/render_process_host.h +++ b/chrome/browser/renderer_host/render_process_host.h @@ -17,9 +17,12 @@ #include "ipc/ipc_sync_channel.h" class Profile; +class URLRequestContextGetter; struct ViewMsg_ClosePage_Params; -class URLRequestContextGetter; +namespace base { +class SharedMemory; +} // Virtual interface that represents the browser side of the browser <-> // renderer communication channel. There will generally be one @@ -173,6 +176,9 @@ class RenderProcessHost : public IPC::Channel::Sender, // Add a word in the spellchecker. virtual void AddWord(const string16& word) = 0; + // Informs the renderer about a new visited link table. + virtual void SendVisitedLinkTable(base::SharedMemory* table_memory) = 0; + // Notify the renderer that a link was visited. virtual void AddVisitedLinks( const VisitedLinkCommon::Fingerprints& links) = 0; diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc index 055d68f..7706c5b 100644 --- a/chrome/browser/tab_contents/tab_contents.cc +++ b/chrome/browser/tab_contents/tab_contents.cc @@ -2139,9 +2139,6 @@ void TabContents::DidStopLoading() { // An entry may not exist for a stop when loading an initial blank page or // if an iframe injected by script into a blank page finishes loading. if (entry) { - scoped_ptr<base::ProcessMetrics> metrics( - base::ProcessMetrics::CreateProcessMetrics(process()->GetHandle())); - base::TimeDelta elapsed = base::TimeTicks::Now() - current_load_start_; details.reset(new LoadNotificationDetails( diff --git a/chrome/browser/visitedlink_event_listener.cc b/chrome/browser/visitedlink_event_listener.cc index 17ca1fd..9003fcc 100644 --- a/chrome/browser/visitedlink_event_listener.cc +++ b/chrome/browser/visitedlink_event_listener.cc @@ -24,15 +24,7 @@ void VisitedLinkEventListener::NewTable(base::SharedMemory* table_memory) { if (!i.GetCurrentValue()->HasConnection()) continue; - base::SharedMemoryHandle new_table; - base::ProcessHandle process = i.GetCurrentValue()->GetHandle(); - if (!process) { - // process can be null if it's started with the --single-process flag. - process = base::Process::Current().handle(); - } - - table_memory->ShareToProcess(process, &new_table); - i.GetCurrentValue()->Send(new ViewMsg_VisitedLink_NewTable(new_table)); + i.GetCurrentValue()->SendVisitedLinkTable(table_memory); } } diff --git a/chrome/browser/visitedlink_master.cc b/chrome/browser/visitedlink_master.cc index a7ccd13..7292551 100644 --- a/chrome/browser/visitedlink_master.cc +++ b/chrome/browser/visitedlink_master.cc @@ -258,19 +258,6 @@ bool VisitedLinkMaster::Init() { return true; } -bool VisitedLinkMaster::ShareToProcess(base::ProcessHandle process, - base::SharedMemoryHandle *new_handle) { - if (shared_memory_) - return shared_memory_->ShareToProcess(process, new_handle); - - NOTREACHED(); - return false; -} - -base::SharedMemoryHandle VisitedLinkMaster::GetSharedMemoryHandle() { - return shared_memory_->handle(); -} - VisitedLinkMaster::Hash VisitedLinkMaster::TryToAddURL(const GURL& url) { // Extra check that we are not off the record. This should not happen. if (profile_ && profile_->IsOffTheRecord()) { diff --git a/chrome/browser/visitedlink_master.h b/chrome/browser/visitedlink_master.h index a2bc7b9..fc35ff9 100644 --- a/chrome/browser/visitedlink_master.h +++ b/chrome/browser/visitedlink_master.h @@ -80,13 +80,7 @@ class VisitedLinkMaster : public VisitedLinkCommon { // object won't work. bool Init(); - // Duplicates the handle to the shared memory to another process. - // Returns true on success. - bool ShareToProcess(base::ProcessHandle process, - base::SharedMemoryHandle *new_handle); - - // Returns the handle to the shared memory - base::SharedMemoryHandle GetSharedMemoryHandle(); + base::SharedMemory* shared_memory() { return shared_memory_; } // Adds a URL to the table. void AddURL(const GURL& url); diff --git a/chrome/browser/visitedlink_unittest.cc b/chrome/browser/visitedlink_unittest.cc index b873243..3d79355 100644 --- a/chrome/browser/visitedlink_unittest.cc +++ b/chrome/browser/visitedlink_unittest.cc @@ -131,7 +131,8 @@ class VisitedLinkTest : public testing::Test { // Create a slave database. VisitedLinkSlave slave; base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle(); - master_->ShareToProcess(base::GetCurrentProcessHandle(), &new_handle); + master_->shared_memory()->ShareToProcess( + base::GetCurrentProcessHandle(), &new_handle); bool success = slave.Init(new_handle); ASSERT_TRUE(success); g_slaves.push_back(&slave); @@ -271,7 +272,8 @@ TEST_F(VisitedLinkTest, DeleteAll) { { VisitedLinkSlave slave; base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle(); - master_->ShareToProcess(base::GetCurrentProcessHandle(), &new_handle); + master_->shared_memory()->ShareToProcess( + base::GetCurrentProcessHandle(), &new_handle); ASSERT_TRUE(slave.Init(new_handle)); g_slaves.push_back(&slave); @@ -319,7 +321,8 @@ TEST_F(VisitedLinkTest, Resizing) { // ...and a slave VisitedLinkSlave slave; base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle(); - master_->ShareToProcess(base::GetCurrentProcessHandle(), &new_handle); + master_->shared_memory()->ShareToProcess( + base::GetCurrentProcessHandle(), &new_handle); bool success = slave.Init(new_handle); ASSERT_TRUE(success); g_slaves.push_back(&slave); diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc index 9344f28..2db423a 100644 --- a/chrome/browser/zygote_host_linux.cc +++ b/chrome/browser/zygote_host_linux.cc @@ -200,11 +200,11 @@ pid_t ZygoteHost::ForkRenderer( } if (!base::SendMsg(control_fd_, pickle.data(), pickle.size(), fds)) - return -1; + return base::kNullProcessHandle; pid_t pid; if (HANDLE_EINTR(read(control_fd_, &pid, sizeof(pid))) != sizeof(pid)) - return -1; + return base::kNullProcessHandle; return pid; } diff --git a/chrome/browser/zygote_host_linux.h b/chrome/browser/zygote_host_linux.h index 5b4244c..f84a88b 100644 --- a/chrome/browser/zygote_host_linux.h +++ b/chrome/browser/zygote_host_linux.h @@ -26,6 +26,8 @@ class ZygoteHost { public: void Init(const std::string& sandbox_cmd); + // Tries to start a renderer process. Returns its pid on success, otherwise + // base::kNullProcessHandle; pid_t ForkRenderer(const std::vector<std::string>& command_line, const base::GlobalDescriptors::Mapping& mapping); void EnsureProcessTerminated(pid_t process); diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index d47ac08..c4ac3f1 100755 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -974,6 +974,8 @@ 'browser/cert_store.h', 'browser/character_encoding.cc', 'browser/character_encoding.h', + 'browser/child_process_launcher.cc', + 'browser/child_process_launcher.h', 'browser/child_process_security_policy.cc', 'browser/child_process_security_policy.h', 'browser/chrome_browser_application_mac.h', diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h index 8d4b747..781e252 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -346,6 +346,11 @@ class NotificationType { // details are expected. CWINDOW_CLOSED, + // Indicates that a RenderProcessHost was created and its handle is now + // available. The source will be the RenderProcessHost that corresponds to + // the process. + RENDERER_PROCESS_CREATED, + // Indicates that a RenderProcessHost is destructing. The source will be the // RenderProcessHost that corresponds to the process. RENDERER_PROCESS_TERMINATED, @@ -372,7 +377,7 @@ class NotificationType { // the RenderWidgetHost, the details are not used. RENDER_WIDGET_HOST_DESTROYED, - // Sent from ~RenderViewHost. The source is the RenderViewHost. + // Sent from ~RenderViewHost. The source is the TabContents. RENDER_VIEW_HOST_DELETED, // Indicates a RenderWidgetHost has been hidden or restored. The source is |