diff options
author | mbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-25 08:56:06 +0000 |
---|---|---|
committer | mbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-25 08:56:06 +0000 |
commit | 0935275316a0edc8595edf0fc268f69adf1e0d86 (patch) | |
tree | 91688b63b13936b46eb05c9585493b9a0428b9d9 | |
parent | fa1b5dace74e745d2484012f98559d69825078f5 (diff) | |
download | chromium_src-0935275316a0edc8595edf0fc268f69adf1e0d86.zip chromium_src-0935275316a0edc8595edf0fc268f69adf1e0d86.tar.gz chromium_src-0935275316a0edc8595edf0fc268f69adf1e0d86.tar.bz2 |
Modify the RenderThread to track the number of widgets
and "hidden widgets" which are running through that thread.
By knowing the if the widgets are all hidden, the thread
can accurately inform V8 when it is idle so that V8 can
better cleanup unused memory when idle.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/174303
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24227 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/renderer/mock_render_thread.h | 10 | ||||
-rw-r--r-- | chrome/renderer/render_thread.cc | 65 | ||||
-rw-r--r-- | chrome/renderer/render_thread.h | 26 | ||||
-rw-r--r-- | chrome/renderer/render_widget.cc | 22 | ||||
-rw-r--r-- | chrome/renderer/render_widget.h | 5 |
5 files changed, 115 insertions, 13 deletions
diff --git a/chrome/renderer/mock_render_thread.h b/chrome/renderer/mock_render_thread.h index 0e5bab8..a76180e 100644 --- a/chrome/renderer/mock_render_thread.h +++ b/chrome/renderer/mock_render_thread.h @@ -41,10 +41,12 @@ class MockRenderThread : public RenderThreadBase { virtual bool Send(IPC::Message* msg); // Our mock thread doesn't do filtering. - virtual void AddFilter(IPC::ChannelProxy::MessageFilter* filter) { - } - virtual void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { - } + virtual void AddFilter(IPC::ChannelProxy::MessageFilter* filter) { } + virtual void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { } + + // Our mock thread doesn't deal with hidden and restored tabs. + virtual void WidgetHidden() { } + virtual void WidgetRestored() { } ////////////////////////////////////////////////////////////////////////// // The following functions are called by the test itself. diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index c6e5b17..e669e71 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/lazy_instance.h" +#include "base/logging.h" #include "base/shared_memory.h" #include "base/stats_table.h" #include "base/thread_local.h" @@ -58,6 +59,8 @@ using WebKit::WebString; namespace { static const unsigned int kCacheStatsDelayMS = 2000 /* milliseconds */; +static const double kInitialIdleHandlerDelayS = 1.0 /* seconds */; + static base::LazyInstance<base::ThreadLocalPointer<RenderThread> > lazy_tls( base::LINKER_INITIALIZED); @@ -105,8 +108,11 @@ void RenderThread::Init() { #endif plugin_refresh_allowed_ = true; - cache_stats_factory_.reset( - new ScopedRunnableMethodFactory<RenderThread>(this)); + cache_stats_task_pending_ = false; + widget_count_ = 0; + hidden_widget_count_ = 0; + idle_notification_delay_in_s_ = kInitialIdleHandlerDelayS; + task_factory_.reset(new ScopedRunnableMethodFactory<RenderThread>(this)); visited_link_slave_.reset(new VisitedLinkSlave()); user_script_slave_.reset(new UserScriptSlave()); @@ -156,6 +162,30 @@ void RenderThread::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { channel()->RemoveFilter(filter); } +void RenderThread::WidgetHidden() { + DCHECK(hidden_widget_count_ <= widget_count_); + hidden_widget_count_++ ; + if (hidden_widget_count_ == widget_count_) { + // Reset the delay. + idle_notification_delay_in_s_ = kInitialIdleHandlerDelayS; + + // Schedule the IdleHandler to wakeup in a bit. + MessageLoop::current()->PostDelayedTask(FROM_HERE, + task_factory_->NewRunnableMethod(&RenderThread::IdleHandler), + static_cast<int64>(floor(idle_notification_delay_in_s_)) * 1000); + } +} + +void RenderThread::WidgetRestored() { + DCHECK(hidden_widget_count_ > 0); + hidden_widget_count_--; + + // Note: we may have a timer pending to call the IdleHandler (see the + // WidgetHidden() code). But we don't bother to cancel it as it is + // benign and won't do anything if the tab is un-hidden when it is + // called. +} + void RenderThread::Resolve(const char* name, size_t length) { return dns_master_->Resolve(name, length); } @@ -316,15 +346,17 @@ void RenderThread::InformHostOfCacheStats() { WebCache::UsageStats stats; WebCache::getUsageStats(&stats); Send(new ViewHostMsg_UpdatedCacheStats(stats)); + cache_stats_task_pending_ = false; } void RenderThread::InformHostOfCacheStatsLater() { // Rate limit informing the host of our cache stats. - if (!cache_stats_factory_->empty()) + if (cache_stats_task_pending_) return; + cache_stats_task_pending_ = true; MessageLoop::current()->PostDelayedTask(FROM_HERE, - cache_stats_factory_->NewRunnableMethod( + task_factory_->NewRunnableMethod( &RenderThread::InformHostOfCacheStats), kCacheStatsDelayMS); } @@ -424,6 +456,31 @@ void RenderThread::EnsureWebKitInitialized() { } } +void RenderThread::IdleHandler() { + // It is possible that the timer was set while the widgets were idle, + // but that they are no longer idle. If so, just return. + if (hidden_widget_count_ < widget_count_) + return; + + LOG(INFO) << "RenderThread calling v8 IdleNotification for " << this; + + // When V8::IdleNotification returns true, it means that it has cleaned up + // as much as it can. There is no point in continuing to call it. + if (!v8::V8::IdleNotification(false)) { + // Dampen the delay using the algorithm: + // delay = delay + 1 / (delay + 2) + // Using floor(delay) has a dampening effect such as: + // 1s, 1, 1, 2, 2, 2, 2, 3, 3, ... + idle_notification_delay_in_s_ += + 1.0 / (idle_notification_delay_in_s_ + 2.0); + + // Schedule the next timer. + MessageLoop::current()->PostDelayedTask(FROM_HERE, + task_factory_->NewRunnableMethod(&RenderThread::IdleHandler), + static_cast<int64>(floor(idle_notification_delay_in_s_)) * 1000); + } +} + void RenderThread::OnExtensionMessageInvoke(const std::string& function_name, const ListValue& args) { RendererExtensionBindings::Invoke(function_name, args, NULL); diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index 5c22236..007bac4 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -49,6 +49,10 @@ class RenderThreadBase { virtual void AddFilter(IPC::ChannelProxy::MessageFilter* filter) = 0; virtual void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) = 0; + + // Called by a RenderWidget when it is hidden or restored. + virtual void WidgetHidden() = 0; + virtual void WidgetRestored() = 0; }; // The RenderThread class represents a background thread where RenderView @@ -79,15 +83,20 @@ class RenderThread : public RenderThreadBase, } virtual void AddRoute(int32 routing_id, IPC::Channel::Listener* listener) { + widget_count_++; return ChildThread::AddRoute(routing_id, listener); } virtual void RemoveRoute(int32 routing_id) { + widget_count_--; return ChildThread::RemoveRoute(routing_id); } virtual void AddFilter(IPC::ChannelProxy::MessageFilter* filter); virtual void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter); + virtual void WidgetHidden(); + virtual void WidgetRestored(); + VisitedLinkSlave* visited_link_slave() const { return visited_link_slave_.get(); } @@ -163,8 +172,11 @@ class RenderThread : public RenderThreadBase, // We initialize WebKit as late as possible. void EnsureWebKitInitialized(); + // A task we invoke periodically to assist with idle cleanup. + void IdleHandler(); + // These objects live solely on the render thread. - scoped_ptr<ScopedRunnableMethodFactory<RenderThread> > cache_stats_factory_; + scoped_ptr<ScopedRunnableMethodFactory<RenderThread> > task_factory_; scoped_ptr<VisitedLinkSlave> visited_link_slave_; scoped_ptr<UserScriptSlave> user_script_slave_; scoped_ptr<RenderDnsMaster> dns_master_; @@ -183,6 +195,18 @@ class RenderThread : public RenderThreadBase, // If true, then a GetPlugins call is allowed to rescan the disk. bool plugin_refresh_allowed_; + // Is there a pending task for doing CacheStats. + bool cache_stats_task_pending_; + + // The count of RenderWidgets running through this thread. + int widget_count_; + + // The count of hidden RenderWidgets running through this thread. + int hidden_widget_count_; + + // The current value of the idle notification timer delay. + double idle_notification_delay_in_s_; + DISALLOW_COPY_AND_ASSIGN(RenderThread); }; diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc index 863e73d..cd722ba 100644 --- a/chrome/renderer/render_widget.cc +++ b/chrome/renderer/render_widget.cc @@ -180,8 +180,10 @@ void RenderWidget::OnClose() { closing_ = true; // Browser correspondence is no longer needed at this point. - if (routing_id_ != MSG_ROUTING_NONE) + if (routing_id_ != MSG_ROUTING_NONE) { render_thread_->RemoveRoute(routing_id_); + SetHidden(false); + } // If there is a Send call on the stack, then it could be dangerous to close // now. Post a task that only gets invoked when there are no nested message @@ -203,7 +205,7 @@ void RenderWidget::OnResize(const gfx::Size& new_size, resizer_rect_ = resizer_rect; // TODO(darin): We should not need to reset this here. - is_hidden_ = false; + SetHidden(false); needs_repainting_on_restore_ = false; // We shouldn't be asked to resize to our current size. @@ -232,7 +234,7 @@ void RenderWidget::OnResize(const gfx::Size& new_size, void RenderWidget::OnWasHidden() { // Go into a mode where we stop generating paint and scrolling events. - is_hidden_ = true; + SetHidden(true); } void RenderWidget::OnWasRestored(bool needs_repainting) { @@ -241,7 +243,7 @@ void RenderWidget::OnWasRestored(bool needs_repainting) { return; // See OnWasHidden - is_hidden_ = false; + SetHidden(false); if (!needs_repainting && !needs_repainting_on_restore_) return; @@ -736,6 +738,18 @@ void RenderWidget::OnSetTextDirection(WebTextDirection direction) { webwidget_->setTextDirection(direction); } +void RenderWidget::SetHidden(bool hidden) { + if (is_hidden_ == hidden) + return; + + // The status has changed. Tell the RenderThread about it. + is_hidden_ = hidden; + if (is_hidden_) + render_thread_->WidgetHidden(); + else + render_thread_->WidgetRestored(); +} + void RenderWidget::SetBackground(const SkBitmap& background) { background_ = background; // Generate a full repaint. diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h index 7987d28..ae54a55 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -151,6 +151,11 @@ class RenderWidget : public IPC::Channel::Listener, // browser side has updated the screen for a newly painted region. virtual void DidPaint() {} + // Sets the "hidden" state of this widget. All accesses to is_hidden_ should + // use this method so that we can properly inform the RenderThread of our + // state. + void SetHidden(bool hidden); + // True if a PaintRect_ACK message is pending. bool paint_reply_pending() const { return paint_reply_pending_; |