Backed out changeset 18d79e1e15f9 (bug 958317)
authorEd Morley <emorley@mozilla.com>
Wed, 05 Mar 2014 17:24:52 +0000
changeset 190299 6b70b3d56bfc1582db005ccc8916bff5c07f3dcf
parent 190298 0fe1cd05c078d4efffc71504448514abdeee8364
child 190300 2ec9d9897a96e8370a6dfd7f3b045e5e1dca2d2b
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs958317
milestone30.0a1
backs out18d79e1e15f9318a6311e91bdd6743cd0527f2c8
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 18d79e1e15f9 (bug 958317)
netwerk/cache2/CacheFileIOManager.cpp
netwerk/cache2/CacheIOThread.cpp
netwerk/cache2/CacheIOThread.h
netwerk/cache2/CacheIndex.cpp
netwerk/cache2/CacheStorageService.cpp
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -37,17 +37,19 @@
 #endif
 
 
 namespace mozilla {
 namespace net {
 
 #define kOpenHandlesLimit      64
 #define kMetadataWriteDelay    5000
+#define kEvictionLoopLimit     40      // in milliseconds
 #define kRemoveTrashStartDelay 60000   // in milliseconds
+#define kRemoveTrashLoopLimit  40      // in milliseconds
 
 bool
 CacheFileHandle::DispatchRelease()
 {
   if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
     return false;
   }
 
@@ -2346,46 +2348,52 @@ CacheFileIOManager::OverLimitEvictionInt
 {
   LOG(("CacheFileIOManager::OverLimitEvictionInternal()"));
 
   nsresult rv;
 
   MOZ_ASSERT(mIOThread->IsCurrentThread());
 
   // mOverLimitEvicting is accessed only on IO thread, so we can set it to false
-  // here and set it to true again once we dispatch another event that will
+  // here and set ti to true again once we dispatch another event that will
   // continue with the eviction. The reason why we do so is that we can fail
   // early anywhere in this method and the variable will contain a correct
   // value. Otherwise we would need to set it to false on every failing place.
   mOverLimitEvicting = false;
 
   if (mShuttingDown) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
+  TimeStamp start;
+
   while (true) {
     uint32_t cacheUsage;
     rv = CacheIndex::GetCacheSize(&cacheUsage);
     NS_ENSURE_SUCCESS(rv, rv);
 
     uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
     if (cacheUsage <= cacheLimit) {
       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size under "
            "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
       return NS_OK;
     }
 
     LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size over "
          "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
 
-    if (CacheIOThread::YieldAndRerun()) {
-      LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Breaking loop "
-           "for higher level events."));
-      mOverLimitEvicting = true;
-      return NS_OK;
+    if (start.IsNull()) {
+      start = TimeStamp::NowLoRes();
+    } else {
+      TimeDuration elapsed = TimeStamp::NowLoRes() - start;
+      if (elapsed.ToMilliseconds() >= kEvictionLoopLimit) {
+        LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Breaking loop "
+             "after %u ms.", static_cast<uint32_t>(elapsed.ToMilliseconds())));
+        break;
+      }
     }
 
     SHA1Sum::Hash hash;
     uint32_t cnt;
     static uint32_t consecutiveFailures = 0;
     rv = CacheIndex::GetEntryForEviction(&hash, &cnt);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2430,17 +2438,24 @@ CacheFileIOManager::OverLimitEvictionInt
         // but we've reached a sane number of tries. It is likely that another
         // eviction will start soon. And as said earlier, this normally doesn't
         // happen at all.
         return NS_OK;
       }
     }
   }
 
-  NS_NOTREACHED("We should never get here");
+  nsCOMPtr<nsIRunnable> ev;
+  ev = NS_NewRunnableMethod(this,
+                            &CacheFileIOManager::OverLimitEvictionInternal);
+
+  rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mOverLimitEvicting = true;
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::TrashDirectory(nsIFile *aFile)
 {
 #ifdef PR_LOGGING
   nsAutoCString path;
@@ -2608,22 +2623,30 @@ CacheFileIOManager::RemoveTrashInternal(
     }
   }
 
   // mRemovingTrashDirs is accessed only on IO thread, so we can drop the flag
   // here and set it again once we dispatch a continuation event. By doing so,
   // we don't have to drop the flag on any possible early return.
   mRemovingTrashDirs = false;
 
