Bug 1196847 - Part 2: Implement a cell hasher that uses unique id based hashes; r=jonco

--HG--
extra : rebase_source : aec278105f1cb4303eff593edef244f10ebd2a0e
This commit is contained in:
Terrence Cole 2015-08-26 14:51:35 -07:00
parent 954ca0c585
commit 5e9eddeee5
3 changed files with 93 additions and 5 deletions

View File

@ -653,8 +653,6 @@ class HashMapEntry
template <class> friend class detail::HashTableEntry;
template <class, class, class, class> friend class HashMap;
Key & mutableKey() { return key_; }
public:
template<typename KeyInput, typename ValueInput>
HashMapEntry(KeyInput&& k, ValueInput&& v)
@ -670,9 +668,10 @@ class HashMapEntry
typedef Key KeyType;
typedef Value ValueType;
const Key & key() const { return key_; }
const Value & value() const { return value_; }
Value & value() { return value_; }
const Key& key() const { return key_; }
Key& mutableKey() { return key_; }
const Value& value() const { return value_; }
Value& value() { return value_; }
private:
HashMapEntry(const HashMapEntry&) = delete;
@ -741,6 +740,7 @@ class HashTableEntry
}
T& get() { MOZ_ASSERT(isLive()); return *mem.addr(); }
NonConstT& getMutable() { MOZ_ASSERT(isLive()); return *mem.addr(); }
bool isFree() const { return keyHash == sFreeKey; }
void clearLive() { MOZ_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); }
@ -980,6 +980,16 @@ class HashTable : private AllocPolicy
#endif
}
NonConstT& mutableFront() {
MOZ_ASSERT(!this->empty());
#ifdef JS_DEBUG
MOZ_ASSERT(this->validEntry);
MOZ_ASSERT(this->generation == this->Range::table_->generation());
MOZ_ASSERT(this->mutationCount == this->Range::table_->mutationCount);
#endif
return this->cur->getMutable();
}
// Removes the |front()| element and re-inserts it into the table with
// a new key at the new Lookup position. |front()| is invalid after
// this operation until the next call to |popFront()|.

View File

@ -102,3 +102,56 @@ JS::HeapValuePostBarrier(JS::Value* valuep, const Value& prev, const Value& next
MOZ_ASSERT(valuep);
js::InternalGCMethods<JS::Value>::postBarrier(valuep, prev, next);
}
template <typename T>
/* static */ HashNumber
js::MovableCellHasher<T>::hash(const Lookup& l)
{
if (!l)
return 0;
// We have to access the zone from-any-thread here: a worker thread may be
// cloning a self-hosted object from the main-thread-runtime-owned self-
// hosting zone into the off-main-thread runtime. The zone's uid lock will
// protect against multiple workers doing this simultaneously.
MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) ||
l->zoneFromAnyThread()->isSelfHostingZone());
HashNumber hn;
if (!l->zoneFromAnyThread()->getHashCode(l, &hn))
js::CrashAtUnhandlableOOM("failed to get a stable hash code");
return hn;
}
template <typename T>
/* static */ bool
js::MovableCellHasher<T>::match(const Key& k, const Lookup& l)
{
// Return true if both are null or false if only one is null.
if (!k)
return !l;
if (!l)
return false;
MOZ_ASSERT(k);
MOZ_ASSERT(l);
MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) ||
l->zoneFromAnyThread()->isSelfHostingZone());
Zone* zone = k->zoneFromAnyThread();
if (zone != l->zoneFromAnyThread())
return false;
MOZ_ASSERT(zone->hasUniqueId(k));
MOZ_ASSERT(zone->hasUniqueId(l));
// Since both already have a uid (from hash), the get is infallible.
uint64_t uidK, uidL;
MOZ_ALWAYS_TRUE(zone->getUniqueId(k, &uidK));
MOZ_ALWAYS_TRUE(zone->getUniqueId(l, &uidL));
return uidK == uidL;
}
template struct js::MovableCellHasher<JSObject*>;
template struct js::MovableCellHasher<js::GlobalObject*>;
template struct js::MovableCellHasher<js::SavedFrame*>;
template struct js::MovableCellHasher<js::ScopeObject*>;

View File

@ -545,6 +545,31 @@ BarrieredSetPair(Zone* zone,
v2.postBarrieredSet(val2);
}
// Provide hash codes for Cell kinds that may be relocated and, thus, not have
// a stable address to use as the base for a hash code. Instead of the address,
// this hasher uses Cell::getUniqueId to provide exact matches and as a base
// for generating hash codes.
//
// Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr
// would not likely be a useful key, there are some cases where being able to
// hash a nullptr is useful, either on purpose or because of bugs:
// (1) existence checks where the key may happen to be null and (2) some
// aggregate Lookup kinds embed a JSObject* that is frequently null and do not
// null test before dispatching to the hasher.
template <typename T>
struct MovableCellHasher
{
static_assert(mozilla::IsBaseOf<JSObject, typename mozilla::RemovePointer<T>::Type>::value,
"MovableCellHasher's T must be a Cell type that may move");
using Key = T;
using Lookup = T;
static HashNumber hash(const Lookup& l);
static bool match(const Key& k, const Lookup& l);
static void rekey(Key& k, const Key& newKey) { k = newKey; }
};
/* Useful for hashtables with a HeapPtr as key. */
template <class T>
struct HeapPtrHasher