зеркало из https://github.com/mozilla/gecko-dev.git
Bug 759991 - Fix infinite loop in rekeyFront with fully collided Table; r=luke
This hooks up the same path to putNew, because it is slightly more efficient and fixes an OOM failure introduced in c9024bcb8da0.
This commit is contained in:
Родитель
44629df657
Коммит
a0b05d6208
|
@ -220,10 +220,10 @@ class HashTable : private AllocPolicy
|
||||||
JS_ASSERT(&k != &HashPolicy::getKey(this->cur->t));
|
JS_ASSERT(&k != &HashPolicy::getKey(this->cur->t));
|
||||||
if (match(*this->cur, l))
|
if (match(*this->cur, l))
|
||||||
return;
|
return;
|
||||||
Entry e = *this->cur;
|
typename HashTableEntry<T>::NonConstT t = this->cur->t;
|
||||||
HashPolicy::setKey(e.t, const_cast<Key &>(k));
|
HashPolicy::setKey(t, const_cast<Key &>(k));
|
||||||
table.remove(*this->cur);
|
table.remove(*this->cur);
|
||||||
table.add(l, e);
|
table.putNewInfallible(l, t);
|
||||||
added = true;
|
added = true;
|
||||||
this->validEntry = false;
|
this->validEntry = false;
|
||||||
}
|
}
|
||||||
|
@ -234,22 +234,27 @@ class HashTable : private AllocPolicy
|
||||||
|
|
||||||
/* Potentially rehashes the table. */
|
/* Potentially rehashes the table. */
|
||||||
~Enum() {
|
~Enum() {
|
||||||
if (added)
|
JS_ASSERT(!added);
|
||||||
table.checkOverloaded();
|
|
||||||
if (removed)
|
if (removed)
|
||||||
table.checkUnderloaded();
|
table.checkUnderloaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Can be used to end the enumeration before the destructor. */
|
/*
|
||||||
void endEnumeration() {
|
* Can be used to end the enumeration before the destructor. Unlike
|
||||||
|
* |~Enum()|, this can report OOM on resize, so must be called if
|
||||||
|
* |rekeyFront()| is used during enumeration.
|
||||||
|
*/
|
||||||
|
bool endEnumeration() {
|
||||||
if (added) {
|
if (added) {
|
||||||
table.checkOverloaded();
|
|
||||||
added = false;
|
added = false;
|
||||||
|
if (table.checkOverloaded() == RehashFailed)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (removed) {
|
if (removed) {
|
||||||
table.checkUnderloaded();
|
|
||||||
removed = false;
|
removed = false;
|
||||||
|
table.checkUnderloaded();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -519,7 +524,7 @@ class HashTable : private AllocPolicy
|
||||||
Entry *entry = &table[h1];
|
Entry *entry = &table[h1];
|
||||||
|
|
||||||
/* Miss: return space for a new entry. */
|
/* Miss: return space for a new entry. */
|
||||||
if (entry->isFree()) {
|
if (!entry->isLive()) {
|
||||||
METER(stats.misses++);
|
METER(stats.misses++);
|
||||||
return *entry;
|
return *entry;
|
||||||
}
|
}
|
||||||
|
@ -535,14 +540,16 @@ class HashTable : private AllocPolicy
|
||||||
h1 = applyDoubleHash(h1, dh);
|
h1 = applyDoubleHash(h1, dh);
|
||||||
|
|
||||||
entry = &table[h1];
|
entry = &table[h1];
|
||||||
if (entry->isFree()) {
|
if (!entry->isLive()) {
|
||||||
METER(stats.misses++);
|
METER(stats.misses++);
|
||||||
return *entry;
|
return *entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool changeTableSize(int deltaLog2)
|
enum RebuildStatus { NotOverloaded, Rehashed, RehashFailed };
|
||||||
|
|
||||||
|
RebuildStatus changeTableSize(int deltaLog2)
|
||||||
{
|
{
|
||||||
/* Look, but don't touch, until we succeed in getting new entry store. */
|
/* Look, but don't touch, until we succeed in getting new entry store. */
|
||||||
Entry *oldTable = table;
|
Entry *oldTable = table;
|
||||||
|
@ -551,12 +558,12 @@ class HashTable : private AllocPolicy
|
||||||
uint32_t newCapacity = JS_BIT(newLog2);
|
uint32_t newCapacity = JS_BIT(newLog2);
|
||||||
if (newCapacity > sMaxCapacity) {
|
if (newCapacity > sMaxCapacity) {
|
||||||
this->reportAllocOverflow();
|
this->reportAllocOverflow();
|
||||||
return false;
|
return RehashFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry *newTable = createTable(*this, newCapacity);
|
Entry *newTable = createTable(*this, newCapacity);
|
||||||
if (!newTable)
|
if (!newTable)
|
||||||
return false;
|
return RehashFailed;
|
||||||
|
|
||||||
/* We can't fail from here on, so update table parameters. */
|
/* We can't fail from here on, so update table parameters. */
|
||||||
setTableSizeLog2(newLog2);
|
setTableSizeLog2(newLog2);
|
||||||
|
@ -573,32 +580,13 @@ class HashTable : private AllocPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyTable(*this, oldTable, oldCap);
|
destroyTable(*this, oldTable, oldCap);
|
||||||
return true;
|
return Rehashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(const Lookup &l, const Entry &e)
|
RebuildStatus checkOverloaded()
|
||||||
{
|
|
||||||
JS_ASSERT(table);
|
|
||||||
|
|
||||||
HashNumber keyHash = prepareHash(l);
|
|
||||||
Entry &entry = lookup(l, keyHash, sCollisionBit);
|
|
||||||
|
|
||||||
if (entry.isRemoved()) {
|
|
||||||
METER(stats.addOverRemoved++);
|
|
||||||
removedCount--;
|
|
||||||
keyHash |= sCollisionBit;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.t = e.t;
|
|
||||||
entry.setLive(keyHash);
|
|
||||||
entryCount++;
|
|
||||||
mutationCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkOverloaded()
|
|
||||||
{
|
{
|
||||||
if (!overloaded())
|
if (!overloaded())
|
||||||
return false;
|
return NotOverloaded;
|
||||||
|
|
||||||
/* Compress if a quarter or more of all entries are removed. */
|
/* Compress if a quarter or more of all entries are removed. */
|
||||||
int deltaLog2;
|
int deltaLog2;
|
||||||
|
@ -732,8 +720,11 @@ class HashTable : private AllocPolicy
|
||||||
removedCount--;
|
removedCount--;
|
||||||
p.keyHash |= sCollisionBit;
|
p.keyHash |= sCollisionBit;
|
||||||
} else {
|
} else {
|
||||||
if (checkOverloaded())
|
/* Preserve the validity of |p.entry|. */
|
||||||
/* Preserve the validity of |p.entry|. */
|
RebuildStatus status = checkOverloaded();
|
||||||
|
if (status == RehashFailed)
|
||||||
|
return false;
|
||||||
|
if (status == Rehashed)
|
||||||
p.entry = &findFreeEntry(p.keyHash);
|
p.entry = &findFreeEntry(p.keyHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -764,6 +755,34 @@ class HashTable : private AllocPolicy
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void putNewInfallible(const Lookup &l, const T &t)
|
||||||
|
{
|
||||||
|
JS_ASSERT(table);
|
||||||
|
|
||||||
|
HashNumber keyHash = prepareHash(l);
|
||||||
|
Entry *entry = &findFreeEntry(keyHash);
|
||||||
|
|
||||||
|
if (entry->isRemoved()) {
|
||||||
|
METER(stats.addOverRemoved++);
|
||||||
|
removedCount--;
|
||||||
|
keyHash |= sCollisionBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->t = t;
|
||||||
|
entry->setLive(keyHash);
|
||||||
|
entryCount++;
|
||||||
|
mutationCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool putNew(const Lookup &l, const T &t)
|
||||||
|
{
|
||||||
|
if (checkOverloaded() == RehashFailed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
putNewInfallible(l, t);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t)
|
bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t)
|
||||||
{
|
{
|
||||||
p.mutationCount = mutationCount;
|
p.mutationCount = mutationCount;
|
||||||
|
@ -1149,9 +1168,7 @@ class HashMap
|
||||||
|
|
||||||
/* Like put, but assert that the given key is not already present. */
|
/* Like put, but assert that the given key is not already present. */
|
||||||
bool putNew(const Key &k, const Value &v) {
|
bool putNew(const Key &k, const Value &v) {
|
||||||
AddPtr p = lookupForAdd(k);
|
return impl.putNew(k, Entry(k, v));
|
||||||
JS_ASSERT(!p);
|
|
||||||
return add(p, k, v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */
|
/* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */
|
||||||
|
@ -1357,15 +1374,11 @@ class HashSet
|
||||||
|
|
||||||
/* Like put, but assert that the given key is not already present. */
|
/* Like put, but assert that the given key is not already present. */
|
||||||
bool putNew(const T &t) {
|
bool putNew(const T &t) {
|
||||||
AddPtr p = lookupForAdd(t);
|
return impl.putNew(t, t);
|
||||||
JS_ASSERT(!p);
|
|
||||||
return add(p, t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool putNew(const Lookup &l, const T &t) {
|
bool putNew(const Lookup &l, const T &t) {
|
||||||
AddPtr p = lookupForAdd(l);
|
return impl.putNew(l, t);
|
||||||
JS_ASSERT(!p);
|
|
||||||
return add(p, t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(const Lookup &l) {
|
void remove(const Lookup &l) {
|
||||||
|
|
|
@ -193,11 +193,13 @@ BEGIN_TEST(testHashRekeyManual)
|
||||||
CHECK(AddLowKeys(&am, &bm, i));
|
CHECK(AddLowKeys(&am, &bm, i));
|
||||||
CHECK(MapsAreEqual(am, bm));
|
CHECK(MapsAreEqual(am, bm));
|
||||||
|
|
||||||
for (IntMap::Enum e(am); !e.empty(); e.popFront()) {
|
IntMap::Enum e(am);
|
||||||
|
for (; !e.empty(); e.popFront()) {
|
||||||
uint32_t tmp = LowToHigh::rekey(e.front().key);
|
uint32_t tmp = LowToHigh::rekey(e.front().key);
|
||||||
if (tmp != e.front().key)
|
if (tmp != e.front().key)
|
||||||
e.rekeyFront(tmp);
|
e.rekeyFront(tmp);
|
||||||
}
|
}
|
||||||
|
CHECK(e.endEnumeration());
|
||||||
CHECK(SlowRekey<LowToHigh>(&bm));
|
CHECK(SlowRekey<LowToHigh>(&bm));
|
||||||
|
|
||||||
CHECK(MapsAreEqual(am, bm));
|
CHECK(MapsAreEqual(am, bm));
|
||||||
|
@ -215,11 +217,13 @@ BEGIN_TEST(testHashRekeyManual)
|
||||||
CHECK(AddLowKeys(&as, &bs, i));
|
CHECK(AddLowKeys(&as, &bs, i));
|
||||||
CHECK(SetsAreEqual(as, bs));
|
CHECK(SetsAreEqual(as, bs));
|
||||||
|
|
||||||
for (IntSet::Enum e(as); !e.empty(); e.popFront()) {
|
IntSet::Enum e(as);
|
||||||
|
for (; !e.empty(); e.popFront()) {
|
||||||
uint32_t tmp = LowToHigh::rekey(e.front());
|
uint32_t tmp = LowToHigh::rekey(e.front());
|
||||||
if (tmp != e.front())
|
if (tmp != e.front())
|
||||||
e.rekeyFront(tmp);
|
e.rekeyFront(tmp);
|
||||||
}
|
}
|
||||||
|
CHECK(e.endEnumeration());
|
||||||
CHECK(SlowRekey<LowToHigh>(&bs));
|
CHECK(SlowRekey<LowToHigh>(&bs));
|
||||||
|
|
||||||
CHECK(SetsAreEqual(as, bs));
|
CHECK(SetsAreEqual(as, bs));
|
||||||
|
@ -243,7 +247,8 @@ BEGIN_TEST(testHashRekeyManualRemoval)
|
||||||
CHECK(AddLowKeys(&am, &bm, i));
|
CHECK(AddLowKeys(&am, &bm, i));
|
||||||
CHECK(MapsAreEqual(am, bm));
|
CHECK(MapsAreEqual(am, bm));
|
||||||
|
|
||||||
for (IntMap::Enum e(am); !e.empty(); e.popFront()) {
|
IntMap::Enum e(am);
|
||||||
|
for (; !e.empty(); e.popFront()) {
|
||||||
if (LowToHighWithRemoval::shouldBeRemoved(e.front().key)) {
|
if (LowToHighWithRemoval::shouldBeRemoved(e.front().key)) {
|
||||||
e.removeFront();
|
e.removeFront();
|
||||||
} else {
|
} else {
|
||||||
|
@ -252,6 +257,7 @@ BEGIN_TEST(testHashRekeyManualRemoval)
|
||||||
e.rekeyFront(tmp);
|
e.rekeyFront(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CHECK(e.endEnumeration());
|
||||||
CHECK(SlowRekey<LowToHighWithRemoval>(&bm));
|
CHECK(SlowRekey<LowToHighWithRemoval>(&bm));
|
||||||
|
|
||||||
CHECK(MapsAreEqual(am, bm));
|
CHECK(MapsAreEqual(am, bm));
|
||||||
|
@ -269,7 +275,8 @@ BEGIN_TEST(testHashRekeyManualRemoval)
|
||||||
CHECK(AddLowKeys(&as, &bs, i));
|
CHECK(AddLowKeys(&as, &bs, i));
|
||||||
CHECK(SetsAreEqual(as, bs));
|
CHECK(SetsAreEqual(as, bs));
|
||||||
|
|
||||||
for (IntSet::Enum e(as); !e.empty(); e.popFront()) {
|
IntSet::Enum e(as);
|
||||||
|
for (; !e.empty(); e.popFront()) {
|
||||||
if (LowToHighWithRemoval::shouldBeRemoved(e.front())) {
|
if (LowToHighWithRemoval::shouldBeRemoved(e.front())) {
|
||||||
e.removeFront();
|
e.removeFront();
|
||||||
} else {
|
} else {
|
||||||
|
@ -278,6 +285,7 @@ BEGIN_TEST(testHashRekeyManualRemoval)
|
||||||
e.rekeyFront(tmp);
|
e.rekeyFront(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CHECK(e.endEnumeration());
|
||||||
CHECK(SlowRekey<LowToHighWithRemoval>(&bs));
|
CHECK(SlowRekey<LowToHighWithRemoval>(&bs));
|
||||||
|
|
||||||
CHECK(SetsAreEqual(as, bs));
|
CHECK(SetsAreEqual(as, bs));
|
||||||
|
|
Загрузка…
Ссылка в новой задаче