+  TimeStamp start;
+
   while (true) {
-    if (CacheIOThread::YieldAndRerun()) {
-      LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop for "
-           "higher level events."));
-      mRemovingTrashDirs = true;
-      return NS_OK;
+    if (start.IsNull()) {
+      start = TimeStamp::NowLoRes();
+    } else {
+      static TimeDuration const kLimit = TimeDuration::FromMilliseconds(
+                                           kRemoveTrashLoopLimit);
+      TimeDuration elapsed = TimeStamp::NowLoRes() - start;
+      if (elapsed >= kLimit) {
+        LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop after "
+             "%u ms.", static_cast<uint32_t>(elapsed.ToMilliseconds())));
+        break;
+      }
     }
 
     // Find some trash directory
     if (!mTrashDir) {
       MOZ_ASSERT(!mTrashDirEnumerator);
 
       rv = FindTrashDirToRemove();
       if (rv == NS_ERROR_NOT_AVAILABLE) {
@@ -2681,17 +2704,24 @@ CacheFileIOManager::RemoveTrashInternal(
         LOG(("CacheFileIOManager::RemoveTrashInternal() - Found a directory in a trash "
             "directory! It will be removed recursively, but this can block IO "
             "thread for a while! [file=%s]", path.get()));
       }
       file->Remove(isDir);
     }
   }
 
