Bug 763636 - Do not OOM after rekeying a HashTable; r=luke

This makes the overloaded case fixup after a rekey infallible by rehashing into
the existing table, rather than allocating a new one.

--HG--
rename : mobile/android/base/resources/drawable/tabs_button_contracted.xml => mobile/android/base/resources/drawable/tabs_button.xml
rename : toolkit/mozapps/update/test/unit/test_0162_appInUse_xp_win_complete.js => toolkit/mozapps/update/test/unit/test_0161_appInUse_xp_win_complete.js
rename : toolkit/mozapps/update/test/unit/test_0174_fileLocked_xp_win_complete.js => toolkit/mozapps/update/test/unit/test_0172_fileLocked_xp_win_complete.js
rename : toolkit/mozapps/update/test/unit/test_0175_fileLocked_xp_win_partial.js => toolkit/mozapps/update/test/unit/test_0173_fileLocked_xp_win_partial.js
rename : toolkit/mozapps/update/test/unit/test_0188_fileInUse_xp_win_complete.js => toolkit/mozapps/update/test/unit/test_0184_fileInUse_xp_win_complete.js
rename : toolkit/mozapps/update/test/unit/test_0189_fileInUse_xp_win_partial.js => toolkit/mozapps/update/test/unit/test_0185_fileInUse_xp_win_partial.js
rename : toolkit/mozapps/update/test/unit/test_0190_rmrfdirFileInUse_xp_win_complete.js => toolkit/mozapps/update/test/unit/test_0186_rmrfdirFileInUse_xp_win_complete.js
rename : toolkit/mozapps/update/test/unit/test_0191_rmrfdirFileInUse_xp_win_partial.js => toolkit/mozapps/update/test/unit/test_0187_rmrfdirFileInUse_xp_win_partial.js
rename : toolkit/mozapps/update/test/unit/test_0203_app_launch_apply_update.js => toolkit/mozapps/update/test/unit/test_0201_app_launch_apply_update.js
rename : toolkit/mozapps/update/test_svc/unit/test_0162_appInUse_xp_win_complete_svc.js => toolkit/mozapps/update/test_svc/unit/test_0161_appInUse_xp_win_complete_svc.js
rename : toolkit/mozapps/update/test_svc/unit/test_0174_fileLocked_xp_win_complete_svc.js => toolkit/mozapps/update/test_svc/unit/test_0172_fileLocked_xp_win_complete_svc.js
rename : toolkit/mozapps/update/test_svc/unit/test_0175_fileLocked_xp_win_partial_svc.js => toolkit/mozapps/update/test_svc/unit/test_0173_fileLocked_xp_win_partial_svc.js
rename : toolkit/mozapps/update/test_svc/unit/test_0188_fileInUse_xp_win_complete_svc.js => toolkit/mozapps/update/test_svc/unit/test_0184_fileInUse_xp_win_complete_svc.js
rename : toolkit/mozapps/update/test_svc/unit/test_0189_fileInUse_xp_win_partial_svc.js => toolkit/mozapps/update/test_svc/unit/test_0185_fileInUse_xp_win_partial_svc.js
rename : toolkit/mozapps/update/test_svc/unit/test_0190_rmrfdirFileInUse_xp_win_complete_svc.js => toolkit/mozapps/update/test_svc/unit/test_0186_rmrfdirFileInUse_xp_win_complete_svc.js
rename : toolkit/mozapps/update/test_svc/unit/test_0191_rmrfdirFileInUse_xp_win_partial_svc.js => toolkit/mozapps/update/test_svc/unit/test_0187_rmrfdirFileInUse_xp_win_partial_svc.js
rename : toolkit/mozapps/update/test_svc/unit/test_0203_app_launch_apply_update_svc.js => toolkit/mozapps/update/test_svc/unit/test_0201_app_launch_apply_update_svc.js
extra : rebase_source : 651c27d020f2e64978742676340b2e284609c3a1
This commit is contained in:
Terrence Cole 2012-06-14 16:05:57 -07:00
parent e703351031
commit a15344ecde
2 changed files with 67 additions and 36 deletions

View File

