Commit 08f0d612 authored by sky@chromium.org's avatar sky@chromium.org

Attempt 2 at fixing crash in ProcessPendingTabs. This differs from the

one you reviewed in that I changed a DCHECK in ProcessPendingTabs to
an if. See the comments there for details (this was triggering a
failure on chromeos).

Description from last attempt:

With the current code, during closing a window with unload handlers if
a tab is disconnected then we delay cleanup by way of PostTask with
the tab being disconnected. During the time between when the
disconnect happens and the task is run the unloader handler sets still
maintain a reference to the tab. If ProcessPendingTabs is invoked
during this window and the tab is deleted, then we can crash.

I'm fixing it by making disconnect remove from the sets immediately,
but delay the call to ProcessPendingTabs. I'm also doing a similar
thing in TabDetachedAt.

BUG=15620
TEST=none

Review URL: http://codereview.chromium.org/6321001

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71388 0039d316-1c4b-4281-b951-d872f2087c98
parent 66fc0772
...@@ -2847,7 +2847,7 @@ void Browser::CloseContents(TabContents* source) { ...@@ -2847,7 +2847,7 @@ void Browser::CloseContents(TabContents* source) {
// waiting for unload to fire. Don't actually try to close the tab as it // waiting for unload to fire. Don't actually try to close the tab as it
// will go down the slow shutdown path instead of the fast path of killing // will go down the slow shutdown path instead of the fast path of killing
// all the renderer processes. // all the renderer processes.
ClearUnloadState(source); ClearUnloadState(source, true);
return; return;
} }
...@@ -3208,12 +3208,10 @@ void Browser::Observe(NotificationType type, ...@@ -3208,12 +3208,10 @@ void Browser::Observe(NotificationType type,
switch (type.value) { switch (type.value) {
case NotificationType::TAB_CONTENTS_DISCONNECTED: case NotificationType::TAB_CONTENTS_DISCONNECTED:
if (is_attempting_to_close_browser_) { if (is_attempting_to_close_browser_) {
// Need to do this asynchronously as it will close the tab, which is // Pass in false so that we delay processing. We need to delay the
// currently on the call stack above us. // processing as it may close the tab, which is currently on the call
MessageLoop::current()->PostTask( // stack above us.
FROM_HERE, ClearUnloadState(Source<TabContents>(source).ptr(), false);
method_factory_.NewRunnableMethod(&Browser::ClearUnloadState,
Source<TabContents>(source).ptr()));
} }
break; break;
...@@ -3852,7 +3850,12 @@ void Browser::SyncHistoryWithTabs(int index) { ...@@ -3852,7 +3850,12 @@ void Browser::SyncHistoryWithTabs(int index) {
// Browser, OnBeforeUnload handling (private): // Browser, OnBeforeUnload handling (private):
void Browser::ProcessPendingTabs() { void Browser::ProcessPendingTabs() {
DCHECK(is_attempting_to_close_browser_); if (!is_attempting_to_close_browser_) {
// Because we might invoke this after a delay it's possible for the value of
// is_attempting_to_close_browser_ to have changed since we scheduled the
// task.
return;
}
if (HasCompletedUnloadProcessing()) { if (HasCompletedUnloadProcessing()) {
// We've finished all the unload events and can proceed to close the // We've finished all the unload events and can proceed to close the
...@@ -3870,7 +3873,7 @@ void Browser::ProcessPendingTabs() { ...@@ -3870,7 +3873,7 @@ void Browser::ProcessPendingTabs() {
if (tab->render_view_host()) { if (tab->render_view_host()) {
tab->render_view_host()->FirePageBeforeUnload(false); tab->render_view_host()->FirePageBeforeUnload(false);
} else { } else {
ClearUnloadState(tab); ClearUnloadState(tab, true);
} }
} else if (!tabs_needing_unload_fired_.empty()) { } else if (!tabs_needing_unload_fired_.empty()) {
// We've finished firing all beforeunload events and can proceed with unload // We've finished firing all beforeunload events and can proceed with unload
...@@ -3887,7 +3890,7 @@ void Browser::ProcessPendingTabs() { ...@@ -3887,7 +3890,7 @@ void Browser::ProcessPendingTabs() {
if (tab->render_view_host()) { if (tab->render_view_host()) {
tab->render_view_host()->ClosePage(false, -1, -1); tab->render_view_host()->ClosePage(false, -1, -1);
} else { } else {
ClearUnloadState(tab); ClearUnloadState(tab, true);
} }
} else { } else {
NOTREACHED(); NOTREACHED();
...@@ -3927,18 +3930,23 @@ bool Browser::RemoveFromSet(UnloadListenerSet* set, TabContents* tab) { ...@@ -3927,18 +3930,23 @@ bool Browser::RemoveFromSet(UnloadListenerSet* set, TabContents* tab) {
return false; return false;
} }
void Browser::ClearUnloadState(TabContents* tab) { void Browser::ClearUnloadState(TabContents* tab, bool process_now) {
// Closing of browser could be canceled (via IsClosingPermitted) between the // Closing of browser could be canceled (via IsClosingPermitted) between the
// time when request was initiated and when this method is called, so check // time when request was initiated and when this method is called, so check
// for is_attempting_to_close_browser_ flag before proceeding. // for is_attempting_to_close_browser_ flag before proceeding.
if (is_attempting_to_close_browser_) { if (is_attempting_to_close_browser_) {
RemoveFromSet(&tabs_needing_before_unload_fired_, tab); RemoveFromSet(&tabs_needing_before_unload_fired_, tab);
RemoveFromSet(&tabs_needing_unload_fired_, tab); RemoveFromSet(&tabs_needing_unload_fired_, tab);
ProcessPendingTabs(); if (process_now) {
ProcessPendingTabs();
} else {
MessageLoop::current()->PostTask(
FROM_HERE,
method_factory_.NewRunnableMethod(&Browser::ProcessPendingTabs));
}
} }
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Browser, In-progress download termination handling (private): // Browser, In-progress download termination handling (private):
...@@ -4097,6 +4105,14 @@ void Browser::TabDetachedAtImpl(TabContentsWrapper* contents, int index, ...@@ -4097,6 +4105,14 @@ void Browser::TabDetachedAtImpl(TabContentsWrapper* contents, int index,
find_bar_controller_->ChangeTabContents(NULL); find_bar_controller_->ChangeTabContents(NULL);
} }
if (is_attempting_to_close_browser_) {
// If this is the last tab with unload handlers, then ProcessPendingTabs
// would call back into the TabStripModel (which is invoking this method on
// us). Avoid that by passing in false so that the call to
// ProcessPendingTabs is delayed.
ClearUnloadState(contents->tab_contents(), false);
}
registrar_.Remove(this, NotificationType::TAB_CONTENTS_DISCONNECTED, registrar_.Remove(this, NotificationType::TAB_CONTENTS_DISCONNECTED,
Source<TabContentsWrapper>(contents)); Source<TabContentsWrapper>(contents));
} }
......
...@@ -909,8 +909,13 @@ class Browser : public TabHandlerDelegate, ...@@ -909,8 +909,13 @@ class Browser : public TabHandlerDelegate,
// Cleans up state appropriately when we are trying to close the browser and // Cleans up state appropriately when we are trying to close the browser and
// the tab has finished firing its unload handler. We also use this in the // the tab has finished firing its unload handler. We also use this in the
// cases where a tab crashes or hangs even if the beforeunload/unload haven't // cases where a tab crashes or hangs even if the beforeunload/unload haven't
// successfully fired. // successfully fired. If |process_now| is true |ProcessPendingTabs| is
void ClearUnloadState(TabContents* tab); // invoked immediately, otherwise it is invoked after a delay (PostTask).
//
// Typically you'll want to pass in true for |process_now|. Passing in true
// may result in deleting |tab|. If you know that shouldn't happen (because of
// the state of the stack), pass in false.
void ClearUnloadState(TabContents* tab, bool process_now);
// In-progress download termination handling ///////////////////////////////// // In-progress download termination handling /////////////////////////////////
......
...@@ -40,9 +40,6 @@ REGEX : `anonymous namespace'::purecall$ ...@@ -40,9 +40,6 @@ REGEX : `anonymous namespace'::purecall$
# is the only one on the stack, so ignore that instance # is the only one on the stack, so ignore that instance
REGEX : `anonymous namespace'::invalidparameter$ REGEX : `anonymous namespace'::invalidparameter$
# 15620
PREFIX : browser::processpendingtabs___browser::clearunloadstate
# 47207 # 47207
PREFIX : messageloop::runtask___messageloop::deferorrunpendingtask___messageloop::dodelayedwork___base::messagepumpforio::dorunloop___base::messagepumpwin::run___messageloop::runinternal___messageloop::run___base::thread::run___base::thread::threadmain___`anonymous namespace'::threadfunc PREFIX : messageloop::runtask___messageloop::deferorrunpendingtask___messageloop::dodelayedwork___base::messagepumpforio::dorunloop___base::messagepumpwin::run___messageloop::runinternal___messageloop::run___base::thread::run___base::thread::threadmain___`anonymous namespace'::threadfunc
PREFIX : messageloop::runtask___messageloop::dodelayedwork___base::messagepumpforio::dorunloop___base::messagepumpwin::run___messageloop::runinternal___messageloop::run___base::thread::run___base::thread::threadmain___`anonymous namespace'::threadfunc PREFIX : messageloop::runtask___messageloop::dodelayedwork___base::messagepumpforio::dorunloop___base::messagepumpwin::run___messageloop::runinternal___messageloop::run___base::thread::run___base::thread::threadmain___`anonymous namespace'::threadfunc
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment