Bug 1418443 - Send OnVisit notifications from single runnable r?mak draft
authorDoug Thayer <dothayer@mozilla.com>
Tue, 21 Nov 2017 14:43:47 -0800
changeset 706263 7687ebed4cb6570f1fcd7601f07e063563d25baf
parent 705036 3f6b9aaed8cd57954e0c960cde06d25228196456
child 707007 a6ec7eb2077ba811d2885de0c009aab53aa6fcea
push id91761
push userbmo:dothayer@mozilla.com
push dateFri, 01 Dec 2017 16:40:16 +0000
reviewersmak
bugs1418443
milestone59.0a1
Bug 1418443 - Send OnVisit notifications from single runnable r?mak Because there's an overhead in simply creating/sending/receiving a runnable, it makes sense to send our onVisits notifications from a bulk runnable in the case where we're inserting many visits. This is only step one of many optimizations we can and should make to the observer system. MozReview-Commit-ID: Co5yOUCRdnZ
toolkit/components/places/History.cpp
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -616,79 +616,107 @@ private:
 
 NS_IMPL_ISUPPORTS_INHERITED(
   VisitedQuery
 , AsyncStatementCallback
 , mozIStorageCompletionCallback
 )
 
 /**
- * Notifies observers about a visit.
+ * Notifies observers about a visit or an array of visits.
  */
-class NotifyVisitObservers : public Runnable
+class NotifyManyVisitsObservers : public Runnable
 {
 public:
-  explicit NotifyVisitObservers(VisitData& aPlace)
-    : Runnable("places::NotifyVisitObservers")
+  explicit NotifyManyVisitsObservers(VisitData aPlace)
+    : Runnable("places::NotifyManyVisitsObservers")
     , mPlace(aPlace)
     , mHistory(History::GetService())
   {
   }
 
+  explicit NotifyManyVisitsObservers(nsTArray<VisitData>& aPlaces)
+    : Runnable("places::NotifyManyVisitsObservers")
+    , mHistory(History::GetService())
+  {
+    aPlaces.SwapElements(mPlaces);
+  }
+
+  nsresult NotifyVisit(nsNavHistory* aNavHistory,
+                       nsCOMPtr<nsIObserverService>& aObsService,
+                       PRTime aNow,
+                       VisitData aPlace) {
+    nsCOMPtr<nsIURI> uri;
+    MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), aPlace.spec));
+    if (!uri) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    // Notify the visit.  Note that TRANSITION_EMBED visits are never added
+    // to the database, thus cannot be queried and we don't notify them.
+    if (aPlace.transitionType != nsINavHistoryService::TRANSITION_EMBED) {
+      aNavHistory->NotifyOnVisit(uri, aPlace.visitId, aPlace.visitTime,
+                                 aPlace.referrerVisitId, aPlace.transitionType,
+                                 aPlace.guid, aPlace.hidden,
+                                 aPlace.visitCount + 1, // Add current visit.
+                                 static_cast<uint32_t>(aPlace.typed),
+                                 aPlace.title);
+    }
+
+    if (aObsService) {
+      DebugOnly<nsresult> rv =
+        aObsService->NotifyObservers(uri, URI_VISIT_SAVED, nullptr);
+      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Could not notify observers");
+    }
+
+    if (aNow - aPlace.visitTime < RECENTLY_VISITED_URIS_MAX_AGE) {
+      mHistory->AppendToRecentlyVisitedURIs(uri);
+    }
+    mHistory->NotifyVisited(uri);
+
+    if (aPlace.titleChanged) {
+      aNavHistory->NotifyTitleChange(uri, aPlace.title, aPlace.guid);
+    }
+
+    return NS_OK;
+  }
+
   NS_IMETHOD Run() override
   {
     MOZ_ASSERT(NS_IsMainThread(), "This should be called on the main thread");
 
     // We are in the main thread, no need to lock.
     if (mHistory->IsShuttingDown()) {
       // If we are shutting down, we cannot notify the observers.
       return NS_OK;
     }
 
     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
     if (!navHistory) {
-      NS_WARNING("Trying to notify about a visit but cannot get the history service!");
+      NS_WARNING("Trying to notify visits observers but cannot get the history service!");
       return NS_OK;
     }
 
-    nsCOMPtr<nsIURI> uri;
-    MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), mPlace.spec));
-    if (!uri) {
-      return NS_ERROR_UNEXPECTED;
-    }
-
-    // Notify the visit.  Note that TRANSITION_EMBED visits are never added
-    // to the database, thus cannot be queried and we don't notify them.
-    if (mPlace.transitionType != nsINavHistoryService::TRANSITION_EMBED) {
-      navHistory->NotifyOnVisit(uri, mPlace.visitId, mPlace.visitTime,
-                                mPlace.referrerVisitId, mPlace.transitionType,
-                                mPlace.guid, mPlace.hidden,
-                                mPlace.visitCount + 1, // Add current visit.
-                                static_cast<uint32_t>(mPlace.typed),
-                                mPlace.title);
-    }
-
     nsCOMPtr<nsIObserverService> obsService =
       mozilla::services::GetObserverService();