-  NS_NOTREACHED("We should never get here");
+  nsCOMPtr<nsIRunnable> ev;
+  ev = NS_NewRunnableMethod(this,
+                            &CacheFileIOManager::RemoveTrashInternal);
+
+  rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mRemovingTrashDirs = true;
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::FindTrashDirToRemove()
 {
   LOG(("CacheFileIOManager::FindTrashDirToRemove()"));
 
--- a/netwerk/cache2/CacheIOThread.cpp
+++ b/netwerk/cache2/CacheIOThread.cpp
@@ -9,35 +9,29 @@
 #include "nsISupportsImpl.h"
 #include "nsPrintfCString.h"
 #include "nsThreadUtils.h"
 #include "mozilla/VisualEventTracer.h"
 
 namespace mozilla {
 namespace net {
 
-CacheIOThread* CacheIOThread::sSelf = nullptr;
-
 NS_IMPL_ISUPPORTS1(CacheIOThread, nsIThreadObserver)
 
 CacheIOThread::CacheIOThread()
 : mMonitor("CacheIOThread")
 , mThread(nullptr)
 , mLowestLevelWaiting(LAST_LEVEL)
-, mCurrentlyExecutingLevel(0)
 , mHasXPCOMEvents(false)
-, mRerunCurrentEvent(false)
 , mShutdown(false)
 {
-  sSelf = this;
 }
 
 CacheIOThread::~CacheIOThread()
 {
-  sSelf = nullptr;
 #ifdef DEBUG
   for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
     MOZ_ASSERT(!mEventQueue[level].Length());
   }
 #endif
 }
 
 nsresult CacheIOThread::Init()
@@ -69,37 +63,16 @@ nsresult CacheIOThread::Dispatch(nsIRunn
   return NS_OK;
 }
 
 bool CacheIOThread::IsCurrentThread()
 {
   return mThread == PR_GetCurrentThread();
 }
 
-bool CacheIOThread::YieldInternal()
-{
-  if (!IsCurrentThread()) {
-    NS_WARNING("Trying to yield to priority events on non-cache2 I/O thread? "
-               "You probably do something wrong.");
-    return false;
-  }
-
-  if (mCurrentlyExecutingLevel == XPCOM_LEVEL) {
-    // Doesn't make any sense, since this handler is the one
-    // that would be executed as the next one.
-    return false;
-  }
-
-  if (!EventsPending(mCurrentlyExecutingLevel))
-    return false;
-
-  mRerunCurrentEvent = true;
-  return true;
-}
-
 nsresult CacheIOThread::Shutdown()
 {
   {
     MonitorAutoLock lock(mMonitor);
     mShutdown = true;
     mMonitor.NotifyAll();
   }
 
@@ -159,18 +132,16 @@ loopStart:
       mLowestLevelWaiting = LAST_LEVEL;
 
       // Process xpcom events first
       while (mHasXPCOMEvents) {
         eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
           "net::cache::io::level(xpcom)");
 
         mHasXPCOMEvents = false;
-        mCurrentlyExecutingLevel = XPCOM_LEVEL;
-
         MonitorAutoUnlock unlock(mMonitor);
 
         bool processedEvent;
         nsresult rv;
         do {
           rv = mXPCOMThread->ProcessNextEvent(false, &processedEvent);
         } while (NS_SUCCEEDED(rv) && processedEvent);
       }
@@ -228,44 +199,30 @@ void CacheIOThread::LoopOneLevel(uint32_
 {
   eventtracer::AutoEventTracer tracer(this, eventtracer::eExec, eventtracer::eDone,
     sLevelTraceName[aLevel]);
 
   nsTArray<nsRefPtr<nsIRunnable> > events;
   events.SwapElements(mEventQueue[aLevel]);
   uint32_t length = events.Length();
 
-  mCurrentlyExecutingLevel = aLevel;
-
   bool returnEvents = false;
   uint32_t index;
   {
     MonitorAutoUnlock unlock(mMonitor);
 
     for (index = 0; index < length; ++index) {
       if (EventsPending(aLevel)) {
         // Somebody scheduled a new event on a lower level, break and harry
         // to execute it!  Don't forget to return what we haven't exec.
         returnEvents = true;
         break;
       }
 
-      // Drop any previous flagging, only an event on the current level may set
-      // this flag.
-      mRerunCurrentEvent = false;
-
       events[index]->Run();
-
-      if (mRerunCurrentEvent) {
-        // The event handler yields to higher priority events and wants to rerun.
-        returnEvents = true;
-        break;
-      }
-
-      // Release outside the lock.
       events[index] = nullptr;
     }
   }
 
   if (returnEvents)
     mEventQueue[aLevel].InsertElementsAt(0, events.Elements() + index, length - index);
 }
 
--- a/netwerk/cache2/CacheIOThread.h
+++ b/netwerk/cache2/CacheIOThread.h
@@ -31,66 +31,41 @@ public:
     READ_PRIORITY,
     OPEN,
     READ,
     WRITE,
     MANAGEMENT,
     CLOSE,
     BUILD_OR_UPDATE_INDEX,
     EVICT,
-    LAST_LEVEL,
-
-    // This is actually executed as the first level, but we want this enum
-    // value merely as an indicator while other values are used as indexes
-    // to the queue array.  Hence put at end and not as the first.
-    XPCOM_LEVEL
+    LAST_LEVEL
   };
 
   nsresult Init();
   nsresult Dispatch(nsIRunnable* aRunnable, uint32_t aLevel);
   bool IsCurrentThread();
-
-  /**
-   * Callable only on this thread, checks if there is an event waiting in
-   * the event queue with a higher execution priority.  If so, the result
-   * is true and the current event handler should break it's work and return
-   * from Run() method immediately.  The event handler will be rerun again
-   * when all more priority events are processed.  Events pending after this
-   * handler (i.e. the one that called YieldAndRerun()) will not execute sooner
-   * then this handler is executed w/o a call to YieldAndRerun().
-   */
-  static bool YieldAndRerun()
-  {
-    return sSelf ? sSelf->YieldInternal() : false;
-  }
-
   nsresult Shutdown();
   already_AddRefed<nsIEventTarget> Target();
 
   // Memory reporting
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 private:
   static void ThreadFunc(void* aClosure);
   void ThreadFunc();
   void LoopOneLevel(uint32_t aLevel);
   bool EventsPending(uint32_t aLastLevel = LAST_LEVEL);
-  bool YieldInternal();
-
-  static CacheIOThread* sSelf;
 
   mozilla::Monitor mMonitor;
   PRThread* mThread;
   nsCOMPtr<nsIThread> mXPCOMThread;
   uint32_t mLowestLevelWaiting;
-  uint32_t mCurrentlyExecutingLevel;
   nsTArray<nsRefPtr<nsIRunnable> > mEventQueue[LAST_LEVEL];
 
   bool mHasXPCOMEvents;
-  bool mRerunCurrentEvent;
   bool mShutdown;
 };
 
 } // net
 } // mozilla
 
 #endif
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -20,16 +20,18 @@
 
 
 #define kMinUnwrittenChanges   300
 #define kMinDumpInterval       20000 // in milliseconds
 #define kMaxBufSize            16384
 #define kIndexVersion          0x00000001
 #define kBuildIndexStartDelay  10000 // in milliseconds
 #define kUpdateIndexStartDelay 10000 // in milliseconds
+#define kBuildIndexLoopLimit   40    // in milliseconds
+#define kUpdateIndexLoopLimit  40    // in milliseconds
 
 const char kIndexName[]     = "index";
 const char kTempIndexName[] = "index.tmp";
 const char kJournalName[]   = "index.log";
 
 namespace mozilla {
 namespace net {
 
@@ -2362,20 +2364,30 @@ CacheIndex::BuildIndex()
     }
 
     if (NS_FAILED(rv)) {
       FinishBuild(false);
       return;
     }
   }
 
