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
diff --git a/chrome/browser/prerender/prerender_tracker.h b/chrome/browser/prerender/prerender_tracker.h
new file mode 100644
index 0000000..f263052
--- /dev/null
+++ b/chrome/browser/prerender/prerender_tracker.h
@@ -0,0 +1,136 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRERENDER_PRERENDER_TRACKER_H_
+#define CHROME_BROWSER_PRERENDER_PRERENDER_TRACKER_H_
+#pragma once
+
+#include <map>
+#include <set>
+
+#include "base/memory/singleton.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/prerender/prerender_final_status.h"
+
+namespace prerender {
+
+class PrerenderManager;
+struct RenderViewInfo;
+
+// PrerenderTracker is responsible for keeping track of all prerendering
+// RenderViews and their statuses. Its list is guaranteed to be up to date
+// and can be modified on any thread.
+class PrerenderTracker {
+ public:
+ // Returns the PrerenderTracker singleton.
+ static PrerenderTracker* GetInstance();
+
+ // Attempts to set the status of the specified RenderViewHost to
+ // FINAL_STATUS_USED. Returns true on success. Returns false if it has
+ // already been cancelled for any reason, or is no longer prerendering.
+ // Can only be called only on the IO thread. This method will not call
+ // PrerenderContents::set_final_status() on the corresponding
+ // PrerenderContents.
+ //
+ // If it returns true, all subsequent calls to TryCancel for the RenderView
+ // will return false.
+ bool TryUse(int child_id, int route_id);
+
+ // Attempts to cancel prerendering by the specified RenderView, setting the
+ // FinalStatus to |final_status|. Returns true if the specified prerender has
+ // been cancelled, either as a result of this call or for any other reason.
+ // If the call results in cancelling a PrerenderContents, a task to destroy
+ // it is also posted to the UI thread.
+ //
+ // When true is returned, it is guaranteed that the RenderView will never
+ // be displayed. When false is returned, the RenderView has either been
+ // swapped into a tab or has already been destroyed.
+ bool TryCancel(int child_id, int route_id, FinalStatus final_status);
+
+ // Same as above, but can only called on the IO Thread. Does not acquire a
+ // lock when the RenderView is not being prerendered.
+ bool TryCancelOnIOThread(int child_id, int route_id,
+ FinalStatus final_status);
+
+ // Returns whether or not a RenderView is prerendering. Can only be called on
+ // the IO thread. Does not acquire a lock, so may claim a RenderView that has
+ // been displayed or destroyed is still prerendering.
+ // TODO(mmenke): Remove external use of this method and make it private.
+ bool IsPrerenderingOnIOThread(int child_id, int route_id) const;
+
+ // Gets the FinalStatus of the specified prerendered RenderView. Returns
+ // |true| and sets |final_status| to the status of the RenderView if it
+ // is found, returns false otherwise.
+ bool GetFinalStatus(int child_id, int route_id,
+ FinalStatus* final_status) const;
+
+ protected:
+ friend class PrerenderContents;
+
+ FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerUsed);
+ FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerCancelled);
+ FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerCancelledOnIO);
+ FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerCancelledFast);
+ FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerMultiple);
+
+ // Must be called when a RenderView starts prerendering, before the first
+ // navigation starts to avoid any races.
+ void OnPrerenderingStarted(int child_id, int route_id,
+ PrerenderManager* prerender_manager);
+
+ // Must be called when a RenderView stops prerendering, either because the
+ // RenderView was used or prerendering was cancelled and it is being
+ // destroyed.
+ void OnPrerenderingFinished(int child_id, int route_id);
+
+ private:
+ friend struct DefaultSingletonTraits<PrerenderTracker>;
+
+ typedef std::pair<int, int> ChildRouteIdPair;
+
+ // Map of child/route id pairs to final statuses.
+ typedef std::map<ChildRouteIdPair, RenderViewInfo> FinalStatusMap;
+ // Set of child/route id pairs that may be prerendering.
+ typedef std::set<ChildRouteIdPair> PossiblyPrerenderingChildRouteIdPairs;
+
+ PrerenderTracker();
+ ~PrerenderTracker();
+
+ // Attempts to set the FinalStatus of the specified RenderView to
+ // |final_status|. If the FinalStatus of the RenderView has already been
+ // set, does nothing. Returns the resulting FinalStatus of that RenderView,
+ // regardless of success or failure. If the RenderView isn't currently
+ // prerendering, returns FINAL_STATUS_MAX.
+ FinalStatus SetFinalStatus(int child_id, int route_id,
+ FinalStatus final_status);
+
+ // Add/remove the specified pair to |possibly_prerendering_io_thread_set_| on
+ // the IO Thread.
+ void AddPrerenderOnIOThread(const ChildRouteIdPair& child_route_id_pair);
+ void RemovePrerenderOnIOThread(const ChildRouteIdPair& child_route_id_pair);
+
+ // Tasks posted to the IO Thread to call the above functions.
+ static void AddPrerenderOnIOThreadTask(
+ const ChildRouteIdPair& child_route_id_pair);
+ static void RemovePrerenderOnIOThreadTask(
+ const ChildRouteIdPair& child_route_id_pair);
+
+ // |final_status_map_lock_| protects access to |final_status_map_|.
+ mutable base::Lock final_status_map_lock_;
+ // Map containing child/route id pairs and their final statuses. Must only be
+ // accessed while the lock is held. Values are always accurate and up to
+ // date.
+ FinalStatusMap final_status_map_;
+
+ // Superset of child/route id pairs that are prerendering. Can only access on
+ // the IO thread. May contain entries that have since been displayed. Only
+ // used to prevent locking when not needed.
+ PossiblyPrerenderingChildRouteIdPairs possibly_prerendering_io_thread_set_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrerenderTracker);
+};
+
+} // namespace prerender
+
+#endif // CHROME_BROWSER_PRERENDER_PRERENDER_TRACKER_H_