-    if (obsService) {
-      DebugOnly<nsresult> rv =
-        obsService->NotifyObservers(uri, URI_VISIT_SAVED, nullptr);
-      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Could not notify observers");
+
+    PRTime now = PR_Now();
+    if (mPlaces.Length() > 0) {
+      for (uint32_t i = 0; i < mPlaces.Length(); ++i) {
+        nsresult rv = NotifyVisit(navHistory, obsService, now, mPlaces[i]);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    } else {
+      nsresult rv = NotifyVisit(navHistory, obsService, now, mPlace);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    History* history = History::GetService();
-    NS_ENSURE_STATE(history);
-    if (PR_Now() - mPlace.visitTime < RECENTLY_VISITED_URIS_MAX_AGE) {
-      mHistory->AppendToRecentlyVisitedURIs(uri);
-    }
-    history->NotifyVisited(uri);
-
     return NS_OK;
   }
 private:
+  nsTArray<VisitData> mPlaces;
   VisitData mPlace;
   RefPtr<History> mHistory;
 };
 
 /**
  * Notifies observers about a pages title changing.
  */
 class NotifyTitleObservers : public Runnable
@@ -982,17 +1010,18 @@ public:
     if (mHistory->IsShuttingDown()) {
       // If we were already shutting down, we cannot insert the URIs.
       return NS_OK;
     }
 
     mozStorageTransaction transaction(mDBConn, false,
                                       mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
-    VisitData* lastFetchedPlace = nullptr;
+    const VisitData* lastFetchedPlace = nullptr;
+    uint32_t lastFetchedVisitCount = 0;
     for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
       VisitData& place = mPlaces.ElementAt(i);
 
       // Fetching from the database can overwrite this information, so save it
       // apart.
       bool typed = place.typed;
       bool hidden = place.hidden;
 
@@ -1005,26 +1034,27 @@ public:
           if (!!mCallback && !mIgnoreErrors) {
             nsCOMPtr<nsIRunnable> event =
               new NotifyPlaceInfoCallback(mCallback, place, true, rv);
             return NS_DispatchToMainThread(event);
           }
           return NS_OK;
         }
         lastFetchedPlace = &mPlaces.ElementAt(i);
+        lastFetchedVisitCount = lastFetchedPlace->visitCount;
       } else {
         // Copy over the data from the already known place.
         place.placeId = lastFetchedPlace->placeId;
         place.guid = lastFetchedPlace->guid;
         place.lastVisitId = lastFetchedPlace->visitId;
         place.lastVisitTime = lastFetchedPlace->visitTime;
         place.titleChanged = !lastFetchedPlace->title.Equals(place.title);
         place.frecency = lastFetchedPlace->frecency;
         // Add one visit for the previous loop.
-        place.visitCount = ++(*lastFetchedPlace).visitCount;
+        place.visitCount = ++lastFetchedVisitCount;
       }
 
       // If any transition is typed, ensure the page is marked as typed.
       if (typed != lastFetchedPlace->typed) {
         place.typed = true;
       }
 
       // If any transition is visible, ensure the page is marked as visible.
@@ -1052,42 +1082,35 @@ public:
           NS_ENSURE_SUCCESS(rv2, rv2);
         }
       }
       NS_ENSURE_SUCCESS(rv, rv);
 
       // If we get here, we must have been successful adding/updating this
       // visit/place, so update the count:
       mSuccessfulUpdatedCount++;
-
-      nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(place);
-      rv = NS_DispatchToMainThread(event);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      // Notify about title change if needed.
-      if (place.titleChanged) {
-        event = new NotifyTitleObservers(place.spec, place.title, place.guid);
-        rv = NS_DispatchToMainThread(event);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
     }
 
     {
       // Trigger an update for all the hosts of the places we inserted
       nsAutoCString query("DELETE FROM moz_updatehostsinsert_temp");
       nsCOMPtr<mozIStorageStatement> stmt = mHistory->GetStatement(query);
       NS_ENSURE_STATE(stmt);
       mozStorageStatementScoper scoper(stmt);
       nsresult rv = stmt->Execute();
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     nsresult rv = transaction.Commit();
     NS_ENSURE_SUCCESS(rv, rv);
 
+    nsCOMPtr<nsIRunnable> event = new NotifyManyVisitsObservers(mPlaces);
+    rv = NS_DispatchToMainThread(event);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     return NS_OK;
   }
 private:
   InsertVisitedURIs(
     mozIStorageConnection* aConnection,
     nsTArray<VisitData>& aPlaces,
     const nsMainThreadPtrHandle<mozIVisitInfoCallback>& aCallback,
     bool aGroupNotifications,
@@ -1915,17 +1938,17 @@ StoreAndNotifyEmbedVisit(VisitData& aPla
     Unused << aCallback->GetIgnoreResults(&ignoreResults);
     if (!ignoreResults) {
       nsCOMPtr<nsIRunnable> event =
         new NotifyPlaceInfoCallback(callback, aPlace, true, NS_OK);
       (void)NS_DispatchToMainThread(event);
     }
   }
 
-  nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(aPlace);
+  nsCOMPtr<nsIRunnable> event = new NotifyManyVisitsObservers(aPlace);
   (void)NS_DispatchToMainThread(event);
 }
 
 } // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 //// History