@ -62,7 +62,7 @@ class HashTableEntry {
JS_ASSERT(isLive()); keyHash |= collisionBit;
}
void unsetCollision() { keyHash &= ~sCollisionBit; }
bool hasCollision() const { JS_ASSERT(isLive()); return keyHash & sCollisionBit; }
bool hasCollision() const { return keyHash & sCollisionBit; }
bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; }
HashNumber getKeyHash() const { JS_ASSERT(!hasCollision()); return keyHash; }
};
@ -185,7 +185,7 @@ class HashTable : private AllocPolicy
friend class HashTable;
HashTable &table;
bool added;
bool rekeyed;
bool removed;
/* Not copyable. */
@ -194,7 +194,7 @@ class HashTable : private AllocPolicy
public:
template<class Map> explicit
Enum(Map &map) : Range(map.all()), table(map.impl), added(false), removed(false) {}
Enum(Map &map) : Range(map.all()), table(map.impl), rekeyed(false), removed(false) {}
/*
* Removes the |front()| element from the table, leaving |front()|
@ -224,7 +224,7 @@ class HashTable : private AllocPolicy
HashPolicy::setKey(t, const_cast<Key &>(k));
table.remove(*this->cur);
table.putNewInfallible(l, t);
added = true;
rekeyed = true;
this->validEntry = false;
}
@ -234,28 +234,11 @@ class HashTable : private AllocPolicy
/* Potentially rehashes the table. */
~Enum() {
JS_ASSERT(!added);
if (rekeyed)
table.checkOverRemoved();
if (removed)
table.checkUnderloaded();
}
/*
* 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) {
added = false;
if (table.checkOverloaded() == RehashFailed)
return false;
}
if (removed) {
removed = false;
table.checkUnderloaded();
}
return true;
}
};
private:
@ -281,6 +264,7 @@ class HashTable : private AllocPolicy
uint32_t grows; /* table expansions */
uint32_t shrinks; /* table contractions */
uint32_t compresses; /* table compressions */
uint32_t rehashes; /* tombstone decontaminations */
} stats;
# define METER(x) x
#else
@ -601,6 +585,16 @@ class HashTable : private AllocPolicy
return changeTableSize(deltaLog2);
}
/* Infallibly rehash the table if we are overloaded with removals. */
void checkOverRemoved()
{
if (overloaded()) {
METER(stats.rehashes++);
rehashTable();
JS_ASSERT(!overloaded());
}
}
void remove(Entry &e)
{
JS_ASSERT(table);
@ -625,6 +619,52 @@ class HashTable : private AllocPolicy
}
}
/*
* This is identical to changeTableSize(currentSize), but without requiring
* a second table. We do this by recycling the collision bits to tell us if
* the element is already inserted or still waiting to be inserted. Since
* already-inserted elements win any conflicts, we get the same table as we
* would have gotten through random insertion order.
*/
void rehashTable()
{
removedCount = 0;
for (size_t i = 0; i < capacity(); ++i)
table[i].unsetCollision();
for (size_t i = 0; i < capacity();) {
Entry *src = &table[i];
if (!src->isLive() || src->hasCollision()) {
++i;
continue;
}
HashNumber keyHash = src->getKeyHash();
HashNumber h1 = hash1(keyHash, hashShift);
DoubleHash dh = hash2(keyHash, hashShift);
Entry *tgt = &table[h1];
while (true) {
if (!tgt->hasCollision()) {
Swap(src, tgt);
tgt->setCollision();
break;
}
h1 = applyDoubleHash(h1, dh);
tgt = &table[h1];
}
}
/*
* TODO: this algorithm leaves collision bits on *all* elements, even if
* they are on no collision path. We also have the option of setting the
* collision bits correctly on a subsequent pass or skipping the rehash
* unless we are totally filled with tombstones: benchmark to find out
* which approach is best.
*/
}
public:
void clear()
{

View File

@ -193,13 +193,11 @@ BEGIN_TEST(testHashRekeyManual)
CHECK(AddLowKeys(&am, &bm, i));
CHECK(MapsAreEqual(am, bm));
IntMap::Enum e(am);
for (; !e.empty(); e.popFront()) {
for (IntMap::Enum e(am); !e.empty(); e.popFront()) {
uint32_t tmp = LowToHigh::rekey(e.front().key);
if (tmp != e.front().key)
e.rekeyFront(tmp);
}
CHECK(e.endEnumeration());
CHECK(SlowRekey<LowToHigh>(&bm));
CHECK(MapsAreEqual(am, bm));
@ -217,13 +215,11 @@ BEGIN_TEST(testHashRekeyManual)
CHECK(AddLowKeys(&as, &bs, i));
CHECK(SetsAreEqual(as, bs));
IntSet::Enum e(as);
for (; !e.empty(); e.popFront()) {
for (IntSet::Enum e(as); !e.empty(); e.popFront()) {
uint32_t tmp = LowToHigh::rekey(e.front());
if (tmp != e.front())
e.rekeyFront(tmp);
}
CHECK(e.endEnumeration());
CHECK(SlowRekey<LowToHigh>(&bs));
CHECK(SetsAreEqual(as, bs));
@ -247,8 +243,7 @@ BEGIN_TEST(testHashRekeyManualRemoval)
CHECK(AddLowKeys(&am, &bm, i));
CHECK(MapsAreEqual(am, bm));
IntMap::Enum e(am);
for (; !e.empty(); e.popFront()) {
for (IntMap::Enum e(am); !e.empty(); e.popFront()) {
if (LowToHighWithRemoval::shouldBeRemoved(e.front().key)) {
e.removeFront();
} else {
@ -257,7 +252,6 @@ BEGIN_TEST(testHashRekeyManualRemoval)
e.rekeyFront(tmp);
}
}
CHECK(e.endEnumeration());
CHECK(SlowRekey<LowToHighWithRemoval>(&bm));
CHECK(MapsAreEqual(am, bm));
@ -275,8 +269,7 @@ BEGIN_TEST(testHashRekeyManualRemoval)
CHECK(AddLowKeys(&as, &bs, i));
CHECK(SetsAreEqual(as, bs));
IntSet::Enum e(as);
for (; !e.empty(); e.popFront()) {
for (IntSet::Enum e(as); !e.empty(); e.popFront()) {
if (LowToHighWithRemoval::shouldBeRemoved(e.front())) {
e.removeFront();
} else {
@ -285,7 +278,6 @@ BEGIN_TEST(testHashRekeyManualRemoval)
e.rekeyFront(tmp);
}
}
CHECK(e.endEnumeration());
CHECK(SlowRekey<LowToHighWithRemoval>(&bs));
CHECK(SetsAreEqual(as, bs));
@ -296,4 +288,3 @@ BEGIN_TEST(testHashRekeyManualRemoval)
return true;
}
END_TEST(testHashRekeyManualRemoval)