diff options
29 files changed, 358 insertions, 222 deletions
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 9c6930a..43e1ff2 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -54,7 +54,6 @@ #include "chrome/common/render_messages.h" #include "chrome/common/result_codes.h" #include "chrome/renderer/render_process.h" -#include "chrome/renderer/render_thread.h" #include "chrome/installer/util/google_update_settings.h" #include "grit/generated_resources.h" #include "ipc/ipc_switches.h" @@ -94,8 +93,7 @@ class RendererMainThread : public base::Thread { CoInitialize(NULL); #endif - render_process_ = new RenderProcess(); - render_process_->set_main_thread(new RenderThread(channel_id_)); + render_process_ = new RenderProcess(channel_id_); // It's a little lame to manually set this flag. But the single process // RendererThread will receive the WM_QUIT. We don't need to assert on // this thread, so just force the flag manually. @@ -414,14 +412,18 @@ bool BrowserRenderProcessHost::Init() { 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 - // to prevent a deadlock in single-process mode. Since the primordial - // thread in the renderer process runs the WebKit code and can sometimes - // blocking calls to the UI thread (i.e. this thread), they need to run on - // separate threads. + // to prevent a deadlock in single-process mode. When using multiple + // processes, the primordial thread in the renderer process has a message + // loop which is used for sending messages asynchronously to the io thread + // in the browser process. If we don't create this thread, then the + // RenderThread is both responsible for rendering and also for + // communicating IO. This can lead to deadlocks where the RenderThread is + // waiting for the IO to complete, while the browsermain is trying to pass + // an event to the RenderThread. in_process_renderer_.reset(new RendererMainThread(channel_id)); base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_UI; + options.message_loop_type = MessageLoop::TYPE_IO; in_process_renderer_->StartWithOptions(options); } else { base::ProcessHandle process = 0; diff --git a/chrome/common/child_process.cc b/chrome/common/child_process.cc index 34ea7a2..f32708c 100644 --- a/chrome/common/child_process.cc +++ b/chrome/common/child_process.cc @@ -9,14 +9,14 @@ ChildProcess* ChildProcess::child_process_; -ChildProcess::ChildProcess() - : ref_count_(0), - shutdown_event_(true, false), - io_thread_("Chrome_ChildIOThread") { +ChildProcess::ChildProcess(ChildThread* child_thread) + : child_thread_(child_thread), + ref_count_(0), + shutdown_event_(true, false) { DCHECK(!child_process_); child_process_ = this; - - io_thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0)); + if (child_thread_.get()) // null in unittests. + child_thread_->Run(); } ChildProcess::~ChildProcess() { @@ -28,29 +28,28 @@ ChildProcess::~ChildProcess() { // notice shutdown before the render process begins waiting for them to exit. shutdown_event_.Signal(); - // Kill the main thread object before nulling child_process_, since - // destruction code might depend on it. - main_thread_.reset(); + if (child_thread_.get()) + child_thread_->Stop(); child_process_ = NULL; } void ChildProcess::AddRefProcess() { - DCHECK(!main_thread_.get() || // null in unittests. - MessageLoop::current() == main_thread_->message_loop()); + DCHECK(!child_thread_.get() || // null in unittests. + MessageLoop::current() == child_thread_->message_loop()); ref_count_++; } void ChildProcess::ReleaseProcess() { - DCHECK(!main_thread_.get() || // null in unittests. - MessageLoop::current() == main_thread_->message_loop()); + DCHECK(!child_thread_.get() || // null in unittests. + MessageLoop::current() == child_thread_->message_loop()); DCHECK(ref_count_); DCHECK(child_process_); if (--ref_count_) return; - if (main_thread_.get()) // null in unittests. - main_thread_->OnProcessFinalRelease(); + if (child_thread_.get()) // null in unittests. + child_thread_->OnProcessFinalRelease(); } base::WaitableEvent* ChildProcess::GetShutDownEvent() { diff --git a/chrome/common/child_process.h b/chrome/common/child_process.h index 5e125bc..1366a02 100644 --- a/chrome/common/child_process.h +++ b/chrome/common/child_process.h @@ -7,23 +7,21 @@ #include "base/basictypes.h" #include "base/scoped_ptr.h" -#include "base/thread.h" #include "base/waitable_event.h" -#include "chrome/common/child_thread.h" + +class ChildThread; // Base class for child processes of the browser process (i.e. renderer and // plugin host). This is a singleton object for each child process. class ChildProcess { public: - // Child processes should have an object that derives from this class. - ChildProcess(); + // Child processes should have an object that derives from this class. The + // constructor will return once ChildThread has started. + ChildProcess(ChildThread* child_thread); virtual ~ChildProcess(); - // Getter for the child process' main thread. - ChildThread* main_thread() { return main_thread_.get(); } - void set_main_thread(ChildThread* thread) { main_thread_.reset(thread); } - - MessageLoop* io_message_loop() { return io_thread_.message_loop(); } + // Getter for this process' main thread. + ChildThread* child_thread() { return child_thread_.get(); } // A global event object that is signalled when the main thread's message // loop exits. This gives background threads a way to observe the main @@ -47,19 +45,15 @@ class ChildProcess { static ChildProcess* current() { return child_process_; } private: + // NOTE: make sure that child_thread_ is listed before shutdown_event_, since + // it depends on it (indirectly through IPC::SyncChannel). + scoped_ptr<ChildThread> child_thread_; + int ref_count_; // An event that will be signalled when we shutdown. base::WaitableEvent shutdown_event_; - // The thread that handles IO events. - base::Thread io_thread_; - - // NOTE: make sure that main_thread_ is listed after shutdown_event_, since - // it depends on it (indirectly through IPC::SyncChannel). Same for - // io_thread_. - scoped_ptr<ChildThread> main_thread_; - // The singleton instance for this process. static ChildProcess* child_process_; diff --git a/chrome/common/child_thread.cc b/chrome/common/child_thread.cc index 9168570..1b7ef75 100644 --- a/chrome/common/child_thread.cc +++ b/chrome/common/child_thread.cc @@ -8,68 +8,41 @@ #include "base/command_line.h" #include "chrome/common/child_process.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/notification_service.h" #include "chrome/common/plugin_messages.h" #include "ipc/ipc_logging.h" #include "ipc/ipc_switches.h" #include "webkit/glue/webkit_glue.h" -ChildThread::ChildThread() { +// V8 needs a 1MB stack size. +const size_t ChildThread::kV8StackSize = 1024 * 1024; + +ChildThread::ChildThread(Thread::Options options) + : Thread("Chrome_ChildThread"), + owner_loop_(MessageLoop::current()), + options_(options), + check_with_browser_before_shutdown_(false) { + DCHECK(owner_loop_); channel_name_ = WideToASCII( CommandLine::ForCurrentProcess()->GetSwitchValue( switches::kProcessChannelID)); - Init(); -} -ChildThread::ChildThread(const std::string channel_name) - : channel_name_(channel_name) { - Init(); -} - -void ChildThread::Init() { - check_with_browser_before_shutdown_ = false; - message_loop_ = MessageLoop::current(); if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserAgent)) { webkit_glue::SetUserAgent(WideToUTF8( CommandLine::ForCurrentProcess()->GetSwitchValue( switches::kUserAgent))); } - - channel_.reset(new IPC::SyncChannel(channel_name_, - IPC::Channel::MODE_CLIENT, this, NULL, - ChildProcess::current()->io_message_loop(), true, - ChildProcess::current()->GetShutDownEvent())); -#ifdef IPC_MESSAGE_LOG_ENABLED - IPC::Logging::current()->SetIPCSender(this); -#endif - - resource_dispatcher_.reset(new ResourceDispatcher(this)); - - // When running in unit tests, there is already a NotificationService object. - // Since only one can exist at a time per thread, check first. - if (!NotificationService::current()) - notification_service_.reset(new NotificationService); } ChildThread::~ChildThread() { -#ifdef IPC_MESSAGE_LOG_ENABLED - IPC::Logging::current()->SetIPCSender(NULL); -#endif +} - // The ChannelProxy object caches a pointer to the IPC thread, so need to - // reset it as it's not guaranteed to outlive this object. - // NOTE: this also has the side-effect of not closing the main IPC channel to - // the browser process. This is needed because this is the signal that the - // browser uses to know that this process has died, so we need it to be alive - // until this process is shut down, and the OS closes the handle - // automatically. We used to watch the object handle on Windows to do this, - // but it wasn't possible to do so on POSIX. - channel_->ClearIPCMessageLoop(); +bool ChildThread::Run() { + return StartWithOptions(options_); } void ChildThread::OnChannelError() { - MessageLoop::current()->Quit(); + owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); } bool ChildThread::Send(IPC::Message* msg) { @@ -104,7 +77,7 @@ void ChildThread::OnMessageReceived(const IPC::Message& msg) { } if (msg.type() == PluginProcessMsg_Shutdown::ID) { - MessageLoop::current()->Quit(); + owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); return; } @@ -116,12 +89,33 @@ void ChildThread::OnMessageReceived(const IPC::Message& msg) { } ChildThread* ChildThread::current() { - return ChildProcess::current()->main_thread(); + return ChildProcess::current()->child_thread(); +} + +void ChildThread::Init() { + channel_.reset(new IPC::SyncChannel(channel_name_, + IPC::Channel::MODE_CLIENT, this, NULL, owner_loop_, true, + ChildProcess::current()->GetShutDownEvent())); +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging::current()->SetIPCSender(this); +#endif + + resource_dispatcher_.reset(new ResourceDispatcher(this)); +} + +void ChildThread::CleanUp() { +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging::current()->SetIPCSender(NULL); +#endif + // Need to destruct the SyncChannel to the browser before we go away because + // it caches a pointer to this thread. + channel_.reset(); + resource_dispatcher_.reset(); } void ChildThread::OnProcessFinalRelease() { if (!check_with_browser_before_shutdown_) { - MessageLoop::current()->Quit(); + owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); return; } diff --git a/chrome/common/child_thread.h b/chrome/common/child_thread.h index 91ea9b7..1affe25 100644 --- a/chrome/common/child_thread.h +++ b/chrome/common/child_thread.h @@ -7,20 +7,18 @@ #include "base/basictypes.h" #include "base/scoped_ptr.h" +#include "base/thread.h" #include "chrome/common/message_router.h" #include "chrome/common/resource_dispatcher.h" #include "ipc/ipc_sync_channel.h" -class NotificationService; - -// The main thread of a child process derives from this class. +// Child processes's background thread should derive from this class. class ChildThread : public IPC::Channel::Listener, - public IPC::Message::Sender { + public IPC::Message::Sender, + public base::Thread { public: // Creates the thread. - ChildThread(); - // Used for single-process mode. - ChildThread(const std::string channel_name); + ChildThread(Thread::Options options); virtual ~ChildThread(); // IPC::Message::Sender implementation: @@ -30,39 +28,58 @@ class ChildThread : public IPC::Channel::Listener, void AddRoute(int32 routing_id, IPC::Channel::Listener* listener); void RemoveRoute(int32 routing_id); + MessageLoop* owner_loop() { return owner_loop_; } + ResourceDispatcher* resource_dispatcher() { return resource_dispatcher_.get(); } - MessageLoop* message_loop() { return message_loop_; } - // Returns the one child thread. static ChildThread* current(); protected: friend class ChildProcess; + // Starts the thread. + bool Run(); + + // Overrides the channel name. Used for --single-process mode. + void SetChannelName(const std::string& name) { channel_name_ = name; } + // Called when the process refcount is 0. void OnProcessFinalRelease(); + protected: + // The required stack size if V8 runs on a thread. + static const size_t kV8StackSize; + virtual void OnControlMessageReceived(const IPC::Message& msg) { } IPC::SyncChannel* channel() { return channel_.get(); } - private: - void Init(); + // Thread implementation. + virtual void Init(); + virtual void CleanUp(); + private: // IPC::Channel::Listener implementation: virtual void OnMessageReceived(const IPC::Message& msg); virtual void OnChannelError(); + // The message loop used to run tasks on the thread that started this thread. + MessageLoop* owner_loop_; + std::string channel_name_; scoped_ptr<IPC::SyncChannel> channel_; - // Implements message routing functionality to the consumers of ChildThread. + // Used only on the background render thread to implement message routing + // functionality to the consumers of the ChildThread. MessageRouter router_; + Thread::Options options_; + // Handles resource loads for this process. + // NOTE: this object lives on the owner thread. scoped_ptr<ResourceDispatcher> resource_dispatcher_; // If true, checks with the browser process before shutdown. This avoids race @@ -70,10 +87,6 @@ class ChildThread : public IPC::Channel::Listener, // that would addref it. bool check_with_browser_before_shutdown_; - MessageLoop* message_loop_; - - scoped_ptr<NotificationService> notification_service_; - DISALLOW_COPY_AND_ASSIGN(ChildThread); }; diff --git a/chrome/common/histogram_synchronizer.cc b/chrome/common/histogram_synchronizer.cc index 9ed94b0..4713084 100644 --- a/chrome/common/histogram_synchronizer.cc +++ b/chrome/common/histogram_synchronizer.cc @@ -7,7 +7,6 @@ #include "base/histogram.h" #include "base/logging.h" #include "base/string_util.h" -#include "base/thread.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" diff --git a/chrome/plugin/plugin_main.cc b/chrome/plugin/plugin_main.cc index 63d295d..7361d64 100644 --- a/chrome/plugin/plugin_main.cc +++ b/chrome/plugin/plugin_main.cc @@ -29,8 +29,8 @@ // main() routine for running as the plugin process. int PluginMain(const MainFunctionParams& parameters) { - // The main thread of the plugin services UI. - MessageLoop main_message_loop(MessageLoop::TYPE_UI); + // The main thread of the plugin services IO. + MessageLoopForIO main_message_loop; std::wstring app_name = chrome::kBrowserAppName; PlatformThread::SetName(WideToASCII(app_name + L"_PluginMain").c_str()); @@ -80,8 +80,7 @@ int PluginMain(const MainFunctionParams& parameters) { } { - ChildProcess plugin_process; - plugin_process.set_main_thread(new PluginThread()); + ChildProcess plugin_process(new PluginThread()); #if defined(OS_WIN) if (!no_sandbox && target_services) target_services->LowerToken(); diff --git a/chrome/plugin/plugin_thread.cc b/chrome/plugin/plugin_thread.cc index ae90e1a..da5c68c 100644 --- a/chrome/plugin/plugin_thread.cc +++ b/chrome/plugin/plugin_thread.cc @@ -6,6 +6,11 @@ #include "build/build_config.h" +#if defined(OS_WIN) +#include <windows.h> +#include <objbase.h> +#endif + #include "base/command_line.h" #include "base/lazy_instance.h" #include "base/process_util.h" @@ -13,6 +18,7 @@ #include "chrome/common/child_process.h" #include "chrome/common/chrome_plugin_lib.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" #include "chrome/common/plugin_messages.h" #include "chrome/common/render_messages.h" #include "chrome/plugin/chrome_plugin_host.h" @@ -26,10 +32,27 @@ static base::LazyInstance<base::ThreadLocalPointer<PluginThread> > lazy_tls( base::LINKER_INITIALIZED); PluginThread::PluginThread() - : preloaded_plugin_module_(NULL) { + : ChildThread(base::Thread::Options(MessageLoop::TYPE_UI, 0)), + preloaded_plugin_module_(NULL) { plugin_path_ = FilePath::FromWStringHack( CommandLine::ForCurrentProcess()->GetSwitchValue(switches::kPluginPath)); +} + +PluginThread::~PluginThread() { +} + +PluginThread* PluginThread::current() { + return lazy_tls.Pointer()->Get(); +} + +void PluginThread::OnControlMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(PluginThread, msg) + IPC_MESSAGE_HANDLER(PluginProcessMsg_CreateChannel, OnCreateChannel) + IPC_MESSAGE_HANDLER(PluginProcessMsg_PluginMessage, OnPluginMessage) + IPC_END_MESSAGE_MAP() +} +void PluginThread::Init() { lazy_tls.Pointer()->Set(this); #if defined(OS_LINUX) { @@ -52,8 +75,14 @@ PluginThread::PluginThread() } } #endif + ChildThread::Init(); PatchNPNFunctions(); +#if defined(OS_WIN) + CoInitialize(NULL); +#endif + + notification_service_.reset(new NotificationService); // Preload the library to avoid loading, unloading then reloading preloaded_plugin_module_ = base::LoadNativeLibrary(plugin_path_); @@ -71,7 +100,7 @@ PluginThread::PluginThread() message_loop()->set_exception_restoration(true); } -PluginThread::~PluginThread() { +void PluginThread::CleanUp() { if (preloaded_plugin_module_) { base::UnloadNativeLibrary(preloaded_plugin_module_); preloaded_plugin_module_ = NULL; @@ -79,29 +108,26 @@ PluginThread::~PluginThread() { PluginChannelBase::CleanupChannels(); NPAPI::PluginLib::UnloadAllPlugins(); ChromePluginLib::UnloadAllPlugins(); + notification_service_.reset(); +#if defined(OS_WIN) + CoUninitialize(); +#endif if (webkit_glue::ShouldForcefullyTerminatePluginProcess()) base::KillProcess(base::GetCurrentProcessHandle(), 0, /* wait= */ false); + // Call this last because it deletes the ResourceDispatcher, which is used + // in some of the above cleanup. + // See http://code.google.com/p/chromium/issues/detail?id=8980 + ChildThread::CleanUp(); lazy_tls.Pointer()->Set(NULL); } -PluginThread* PluginThread::current() { - return lazy_tls.Pointer()->Get(); -} - -void PluginThread::OnControlMessageReceived(const IPC::Message& msg) { - IPC_BEGIN_MESSAGE_MAP(PluginThread, msg) - IPC_MESSAGE_HANDLER(PluginProcessMsg_CreateChannel, OnCreateChannel) - IPC_MESSAGE_HANDLER(PluginProcessMsg_PluginMessage, OnPluginMessage) - IPC_END_MESSAGE_MAP() -} - void PluginThread::OnCreateChannel( int process_id, bool off_the_record) { - scoped_refptr<PluginChannel> channel = PluginChannel::GetPluginChannel( - process_id, ChildProcess::current()->io_message_loop()); + scoped_refptr<PluginChannel> channel = + PluginChannel::GetPluginChannel(process_id, owner_loop()); IPC::ChannelHandle channel_handle; if (channel.get()) { channel_handle.name = channel->channel_name(); diff --git a/chrome/plugin/plugin_thread.h b/chrome/plugin/plugin_thread.h index 2e9803c..e9074aa 100644 --- a/chrome/plugin/plugin_thread.h +++ b/chrome/plugin/plugin_thread.h @@ -16,6 +16,8 @@ #include "base/file_descriptor_posix.h" #endif +class NotificationService; + // The PluginThread class represents a background thread where plugin instances // live. Communication occurs between WebPluginDelegateProxy in the renderer // process and WebPluginDelegateStub in this thread through IPC messages. @@ -30,12 +32,18 @@ class PluginThread : public ChildThread { private: virtual void OnControlMessageReceived(const IPC::Message& msg); + // Thread implementation: + virtual void Init(); + virtual void CleanUp(); + // Callback for when a channel has been created. void OnCreateChannel( int process_id, bool off_the_record); void OnPluginMessage(const std::vector<uint8> &data); + scoped_ptr<NotificationService> notification_service_; + // The plugin module which is preloaded in Init base::NativeLibrary preloaded_plugin_module_; diff --git a/chrome/renderer/mock_render_process.h b/chrome/renderer/mock_render_process.h index 49bc02c..629967f 100644 --- a/chrome/renderer/mock_render_process.h +++ b/chrome/renderer/mock_render_process.h @@ -14,7 +14,8 @@ class ChildThread; // a render widget instance. class MockProcess : public ChildProcess { public: - explicit MockProcess() : ChildProcess() {} + explicit MockProcess() : ChildProcess(NULL) {} + explicit MockProcess(ChildThread* thread) : ChildProcess(thread) {} }; #endif // CHROME_RENDERER_MOCK_RENDER_PROCESS_H_ diff --git a/chrome/renderer/render_process.cc b/chrome/renderer/render_process.cc index 2718d37..3fe22c5 100644 --- a/chrome/renderer/render_process.cc +++ b/chrome/renderer/render_process.cc @@ -47,10 +47,43 @@ static size_t GetMaxSharedMemorySize() { //----------------------------------------------------------------------------- RenderProcess::RenderProcess() - : ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_( + : ChildProcess(new RenderThread()), + ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_( base::TimeDelta::FromSeconds(5), this, &RenderProcess::ClearTransportDIBCache)), sequence_number_(0) { + Init(); +} + +RenderProcess::RenderProcess(const std::string& channel_name) + : ChildProcess(new RenderThread(channel_name)), + ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_( + base::TimeDelta::FromSeconds(5), + this, &RenderProcess::ClearTransportDIBCache)), + sequence_number_(0) { + Init(); +} + +RenderProcess::~RenderProcess() { + // TODO(port) + // Try and limit what we pull in for our non-Win unit test bundle +#ifndef NDEBUG + // log important leaked objects + webkit_glue::CheckForLeaks(); +#endif + + GetShutDownEvent()->Signal(); + + // We need to stop the RenderThread as the clearer_factory_ + // member could be in use while the object itself is destroyed, + // as a result of the containing RenderProcess object being destroyed. + // This race condition causes a crash when the renderer process is shutting + // down. + child_thread()->Stop(); + ClearTransportDIBCache(); +} + +void RenderProcess::Init() { in_process_plugins_ = InProcessPlugins(); for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) shared_mem_cache_[i] = NULL; @@ -98,18 +131,6 @@ RenderProcess::RenderProcess() media::InitializeMediaLibrary(module_path); } -RenderProcess::~RenderProcess() { - // TODO(port) - // Try and limit what we pull in for our non-Win unit test bundle -#ifndef NDEBUG - // log important leaked objects - webkit_glue::CheckForLeaks(); -#endif - - GetShutDownEvent()->Signal(); - ClearTransportDIBCache(); -} - bool RenderProcess::InProcessPlugins() { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); #if defined(OS_LINUX) @@ -136,7 +157,7 @@ TransportDIB* RenderProcess::CreateTransportDIB(size_t size) { // get one. TransportDIB::Handle handle; IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, &handle); - if (!main_thread()->Send(msg)) + if (!child_thread()->Send(msg)) return NULL; if (handle.fd < 0) return NULL; @@ -152,7 +173,7 @@ void RenderProcess::FreeTransportDIB(TransportDIB* dib) { // On Mac we need to tell the browser that it can drop a reference to the // shared memory. IPC::Message* msg = new ViewHostMsg_FreeTransportDIB(dib->id()); - main_thread()->Send(msg); + child_thread()->Send(msg); #endif delete dib; diff --git a/chrome/renderer/render_process.h b/chrome/renderer/render_process.h index d445fbe..727aae5 100644 --- a/chrome/renderer/render_process.h +++ b/chrome/renderer/render_process.h @@ -21,7 +21,11 @@ class TransportDIB; // each renderer. class RenderProcess : public ChildProcess { public: + // This constructor grabs the channel name from the command line arguments. RenderProcess(); + // This constructor uses the given channel name. + RenderProcess(const std::string& channel_name); + ~RenderProcess(); // Get a canvas suitable for drawing and transporting to the browser @@ -48,10 +52,14 @@ class RenderProcess : public ChildProcess { return static_cast<RenderProcess*>(ChildProcess::current()); } + protected: + friend class RenderThread; // Just like in_process_plugins(), but called before RenderProcess is created. static bool InProcessPlugins(); private: + void Init(); + // Look in the shared memory cache for a suitable object to reuse. // result: (output) the memory found // size: the resulting memory will be >= this size, in bytes diff --git a/chrome/renderer/render_process_unittest.cc b/chrome/renderer/render_process_unittest.cc index 5692d81..feac0aa 100644 --- a/chrome/renderer/render_process_unittest.cc +++ b/chrome/renderer/render_process_unittest.cc @@ -17,7 +17,7 @@ class RenderProcessTest : public testing::Test { virtual void SetUp() { // Need a MODE_SERVER to make MODE_CLIENTs (like a RenderThread) happy. channel_ = new IPC::Channel(kThreadName, IPC::Channel::MODE_SERVER, NULL); - render_process_.reset(new RenderProcess()); + render_process_.reset(new RenderProcess(kThreadName)); } virtual void TearDown() { diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 80811b2..e35377a 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -17,6 +17,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/render_messages.h" #include "chrome/common/renderer_preferences.h" +#include "chrome/common/notification_service.h" #include "chrome/common/url_constants.h" #include "chrome/plugin/npobject_util.h" // TODO(port) @@ -54,10 +55,54 @@ using WebKit::WebCache; using WebKit::WebString; -namespace { static const unsigned int kCacheStatsDelayMS = 2000 /* milliseconds */; + static base::LazyInstance<base::ThreadLocalPointer<RenderThread> > lazy_tls( base::LINKER_INITIALIZED); + +//----------------------------------------------------------------------------- +// Methods below are only called on the owner's thread: + +// When we run plugins in process, we actually run them on the render thread, +// which means that we need to make the render thread pump UI events. +RenderThread::RenderThread() + : ChildThread( + base::Thread::Options(RenderProcess::InProcessPlugins() ? + MessageLoop::TYPE_UI : MessageLoop::TYPE_DEFAULT, kV8StackSize)), + plugin_refresh_allowed_(true) { +} + +RenderThread::RenderThread(const std::string& channel_name) + : ChildThread( + base::Thread::Options(RenderProcess::InProcessPlugins() ? + MessageLoop::TYPE_UI : MessageLoop::TYPE_DEFAULT, kV8StackSize)), + plugin_refresh_allowed_(true) { + SetChannelName(channel_name); +} + +RenderThread::~RenderThread() { +} + +RenderThread* RenderThread::current() { + return lazy_tls.Pointer()->Get(); +} + +void RenderThread::AddFilter(IPC::ChannelProxy::MessageFilter* filter) { + channel()->AddFilter(filter); +} + +void RenderThread::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { + channel()->RemoveFilter(filter); +} + +void RenderThread::Resolve(const char* name, size_t length) { + return dns_master_->Resolve(name, length); +} + +void RenderThread::SendHistograms(int sequence_number) { + return histogram_snapshots_->SendHistograms(sequence_number); +} + static WebAppCacheContext* CreateAppCacheContextForRenderer() { return new AppCacheContextImpl(RenderThread::current()); } @@ -83,18 +128,6 @@ class SuicideOnChannelErrorFilter : public IPC::ChannelProxy::MessageFilter { } }; #endif -} // namespace - -// When we run plugins in process, we actually run them on the render thread, -// which means that we need to make the render thread pump UI events. -RenderThread::RenderThread() { - Init(); -} - -RenderThread::RenderThread(const std::string& channel_name) - : ChildThread(channel_name) { - Init(); -} void RenderThread::Init() { lazy_tls.Pointer()->Set(this); @@ -105,7 +138,8 @@ void RenderThread::Init() { CoInitialize(0); #endif - plugin_refresh_allowed_ = true; + ChildThread::Init(); + notification_service_.reset(new NotificationService); cache_stats_factory_.reset( new ScopedRunnableMethodFactory<RenderThread>(this)); @@ -124,13 +158,24 @@ void RenderThread::Init() { #endif } -RenderThread::~RenderThread() { +void RenderThread::CleanUp() { // Shutdown in reverse of the initialization order. RemoveFilter(devtools_agent_filter_.get()); + devtools_agent_filter_ = NULL; WebAppCacheContext::SetFactory(NULL); - if (webkit_client_.get()) + app_cache_dispatcher_.reset(); + histogram_snapshots_.reset(); + dns_master_.reset(); + user_script_slave_.reset(); + visited_link_slave_.reset(); + + if (webkit_client_.get()) { WebKit::shutdown(); + webkit_client_.reset(); + } + notification_service_.reset(); + ChildThread::CleanUp(); lazy_tls.Pointer()->Set(NULL); // TODO(port) @@ -143,26 +188,6 @@ RenderThread::~RenderThread() { #endif } -RenderThread* RenderThread::current() { - return lazy_tls.Pointer()->Get(); -} - -void RenderThread::AddFilter(IPC::ChannelProxy::MessageFilter* filter) { - channel()->AddFilter(filter); -} - -void RenderThread::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { - channel()->RemoveFilter(filter); -} - -void RenderThread::Resolve(const char* name, size_t length) { - return dns_master_->Resolve(name, length); -} - -void RenderThread::SendHistograms(int sequence_number) { - return histogram_snapshots_->SendHistograms(sequence_number); -} - void RenderThread::OnUpdateVisitedLinks(base::SharedMemoryHandle table) { DCHECK(base::SharedMemory::IsHandleValid(table)) << "Bad table handle"; visited_link_slave_->Init(table); @@ -249,6 +274,8 @@ void RenderThread::OnCreateNewView(gfx::NativeViewId parent_hwnd, true, false); #endif + // TODO(darin): once we have a RenderThread per RenderView, this will need to + // change to assert that we are not creating more than one view. RenderView::Create( this, parent_hwnd, waitable_event, MSG_ROUTING_NONE, renderer_prefs, webkit_prefs, new SharedRenderViewCounter(0), view_id); diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index d2a1fcd..5ced914 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -20,7 +20,7 @@ class AppCacheDispatcher; class DevToolsAgentFilter; class FilePath; class ListValue; - +class NotificationService; class RenderDnsMaster; class RendererHistogram; class RendererWebKitClientImpl; @@ -114,7 +114,9 @@ class RenderThread : public RenderThreadBase, private: virtual void OnControlMessageReceived(const IPC::Message& msg); - void Init(); + // Called by the thread base class. + virtual void Init(); + virtual void CleanUp(); void OnUpdateVisitedLinks(base::SharedMemoryHandle table); void OnAddVisitedLinks(const VisitedLinkSlave::Fingerprints& fingerprints); @@ -152,15 +154,24 @@ class RenderThread : public RenderThreadBase, void EnsureWebKitInitialized(); // These objects live solely on the render thread. - scoped_ptr<ScopedRunnableMethodFactory<RenderThread> > cache_stats_factory_; scoped_ptr<VisitedLinkSlave> visited_link_slave_; + scoped_ptr<UserScriptSlave> user_script_slave_; + scoped_ptr<RenderDnsMaster> dns_master_; - scoped_ptr<AppCacheDispatcher> app_cache_dispatcher_; - scoped_refptr<DevToolsAgentFilter> devtools_agent_filter_; + scoped_ptr<RendererHistogramSnapshots> histogram_snapshots_; + + scoped_ptr<ScopedRunnableMethodFactory<RenderThread> > cache_stats_factory_; + + scoped_ptr<NotificationService> notification_service_; + scoped_ptr<RendererWebKitClientImpl> webkit_client_; + scoped_ptr<AppCacheDispatcher> app_cache_dispatcher_; + + scoped_refptr<DevToolsAgentFilter> devtools_agent_filter_; + #if defined(OS_POSIX) scoped_refptr<IPC::ChannelProxy::MessageFilter> suicide_on_channel_error_filter_; diff --git a/chrome/renderer/render_thread_unittest.cc b/chrome/renderer/render_thread_unittest.cc index e22cabb..76491fe 100644 --- a/chrome/renderer/render_thread_unittest.cc +++ b/chrome/renderer/render_thread_unittest.cc @@ -18,8 +18,7 @@ class RenderThreadTest : public testing::Test { virtual void SetUp() { // Need a MODE_SERVER to make MODE_CLIENTs (like a RenderThread) happy. channel_ = new IPC::Channel(kThreadName, IPC::Channel::MODE_SERVER, NULL); - mock_process_.reset(new MockProcess()); - mock_process_->set_main_thread(new RenderThread(kThreadName)); + mock_process_.reset(new MockProcess(new RenderThread(kThreadName))); } virtual void TearDown() { @@ -35,13 +34,15 @@ class RenderThreadTest : public testing::Test { } protected: - MessageLoop message_loop_; + MessageLoopForIO message_loop_; scoped_ptr<MockProcess> mock_process_; IPC::Channel *channel_; }; TEST_F(RenderThreadTest, TestGlobal) { - ASSERT_TRUE(RenderThread::current()); + // Can't reach the RenderThread object on other threads, since it's not + // thread-safe! + ASSERT_FALSE(RenderThread::current()); } TEST_F(RenderThreadTest, TestVisitedMsg) { @@ -54,7 +55,7 @@ TEST_F(RenderThreadTest, TestVisitedMsg) { ASSERT_TRUE(msg); // Message goes nowhere, but this confirms Init() has happened. // Unusually (?), RenderThread() Start()s itself in it's constructor. - mock_process_->main_thread()->Send(msg); + mock_process_->child_thread()->Send(msg); // No need to delete msg; per Message::Send() documentation, "The // implementor takes ownership of the given Message regardless of diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index fd8f61a..21c40e2 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -2814,6 +2814,14 @@ std::string RenderView::GetAltHTMLForTemplate( template_html, &error_strings, "t"); } +MessageLoop* RenderView::GetMessageLoopForIO() { + // Assume that we have only one RenderThread in the process and the owner loop + // of RenderThread is an IO message loop. + if (RenderThread::current()) + return RenderThread::current()->owner_loop(); + return NULL; +} + void RenderView::OnMoveOrResizeStarted() { if (webview()) webview()->HideAutofillPopup(); diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index d83bab9..7624620 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -360,6 +360,12 @@ class RenderView : public RenderWidget, delay_seconds_for_form_state_sync_ = delay_in_seconds; } + // Returns a message loop of type IO that can be used to run I/O jobs. The + // renderer thread is of type TYPE_DEFAULT, so doesn't support everything + // needed by some consumers. The returned thread will be the main thread of + // the renderer, which processes all IPC, to any I/O should be non-blocking. + MessageLoop* GetMessageLoopForIO(); + AudioMessageFilter* audio_message_filter() { return audio_message_filter_; } void OnClearFocusedNode(); diff --git a/chrome/renderer/renderer_main.cc b/chrome/renderer/renderer_main.cc index 55234ec..f5d7811 100644 --- a/chrome/renderer/renderer_main.cc +++ b/chrome/renderer/renderer_main.cc @@ -22,7 +22,6 @@ #include "chrome/common/main_function_params.h" #include "chrome/renderer/renderer_main_platform_delegate.h" #include "chrome/renderer/render_process.h" -#include "chrome/renderer/render_thread.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" @@ -97,11 +96,8 @@ int RendererMain(const MainFunctionParams& parameters) { StatsScope<StatsCounterTimer> startup_timer(chrome::Counters::renderer_main()); - // The main message loop of the renderer services doesn't have IO or UI tasks, - // unless in-process-plugins is used. - MessageLoop main_message_loop(RenderProcess::InProcessPlugins() ? - MessageLoop::TYPE_UI : MessageLoop::TYPE_DEFAULT); - + // The main thread of the renderer services IO. + MessageLoopForIO main_message_loop; std::wstring app_name = chrome::kBrowserAppName; PlatformThread::SetName(WideToASCII(app_name + L"_RendererMain").c_str()); @@ -132,7 +128,6 @@ int RendererMain(const MainFunctionParams& parameters) { { RenderProcess render_process; - render_process.set_main_thread(new RenderThread()); bool run_loop = true; if (!no_sandbox) { run_loop = platform.EnableSandbox(); diff --git a/chrome/renderer/webplugin_delegate_proxy.cc b/chrome/renderer/webplugin_delegate_proxy.cc index 233749b9..34a728d 100644 --- a/chrome/renderer/webplugin_delegate_proxy.cc +++ b/chrome/renderer/webplugin_delegate_proxy.cc @@ -237,9 +237,10 @@ bool WebPluginDelegateProxy::Initialize(const GURL& url, char** argn, IPC::AddChannelSocket(channel_handle.name, channel_handle.socket.fd); #endif + MessageLoop* ipc_message_loop = RenderThread::current()->owner_loop(); scoped_refptr<PluginChannelHost> channel_host = - PluginChannelHost::GetPluginChannelHost( - channel_handle.name, ChildProcess::current()->io_message_loop()); + PluginChannelHost::GetPluginChannelHost(channel_handle.name, + ipc_message_loop); if (!channel_host.get()) return false; diff --git a/chrome/utility/utility_main.cc b/chrome/utility/utility_main.cc index bb669a7..ad66043c 100644 --- a/chrome/utility/utility_main.cc +++ b/chrome/utility/utility_main.cc @@ -20,16 +20,15 @@ // Mainline routine for running as the utility process. int UtilityMain(const MainFunctionParams& parameters) { - // The main message loop of the utility process. - MessageLoop main_message_loop; + // The main thread of the render process. + MessageLoopForIO main_message_loop; std::wstring app_name = chrome::kBrowserAppName; PlatformThread::SetName(WideToASCII(app_name + L"_UtilityMain").c_str()); // Initialize the SystemMonitor base::SystemMonitor::Start(); - ChildProcess utility_process; - utility_process.set_main_thread(new UtilityThread()); + ChildProcess utility_process(new UtilityThread()); #if defined(OS_WIN) sandbox::TargetServices* target_services = parameters.sandbox_info_.TargetServices(); diff --git a/chrome/utility/utility_thread.cc b/chrome/utility/utility_thread.cc index dc9f965..46815b1 100644 --- a/chrome/utility/utility_thread.cc +++ b/chrome/utility/utility_thread.cc @@ -11,13 +11,22 @@ #include "chrome/common/extensions/extension_unpacker.h" #include "chrome/common/render_messages.h" -UtilityThread::UtilityThread() { - ChildProcess::current()->AddRefProcess(); +UtilityThread::UtilityThread() : ChildThread(base::Thread::Options()) { } UtilityThread::~UtilityThread() { } +void UtilityThread::Init() { + ChildThread::Init(); + ChildProcess::current()->AddRefProcess(); +} + +void UtilityThread::CleanUp() { + // Shutdown in reverse of the initialization order. + ChildThread::CleanUp(); +} + void UtilityThread::OnControlMessageReceived(const IPC::Message& msg) { IPC_BEGIN_MESSAGE_MAP(UtilityThread, msg) IPC_MESSAGE_HANDLER(UtilityMsg_UnpackExtension, OnUnpackExtension) diff --git a/chrome/utility/utility_thread.h b/chrome/utility/utility_thread.h index 4fad53e..1c128ad 100644 --- a/chrome/utility/utility_thread.h +++ b/chrome/utility/utility_thread.h @@ -7,6 +7,7 @@ #include <string> +#include "base/thread.h" #include "chrome/common/child_thread.h" class GURL; @@ -30,6 +31,10 @@ class UtilityThread : public ChildThread { // IPC messages for web resource service. void OnUnpackWebResource(const std::string& resource_data); + // Called by the thread base class + virtual void Init(); + virtual void CleanUp(); + DISALLOW_COPY_AND_ASSIGN(UtilityThread); }; diff --git a/chrome/worker/worker_main.cc b/chrome/worker/worker_main.cc index d6304ab..03831b8b 100644 --- a/chrome/worker/worker_main.cc +++ b/chrome/worker/worker_main.cc @@ -20,16 +20,15 @@ // Mainline routine for running as the worker process. int WorkerMain(const MainFunctionParams& parameters) { - // The main message loop of the worker process. - MessageLoop main_message_loop; + // The main thread of the render process. + MessageLoopForIO main_message_loop; std::wstring app_name = chrome::kBrowserAppName; PlatformThread::SetName(WideToASCII(app_name + L"_WorkerMain").c_str()); // Initialize the SystemMonitor base::SystemMonitor::Start(); - ChildProcess worker_process; - worker_process.set_main_thread(new WorkerThread()); + ChildProcess worker_process(new WorkerThread()); #if defined(OS_WIN) sandbox::TargetServices* target_services = parameters.sandbox_info_.TargetServices(); diff --git a/chrome/worker/worker_thread.cc b/chrome/worker/worker_thread.cc index e4be0b9..7663b7a 100644 --- a/chrome/worker/worker_thread.cc +++ b/chrome/worker/worker_thread.cc @@ -15,22 +15,37 @@ static base::LazyInstance<base::ThreadLocalPointer<WorkerThread> > lazy_tls( base::LINKER_INITIALIZED); -WorkerThread::WorkerThread() { - lazy_tls.Pointer()->Set(this); - webkit_client_.reset(new WorkerWebKitClientImpl); - WebKit::initialize(webkit_client_.get()); +WorkerThread::WorkerThread() + : ChildThread(base::Thread::Options(MessageLoop::TYPE_DEFAULT, + kV8StackSize)) { } WorkerThread::~WorkerThread() { - // Shutdown in reverse of the initialization order. - WebKit::shutdown(); - lazy_tls.Pointer()->Set(NULL); } WorkerThread* WorkerThread::current() { return lazy_tls.Pointer()->Get(); } +void WorkerThread::Init() { + lazy_tls.Pointer()->Set(this); + ChildThread::Init(); + webkit_client_.reset(new WorkerWebKitClientImpl); + WebKit::initialize(webkit_client_.get()); +} + +void WorkerThread::CleanUp() { + // Shutdown in reverse of the initialization order. + + if (webkit_client_.get()) { + WebKit::shutdown(); + webkit_client_.reset(); + } + + ChildThread::CleanUp(); + lazy_tls.Pointer()->Set(NULL); +} + void WorkerThread::OnControlMessageReceived(const IPC::Message& msg) { IPC_BEGIN_MESSAGE_MAP(WorkerThread, msg) IPC_MESSAGE_HANDLER(WorkerProcessMsg_CreateWorker, OnCreateWorker) diff --git a/chrome/worker/worker_thread.h b/chrome/worker/worker_thread.h index 27d0abf..6bea33c 100644 --- a/chrome/worker/worker_thread.h +++ b/chrome/worker/worker_thread.h @@ -5,6 +5,7 @@ #ifndef CHROME_WORKER_WORKER_THREAD_H_ #define CHROME_WORKER_WORKER_THREAD_H_ +#include "base/thread.h" #include "chrome/common/child_thread.h" class GURL; @@ -21,6 +22,10 @@ class WorkerThread : public ChildThread { private: virtual void OnControlMessageReceived(const IPC::Message& msg); + // Called by the thread base class + virtual void Init(); + virtual void CleanUp(); + void OnCreateWorker(const GURL& url, int route_id); scoped_ptr<WorkerWebKitClientImpl> webkit_client_; diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc index eba506d..d96e413 100644 --- a/ipc/ipc_channel_proxy.cc +++ b/ipc/ipc_channel_proxy.cc @@ -272,10 +272,6 @@ void ChannelProxy::RemoveFilter(MessageFilter* filter) { context_.get(), &Context::OnRemoveFilter, filter)); } -void ChannelProxy::ClearIPCMessageLoop() { - context()->ClearIPCMessageLoop(); -} - #if defined(OS_POSIX) // See the TODO regarding lazy initialization of the channel in // ChannelProxy::Init(). diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h index 77ddd87..1aed33e 100644 --- a/ipc/ipc_channel_proxy.h +++ b/ipc/ipc_channel_proxy.h @@ -117,9 +117,6 @@ class ChannelProxy : public Message::Sender { void AddFilter(MessageFilter* filter); void RemoveFilter(MessageFilter* filter); - // Called to clear the pointer to the IPC message loop when it's going away. - void ClearIPCMessageLoop(); - #if defined(OS_POSIX) // Calls through to the underlying channel's methods. // TODO(playmobil): For now this is only implemented in the case of @@ -143,7 +140,6 @@ class ChannelProxy : public Message::Sender { Context(Channel::Listener* listener, MessageFilter* filter, MessageLoop* ipc_thread); virtual ~Context() { } - void ClearIPCMessageLoop() { ipc_message_loop_ = NULL; } MessageLoop* ipc_message_loop() const { return ipc_message_loop_; } const std::string& channel_id() const { return channel_id_; } diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt index d4cc7b9..5818a12 100644 --- a/tools/valgrind/memcheck/suppressions.txt +++ b/tools/valgrind/memcheck/suppressions.txt @@ -717,10 +717,8 @@ Memcheck:Leak fun:_Znwj fun:_ZN3IPC11SyncChannelC1ERKSsNS_7Channel4ModeEPNS3_8ListenerEPNS_12ChannelProxy13MessageFilterEP11MessageLoopbPN4base13WaitableEventE - ... fun:_ZN11ChildThread4InitEv - fun:_ZN11ChildThreadC2ESs - fun:_ZN12RenderThreadC1ERKSs + fun:_ZN12RenderThread4InitEv } { bug_16129 @@ -911,15 +909,16 @@ { bug_17385 Memcheck:Leak - fun:_Znwj* + fun:_Znw* fun:_ZN14RevocableStoreC2Ev + fun:_ZN3IPC7Channel11ChannelImplC2ERKSsNS0_4ModeEPNS0_8ListenerE fun:_ZN3IPC7Channel11ChannelImplC1ERKSsNS0_4ModeEPNS0_8ListenerE + fun:_ZN3IPC7ChannelC2ERKSsNS0_4ModeEPNS0_8ListenerE fun:_ZN3IPC7ChannelC1ERKSsNS0_4ModeEPNS0_8ListenerE fun:_ZN3IPC12ChannelProxy7Context13CreateChannelERKSsRKNS_7Channel4ModeE fun:_ZN3IPC12ChannelProxy4InitERKSsNS_7Channel4ModeEP11MessageLoopb fun:_ZN3IPC12ChannelProxyC2ERKSsNS_7Channel4ModeEP11MessageLoopPNS0_7ContextEb + fun:_ZN3IPC11SyncChannelC2ERKSsNS_7Channel4ModeEPNS3_8ListenerEPNS_12ChannelProxy13MessageFilterEP11MessageLoopbPN4base13WaitableEventE fun:_ZN3IPC11SyncChannelC1ERKSsNS_7Channel4ModeEPNS3_8ListenerEPNS_12ChannelProxy13MessageFilterEP11MessageLoopbPN4base13WaitableEventE fun:_ZN11ChildThread4InitEv - fun:_ZN11ChildThreadC2ESs - fun:_ZN12RenderThreadC1ERKSs } |