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_