Bug 1207696 Part 8f - Ensure that PL and PLD hashtables have consistent iteration order when recording/replaying, r=froydnj.
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 23 Jul 2018 14:47:55 +0000
changeset 427885 1e146aebbcc65d9610cf1f4ef841a5a6b5ea166d
parent 427884 d9bbebacecd6acdb0636060b4f8d94c74bf38803
child 427886 70c285e729d9e06c05a169ac94940a69e91574f2
push id34320
push userrgurzau@mozilla.com
push dateTue, 24 Jul 2018 09:50:07 +0000
treeherdermozilla-central@1e5fa52a612e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1207696
milestone63.0a1
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
Bug 1207696 Part 8f - Ensure that PL and PLD hashtables have consistent iteration order when recording/replaying, r=froydnj.
xpcom/ds/PLDHashTable.cpp
xpcom/ds/PLDHashTable.h
--- a/xpcom/ds/PLDHashTable.cpp
+++ b/xpcom/ds/PLDHashTable.cpp
@@ -193,17 +193,17 @@ PLDHashTable::HashShift(uint32_t aEntryS
   }
 
   // Compute the hashShift value.
   return kHashBits - log2;
 }
 
 PLDHashTable::PLDHashTable(const PLDHashTableOps* aOps, uint32_t aEntrySize,
                            uint32_t aLength)
-  : mOps(aOps)
+  : mOps(recordreplay::GeneratePLDHashTableCallbacks(aOps))
   , mEntryStore()
   , mGeneration(0)
   , mHashShift(HashShift(aEntrySize, aLength))
   , mEntrySize(aEntrySize)
   , mEntryCount(0)
   , mRemovedCount(0)
 #ifdef DEBUG
   , mChecker()
@@ -222,33 +222,38 @@ PLDHashTable::operator=(PLDHashTable&& a
 {
   if (this == &aOther) {
     return *this;
   }
 
   // |mOps| and |mEntrySize| are required to stay the same, they're
   // conceptually part of the type -- indeed, if PLDHashTable was a templated
   // type like nsTHashtable, they *would* be part of the type -- so it only
-  // makes sense to assign in cases where they match.
-  MOZ_RELEASE_ASSERT(mOps == aOther.mOps);
-  MOZ_RELEASE_ASSERT(mEntrySize == aOther.mEntrySize);
+  // makes sense to assign in cases where they match. An exception is when we
+  // are recording or replaying the execution, in which case custom ops are
+  // generated for each table.
+  MOZ_RELEASE_ASSERT(mOps == aOther.mOps || !mOps || recordreplay::IsRecordingOrReplaying());
+  MOZ_RELEASE_ASSERT(mEntrySize == aOther.mEntrySize || !mEntrySize);
 
   // Reconstruct |this|.
+  const PLDHashTableOps* ops = recordreplay::UnwrapPLDHashTableCallbacks(aOther.mOps);
   this->~PLDHashTable();
-  new (KnownNotNull, this) PLDHashTable(aOther.mOps, aOther.mEntrySize, 0);
+  new (KnownNotNull, this) PLDHashTable(ops, aOther.mEntrySize, 0);
 
   // Move non-const pieces over.
   mHashShift = std::move(aOther.mHashShift);
   mEntryCount = std::move(aOther.mEntryCount);
   mRemovedCount = std::move(aOther.mRemovedCount);
   mEntryStore.Set(aOther.mEntryStore.Get(), &mGeneration);
 #ifdef DEBUG
   mChecker = std::move(aOther.mChecker);
 #endif
 
+  recordreplay::MovePLDHashTableContents(aOther.mOps, mOps);
+
   // Clear up |aOther| so its destruction will be a no-op and it reports being
   // empty.
   {
 #ifdef DEBUG
     AutoDestructorOp op(mChecker);
 #endif
     aOther.mEntryCount = 0;
     aOther.mEntryStore.Set(nullptr, &aOther.mGeneration);
@@ -312,38 +317,41 @@ PLDHashTable::AddressEntry(uint32_t aInd
 
 PLDHashTable::~PLDHashTable()
 {
 #ifdef DEBUG
   AutoDestructorOp op(mChecker);
 #endif
 
   if (!mEntryStore.Get()) {
+    recordreplay::DestroyPLDHashTableCallbacks(mOps);
     return;
   }
 
   // Clear any remaining live entries.
   char* entryAddr = mEntryStore.Get();
   char* entryLimit = entryAddr + Capacity() * mEntrySize;
   while (entryAddr < entryLimit) {
     PLDHashEntryHdr* entry = (PLDHashEntryHdr*)entryAddr;
     if (EntryIsLive(entry)) {
       mOps->clearEntry(this, entry);
     }
     entryAddr += mEntrySize;
   }
 
+  recordreplay::DestroyPLDHashTableCallbacks(mOps);
+
   // Entry storage is freed last, by ~EntryStore().
 }
 
 void
 PLDHashTable::ClearAndPrepareForLength(uint32_t aLength)
 {
   // Get these values before the destructor clobbers them.
-  const PLDHashTableOps* ops = mOps;
+  const PLDHashTableOps* ops = recordreplay::UnwrapPLDHashTableCallbacks(mOps);
   uint32_t entrySize = mEntrySize;
 
   this->~PLDHashTable();
   new (KnownNotNull, this) PLDHashTable(ops, entrySize, aLength);
 }
 
 void
 PLDHashTable::Clear()
--- a/xpcom/ds/PLDHashTable.h
+++ b/xpcom/ds/PLDHashTable.h
@@ -282,39 +282,38 @@ public:
   // initial capacity won't be relevant until the first element is added; prior
   // to that the capacity will be zero.
   //
   // This will crash if |aEntrySize| and/or |aLength| are too large.
   PLDHashTable(const PLDHashTableOps* aOps, uint32_t aEntrySize,
                uint32_t aLength = kDefaultInitialLength);
 
   PLDHashTable(PLDHashTable&& aOther)
-      // We initialize mOps and mEntrySize here because they are |const|, and
-      // the move assignment operator cannot modify them.
-      // We initialize mEntryStore because it is required for a safe call to
-      // the destructor, which the move assignment operator does.
-      // We initialize mGeneration because it is modified by the move
-      // assignment operator.
-    : mOps(aOther.mOps)
+      // Initialize fields which are checked by the move assignment operator
+      // and the destructor (which the move assignment operator calls).
+    : mOps(nullptr)
     , mEntryStore()
     , mGeneration(0)
-    , mEntrySize(aOther.mEntrySize)
+    , mEntrySize(0)
 #ifdef DEBUG
     , mChecker()
 #endif
   {
     *this = std::move(aOther);
   }
 
   PLDHashTable& operator=(PLDHashTable&& aOther);
 
   ~PLDHashTable();
 
   // This should be used rarely.
-  const PLDHashTableOps* Ops() const { return mOps; }
+  const PLDHashTableOps* Ops() const
+  {
+    return mozilla::recordreplay::UnwrapPLDHashTableCallbacks(mOps);
+  }
 
   // Size in entries (gross, not net of free and removed sentinels) for table.
   // This can be zero if no elements have been added yet, in which case the
   // entry storage will not have yet been allocated.
   uint32_t Capacity() const
   {
     return mEntryStore.Get() ? CapacityFromHashShift() : 0;
   }