+  TimeStamp start;
+
   while (true) {
-    if (CacheIOThread::YieldAndRerun()) {
-      LOG(("CacheIndex::BuildIndex() - Breaking loop for higher level events."));
-      return;
+    if (start.IsNull()) {
+      start = TimeStamp::NowLoRes();
+    } else {
+      static TimeDuration const kLimit = TimeDuration::FromMilliseconds(
+                                           kBuildIndexLoopLimit);
+      TimeDuration elapsed = TimeStamp::NowLoRes() - start;
+      if (elapsed >= kLimit) {
+        LOG(("CacheIndex::BuildIndex() - Breaking loop after %u ms.",
+             static_cast<uint32_t>(elapsed.ToMilliseconds())));
+        break;
+      }
     }
 
     nsCOMPtr<nsIFile> file;
     {
       // Do not do IO under the lock.
       CacheIndexAutoUnlock unlock(this);
       rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
     }
@@ -2467,17 +2479,26 @@ CacheIndex::BuildIndex()
       entry = mIndex.PutEntry(hash);
       InitEntryFromDiskData(entry, meta, size);
       LOG(("CacheIndex::BuildIndex() - Added entry to index. [hash=%s]",
            leaf.get()));
       entry->Log();
     }
   }
 
-  NS_NOTREACHED("We should never get here");
+  nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
+  MOZ_ASSERT(ioThread);
+
+  rv = ioThread->Dispatch(this, CacheIOThread::BUILD_OR_UPDATE_INDEX);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("CacheIndex::BuildIndex() - Can't dispatch event");
+    LOG(("CacheIndex::BuildIndex() - Can't dispatch event" ));
+    FinishBuild(false);
+    return;
+  }
 }
 
 void
 CacheIndex::FinishBuild(bool aSucceeded)
 {
   LOG(("CacheIndex::FinishBuild() [succeeded=%d]", aSucceeded));
 
   MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == BUILDING);
@@ -2594,21 +2615,30 @@ CacheIndex::UpdateIndex()
     }
 
     if (NS_FAILED(rv)) {
       FinishUpdate(false);
       return;
     }
   }
 
+  TimeStamp start;
+
   while (true) {
-    if (CacheIOThread::YieldAndRerun()) {
-      LOG(("CacheIndex::UpdateIndex() - Breaking loop for higher level "
-           "events."));
-      return;
+    if (start.IsNull()) {
+      start = TimeStamp::NowLoRes();
+    } else {
+      static TimeDuration const kLimit = TimeDuration::FromMilliseconds(
+                                           kUpdateIndexLoopLimit);
+      TimeDuration elapsed = TimeStamp::NowLoRes() - start;
+      if (elapsed >= kLimit) {
+        LOG(("CacheIndex::UpdateIndex() - Breaking loop after %u ms.",
+             static_cast<uint32_t>(elapsed.ToMilliseconds())));
+        break;
+      }
     }
 
     nsCOMPtr<nsIFile> file;
     {
       // Do not do IO under the lock.
       CacheIndexAutoUnlock unlock(this);
       rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
     }
@@ -2734,17 +2764,26 @@ CacheIndex::UpdateIndex()
       entry = mIndex.PutEntry(hash);
       InitEntryFromDiskData(entry, meta, size);
       LOG(("CacheIndex::UpdateIndex() - Added/updated entry to/in index. "
            "[hash=%s]", leaf.get()));
       entry->Log();
     }
   }
 
-  NS_NOTREACHED("We should never get here");
+  nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
+  MOZ_ASSERT(ioThread);
+
+  rv = ioThread->Dispatch(this, CacheIOThread::BUILD_OR_UPDATE_INDEX);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("CacheIndex::UpdateIndex() - Can't dispatch event");
+    LOG(("CacheIndex::UpdateIndex() - Can't dispatch event" ));
+    FinishUpdate(false);
+    return;
+  }
 }
 
 void
 CacheIndex::FinishUpdate(bool aSucceeded)
 {
   LOG(("CacheIndex::FinishUpdate() [succeeded=%d]", aSucceeded));
 
   MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == UPDATING);
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -633,21 +633,23 @@ nsresult CacheFilesDeletor::Execute()
   LOG(("CacheFilesDeletor::Execute [this=%p]", this));
 
   if (!mEnumerator) {
     // No enumerator means the job is done.
     return NS_OK;
   }
 
   nsresult rv;
