diff options
author | mmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-20 15:05:08 +0000 |
---|---|---|
committer | mmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-20 15:05:08 +0000 |
commit | 28a05f3af8c3031d23b55630d811fc5c18505a3c (patch) | |
tree | cb4691e3b846236408c45695b72051412f68dbf3 /chrome/browser/prerender/prerender_tracker.cc | |
parent | 2937a8c72bfd28188ccd0b3f3b54bc3303b5f537 (diff) | |
download | chromium_src-28a05f3af8c3031d23b55630d811fc5c18505a3c.zip chromium_src-28a05f3af8c3031d23b55630d811fc5c18505a3c.tar.gz chromium_src-28a05f3af8c3031d23b55630d811fc5c18505a3c.tar.bz2 |
Safely cancel prerenders on threads other than the UI thread.
Previously, prerendering was cancelled on the IO thread by not
doing something, and then passing a task to the UI thread
to abort the prerender. This resulted in a race which could
result in swapping in the prerender before the task was
executed. This fixes that.
BUG=83062
TEST=PrerenderStatusManagerTests, PrerenderBrowserTests
Review URL: http://codereview.chromium.org/7038012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86082 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/prerender/prerender_tracker.cc')
-rw-r--r-- | chrome/browser/prerender/prerender_tracker.cc | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/chrome/browser/prerender/prerender_tracker.cc b/chrome/browser/prerender/prerender_tracker.cc new file mode 100644 index 0000000..c82acd3 --- /dev/null +++ b/chrome/browser/prerender/prerender_tracker.cc @@ -0,0 +1,202 @@ +// Copyright (c) 2011 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 "base/logging.h" +#include "chrome/browser/prerender/prerender_manager.h" +#include "chrome/browser/prerender/prerender_tracker.h" +#include "content/browser/browser_thread.h" + +namespace prerender { + +namespace { + +void DestroyPreloadForRenderView( + const base::WeakPtr<PrerenderManager>& prerender_manager_weak_ptr, + int child_id, + int route_id, + FinalStatus final_status) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + PrerenderManager* prerender_manager = prerender_manager_weak_ptr.get(); + if (!prerender_manager) + return; + + prerender_manager->DestroyPreloadForChildRouteIdPair( + std::make_pair(child_id, route_id), + final_status); +} + +} // namespace + +struct RenderViewInfo { + explicit RenderViewInfo(PrerenderManager* prerender_manager) + : final_status(FINAL_STATUS_MAX), + prerender_manager(prerender_manager->AsWeakPtr()) { + } + + FinalStatus final_status; + base::WeakPtr<PrerenderManager> prerender_manager; +}; + +// static +PrerenderTracker* PrerenderTracker::GetInstance() { + return Singleton<PrerenderTracker>::get(); +} + +bool PrerenderTracker::TryUse(int child_id, int route_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + FinalStatus final_status = SetFinalStatus(child_id, route_id, + FINAL_STATUS_USED); + return final_status == FINAL_STATUS_USED; +} + +bool PrerenderTracker::TryCancel( + int child_id, + int route_id, + FinalStatus final_status) { + DCHECK_NE(FINAL_STATUS_USED, final_status); + DCHECK(final_status >= 0 && final_status < FINAL_STATUS_MAX); + + final_status = SetFinalStatus(child_id, route_id, final_status); + return final_status != FINAL_STATUS_USED && final_status != FINAL_STATUS_MAX; +} + +bool PrerenderTracker::TryCancelOnIOThread( + int child_id, + int route_id, + FinalStatus final_status) { + DCHECK_NE(FINAL_STATUS_USED, final_status); + DCHECK(final_status >= 0 && final_status < FINAL_STATUS_MAX); + + if (!IsPrerenderingOnIOThread(child_id, route_id)) + return false; + return TryCancel(child_id, route_id, final_status); +} + +bool PrerenderTracker::IsPrerenderingOnIOThread(int child_id, + int route_id) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + ChildRouteIdPair child_route_id_pair(child_id, route_id); + return possibly_prerendering_io_thread_set_.end() != + possibly_prerendering_io_thread_set_.find(child_route_id_pair); +} + +bool PrerenderTracker::GetFinalStatus(int child_id, int route_id, + FinalStatus* final_status) const { + ChildRouteIdPair child_route_id_pair(child_id, route_id); + + base::AutoLock lock(final_status_map_lock_); + FinalStatusMap::const_iterator final_status_it = + final_status_map_.find(child_route_id_pair); + if (final_status_map_.end() == final_status_map_.find(child_route_id_pair)) + return false; + *final_status = final_status_it->second.final_status; + return true; +} + +void PrerenderTracker::OnPrerenderingStarted( + int child_id, int route_id, PrerenderManager* prerender_manager) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_GE(child_id, 0); + DCHECK_GE(route_id, 0); + + ChildRouteIdPair child_route_id_pair(child_id, route_id); + + // The RenderView should not already be prerendering. + DCHECK(final_status_map_.end() == + final_status_map_.find(child_route_id_pair)); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction(&AddPrerenderOnIOThreadTask, child_route_id_pair)); + + base::AutoLock lock(final_status_map_lock_); + + final_status_map_.insert( + std::make_pair(child_route_id_pair, RenderViewInfo(prerender_manager))); +} + +void PrerenderTracker::OnPrerenderingFinished(int child_id, int route_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_GE(child_id, 0); + DCHECK_GE(route_id, 0); + + ChildRouteIdPair child_route_id_pair(child_id, route_id); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction(&RemovePrerenderOnIOThreadTask, child_route_id_pair)); + + base::AutoLock lock(final_status_map_lock_); + size_t num_erased = final_status_map_.erase(child_route_id_pair); + DCHECK_EQ(1u, num_erased); +} + +PrerenderTracker::PrerenderTracker() { +} + +PrerenderTracker::~PrerenderTracker() { +} + +FinalStatus PrerenderTracker::SetFinalStatus(int child_id, int route_id, + FinalStatus final_status) { + DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX); + + ChildRouteIdPair child_route_id_pair(child_id, route_id); + + base::AutoLock lock(final_status_map_lock_); + FinalStatusMap::iterator final_status_it = + final_status_map_.find(child_route_id_pair); + if (final_status_it == final_status_map_.end()) { + // The RenderView has already been either used or destroyed. + return FINAL_STATUS_MAX; + } + + if (final_status_it->second.final_status == FINAL_STATUS_MAX) { + final_status_it->second.final_status = final_status; + if (final_status != FINAL_STATUS_USED) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableFunction(&DestroyPreloadForRenderView, + final_status_it->second.prerender_manager, + child_id, + route_id, + final_status)); + } + } + return final_status_it->second.final_status; +} + +void PrerenderTracker::AddPrerenderOnIOThread( + const ChildRouteIdPair& child_route_id_pair) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!IsPrerenderingOnIOThread(child_route_id_pair.first, + child_route_id_pair.second)); + + possibly_prerendering_io_thread_set_.insert(child_route_id_pair); +} + +void PrerenderTracker::RemovePrerenderOnIOThread( + const ChildRouteIdPair& child_route_id_pair) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(IsPrerenderingOnIOThread(child_route_id_pair.first, + child_route_id_pair.second)); + + possibly_prerendering_io_thread_set_.erase(child_route_id_pair); +} + +// static +void PrerenderTracker::AddPrerenderOnIOThreadTask( + const ChildRouteIdPair& child_route_id_pair) { + GetInstance()->AddPrerenderOnIOThread(child_route_id_pair); +} + +// static +void PrerenderTracker::RemovePrerenderOnIOThreadTask( + const ChildRouteIdPair& child_route_id_pair) { + GetInstance()->RemovePrerenderOnIOThread(child_route_id_pair); +} + +} // namespace prerender |