diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-05 20:36:07 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-05 20:36:07 +0000 |
commit | 5fa1c542119e3976128c1de3692262292d105ffe (patch) | |
tree | 53a12d0f64b46699310be057067ff3d5437ed6f0 /chrome | |
parent | c12fac94defb54d0a16ca2e1a27fc4023a1acba7 (diff) | |
download | chromium_src-5fa1c542119e3976128c1de3692262292d105ffe.zip chromium_src-5fa1c542119e3976128c1de3692262292d105ffe.tar.gz chromium_src-5fa1c542119e3976128c1de3692262292d105ffe.tar.bz2 |
POSIX: Don't allow onunload handlers to hang a renderer forever.
(Reland of r15025 which was reverted in r15095. |exit| has been
changed to |_exit| to save running the onexit handlers while another
thread is still in V8 code.)
On POSIX one can install an unload handler which loops forever and
leave behind a renderer process which eats 100% CPU forever.
This is because the terminate signals (ViewMsg_ShouldClose and the
error from the IPC channel) are routed to the main message loop but
never processes (because that message loop is stuck in V8).
One could make the browser SIGKILL the renderers, but that leaves open
a large window where a browser failure (or a user, manually
terminating the browser because "it's stuck") will leave behind a
process eating all the CPU.
On Windows we don't have this issue because all the processes are in a
job so when the parent dies, all the children are killed too.
http://codereview.chromium.org/100222
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15332 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/common/ipc_channel_proxy.cc | 3 | ||||
-rw-r--r-- | chrome/common/ipc_channel_proxy.h | 4 | ||||
-rw-r--r-- | chrome/renderer/render_thread.cc | 27 | ||||
-rw-r--r-- | chrome/renderer/render_thread.h | 5 |
4 files changed, 39 insertions, 0 deletions
diff --git a/chrome/common/ipc_channel_proxy.cc b/chrome/common/ipc_channel_proxy.cc index 7668923..6b18adb 100644 --- a/chrome/common/ipc_channel_proxy.cc +++ b/chrome/common/ipc_channel_proxy.cc @@ -81,6 +81,9 @@ void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) { // Called on the IPC::Channel thread void ChannelProxy::Context::OnChannelError() { + for (size_t i = 0; i < filters_.size(); ++i) + filters_[i]->OnChannelError(); + // See above comment about using listener_message_loop_ here. listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &Context::OnDispatchError)); diff --git a/chrome/common/ipc_channel_proxy.h b/chrome/common/ipc_channel_proxy.h index 1a68971..3773cd0 100644 --- a/chrome/common/ipc_channel_proxy.h +++ b/chrome/common/ipc_channel_proxy.h @@ -64,6 +64,10 @@ class ChannelProxy : public Message::Sender { // have received the internal Hello message from the peer. virtual void OnChannelConnected(int32 peer_pid) {} + // Called when there is an error on the channel, typically that the channel + // has been closed. + virtual void OnChannelError() {} + // Called to inform the filter that the IPC channel will be destroyed. // OnFilterRemoved is called immediately after this. virtual void OnChannelClosing() {} diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index afe3f38..7bf055e 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -103,6 +103,28 @@ static WebAppCacheContext* CreateAppCacheContextForRenderer() { return new AppCacheContextImpl(RenderThread::current()); } +#if defined(OS_POSIX) +class SuicideOnChannelErrorFilter : public IPC::ChannelProxy::MessageFilter { + void OnChannelError() { + // On POSIX, at least, one can install an unload handler which loops + // forever and leave behind a renderer process which eats 100% CPU forever. + // + // This is because the terminate signals (ViewMsg_ShouldClose and the error + // from the IPC channel) are routed to the main message loop but never + // processed (because that message loop is stuck in V8). + // + // One could make the browser SIGKILL the renderers, but that leaves open a + // large window where a browser failure (or a user, manually terminating + // the browser because "it's stuck") will leave behind a process eating all + // the CPU. + // + // So, we install a filter on the channel so that we can process this event + // here and kill the process. + _exit(0); + } +}; +#endif + void RenderThread::Init() { #if defined(OS_WIN) // If you are running plugins in this thread you need COM active but in @@ -124,6 +146,11 @@ void RenderThread::Init() { WebAppCacheContext::SetFactory(CreateAppCacheContextForRenderer); devtools_agent_filter_ = new DevToolsAgentFilter(); AddFilter(devtools_agent_filter_.get()); + +#if defined(OS_POSIX) + suicide_on_channel_error_filter_ = new SuicideOnChannelErrorFilter; + AddFilter(suicide_on_channel_error_filter_.get()); +#endif } void RenderThread::CleanUp() { diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index 221ee53..123c9dc 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -160,6 +160,11 @@ class RenderThread : public RenderThreadBase, scoped_refptr<DevToolsAgentFilter> devtools_agent_filter_; +#if defined(OS_POSIX) + scoped_refptr<IPC::ChannelProxy::MessageFilter> + suicide_on_channel_error_filter_; +#endif + // If true, then a GetPlugins call is allowed to rescan the disk. bool plugin_refresh_allowed_; |