+  TimeStamp start;
 
   switch (mMode) {
   case ALL:
   case DOOMED:
     // Simply delete all files, don't doom then though the backend
+    start = TimeStamp::NowLoRes();
 
     while (mEnumerator->HasMore()) {
       nsCOMPtr<nsIFile> file;
       rv = mEnumerator->GetNextFile(getter_AddRefs(file));
       if (NS_FAILED(rv))
         return rv;
 
 #ifdef PR_LOG
@@ -658,19 +660,25 @@ nsresult CacheFilesDeletor::Execute()
 
       rv = file->Remove(false);
       if (NS_FAILED(rv)) {
         LOG(("  could not remove the file, probably doomed, rv=0x%08x", rv));
       }
 
       ++mRunning;
 
-      if (CacheIOThread::YieldAndRerun()) {
-        LOG(("  deleted %u files, breaking loop for higher level events."));
-        return NS_OK;
+      if (!(mRunning % (1 << 5)) && mEnumerator->HasMore()) {
+        TimeStamp now(TimeStamp::NowLoRes());
+#define DELETOR_LOOP_LIMIT_MS 200
+        static TimeDuration const kLimitms = TimeDuration::FromMilliseconds(DELETOR_LOOP_LIMIT_MS);
+        if ((now - start) > kLimitms) {
+          LOG(("  deleted %u files, breaking %dms loop", mRunning, DELETOR_LOOP_LIMIT_MS));
+          rv = mIOThread->Dispatch(this, CacheIOThread::EVICT);
+          return rv;
+        }
       }
     }
 
     break;
 
   default:
     MOZ_ASSERT(false);
   }
@@ -1053,35 +1061,30 @@ CacheStorageService::PurgeOverMemoryLimi
 
   if (mMemorySize > memoryLimit) {
     LOG(("  memory data consumption over the limit, abandon any entry"));
     PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE);
   }
 
   LOG(("  purging took %1.2fms", (TimeStamp::Now() - start).ToMilliseconds()));
 
-  // When we exit because of yield, leave the flag so this event is not reposted
-  // from OnMemoryConsumptionChange unnecessarily until we are dequeued again.
-  mPurging = CacheIOThread::YieldAndRerun();
+  mPurging = false;
 }
 
 void
 CacheStorageService::PurgeExpired()
 {
   MOZ_ASSERT(IsOnManagementThread());
 
   mExpirationArray.Sort(ExpirationComparator());
   uint32_t now = NowInSeconds();
 
   uint32_t const memoryLimit = CacheObserver::MemoryLimit();
 
   for (uint32_t i = 0; mMemorySize > memoryLimit && i < mExpirationArray.Length();) {
-    if (CacheIOThread::YieldAndRerun())
-      return;
-
     nsRefPtr<CacheEntry> entry = mExpirationArray[i];
 
     uint32_t expirationTime = entry->GetExpirationTime();
     if (expirationTime > 0 && expirationTime <= now) {
       LOG(("  dooming expired entry=%p, exptime=%u (now=%u)",
         entry.get(), entry->GetExpirationTime(), now));
 
       entry->PurgeAndDoom();
@@ -1101,19 +1104,16 @@ CacheStorageService::PurgeByFrecency(boo
   if (aFrecencyNeedsSort) {
     mFrecencyArray.Sort(FrecencyComparator());
     aFrecencyNeedsSort = false;
   }
 
   uint32_t const memoryLimit = CacheObserver::MemoryLimit();
 
   for (uint32_t i = 0; mMemorySize > memoryLimit && i < mFrecencyArray.Length();) {
-    if (CacheIOThread::YieldAndRerun())
-      return;
-
     nsRefPtr<CacheEntry> entry = mFrecencyArray[i];
 
     if (entry->Purge(aWhat)) {
       LOG(("  abandoned (%d), entry=%p, frecency=%1.10f",
         aWhat, entry.get(), entry->GetFrecency()));
       continue;
     }
 
@@ -1124,19 +1124,16 @@ CacheStorageService::PurgeByFrecency(boo
 
 void
 CacheStorageService::PurgeAll(uint32_t aWhat)
 {
   LOG(("CacheStorageService::PurgeAll aWhat=%d", aWhat));
   MOZ_ASSERT(IsOnManagementThread());
 
   for (uint32_t i = 0; i < mFrecencyArray.Length();) {
-    if (CacheIOThread::YieldAndRerun())
-      return;
-
     nsRefPtr<CacheEntry> entry = mFrecencyArray[i];
 
     if (entry->Purge(aWhat)) {
       LOG(("  abandoned entry=%p", entry.get()));
       continue;
     }
 
     // not purged, move to the next one