Bug 1027921 - Part 2: Switch to nsClassHashtable. r=froydnj

This commit is contained in:
Eric Rahm 2014-08-04 16:15:46 -07:00
parent 1d48eb0a57
commit 2c069186a9

View File

@ -10,9 +10,9 @@
#include <stdlib.h>
#include "plhash.h"
#include "prlock.h"
#include "nsClassHashtable.h"
#include "nsTArray.h"
#ifdef NS_TRACE_MALLOC
@ -177,7 +177,8 @@ public:
typedef nsTArray<ResourceAcquisition> ResourceAcquisitionArray;
private:
typedef nsTArray<PLHashEntry*> HashEntryArray;
struct OrderingEntry;
typedef nsTArray<OrderingEntry*> HashEntryArray;
typedef typename HashEntryArray::index_type index_type;
typedef typename HashEntryArray::size_type size_type;
static const index_type NoIndex = HashEntryArray::NoIndex;
@ -206,43 +207,11 @@ private:
const T* mResource;
};
static void* TableAlloc(void* /*aPool*/, size_t aSize)
{
return operator new(aSize);
}
static void TableFree(void* /*aPool*/, void* aItem)
{
operator delete(aItem);
}
static PLHashEntry* EntryAlloc(void* /*aPool*/, const void* aKey)
{
return new PLHashEntry;
}
static void EntryFree(void* /*aPool*/, PLHashEntry* aEntry, unsigned aFlag)
{
delete static_cast<T*>(const_cast<void*>(aEntry->key));
delete static_cast<OrderingEntry*>(aEntry->value);
aEntry->value = 0;
if (aFlag == HT_FREE_ENTRY) {
delete aEntry;
}
}
static PLHashNumber HashKey(const void* aKey)
{
return static_cast<PLHashNumber>(NS_PTR_TO_INT32(aKey) >> 2);
}
static const PLHashAllocOps kAllocOps;
// Hash table "interface" the rest of the code should use
PLHashEntry** GetEntry(const T* aKey)
{
return PL_HashTableRawLookup(mOrdering, HashKey(aKey), aKey);
}
void PutEntry(T* aKey)
{
PL_HashTableAdd(mOrdering, aKey, new OrderingEntry(aKey));
mOrdering.Put(aKey, new OrderingEntry(aKey));
}
// XXX need these helper methods because OrderingEntry doesn't have
@ -257,9 +226,9 @@ private:
* the "worst" bad case, adding it anyway will cause
* |InTransitiveClosure()| to diverge.
*/
void AddOrder(PLHashEntry* aLT, PLHashEntry* aGT)
void AddOrder(OrderingEntry* aLT, OrderingEntry* aGT)
{
static_cast<OrderingEntry*>(aLT->value)->mOrderedLT
aLT->mOrderedLT
.InsertElementSorted(aGT);
}
@ -269,12 +238,10 @@ private:
*
* Does not consider transitivity.
*/
bool IsOrdered(const PLHashEntry* aFirst, const PLHashEntry* aSecond)
bool IsOrdered(const OrderingEntry* aFirst, const OrderingEntry* aSecond)
const
{
const OrderingEntry* entry =
static_cast<const OrderingEntry*>(aFirst->value);
return entry->mOrderedLT.BinaryIndexOf(aSecond) != NoIndex;
return aFirst->mOrderedLT.BinaryIndexOf(aSecond) != NoIndex;
}
/**
@ -283,10 +250,10 @@ private:
*
* NOTE: this does *not* consider transitive orderings.
*/
PLHashEntry* const* GetOrders(const PLHashEntry* aEntry) const
OrderingEntry* const* GetOrders(const OrderingEntry* aEntry) const
{
return
static_cast<const OrderingEntry*>(aEntry->value)->mOrderedLT.Elements();
aEntry->mOrderedLT.Elements();
}
/**
@ -295,18 +262,18 @@ private:
*
* NOTE: this does *not* consider transitive orderings.
*/
size_type NumOrders(const PLHashEntry* aEntry) const
size_type NumOrders(const OrderingEntry* aEntry) const
{
return
static_cast<const OrderingEntry*>(aEntry->value)->mOrderedLT.Length();
aEntry->mOrderedLT.Length();
}
/** Make a ResourceAcquisition out of |aEntry|. */
ResourceAcquisition MakeResourceAcquisition(const PLHashEntry* aEntry) const
ResourceAcquisition MakeResourceAcquisition(const OrderingEntry* aEntry) const
{
return ResourceAcquisition(
static_cast<const T*>(aEntry->key),
static_cast<const OrderingEntry*>(aEntry->value)->mFirstSeen);
aEntry->mResource,
aEntry->mFirstSeen);
}
// Throwaway RAII lock to make the following code safer.
@ -328,15 +295,8 @@ public:
* that will be checked.
*/
explicit DeadlockDetector(uint32_t aNumResourcesGuess = kDefaultNumBuckets)
: mOrdering(aNumResourcesGuess)
{
mOrdering = PL_NewHashTable(aNumResourcesGuess,
HashKey,
PL_CompareValues, PL_CompareValues,
&kAllocOps, 0);
if (!mOrdering) {
NS_RUNTIMEABORT("couldn't initialize resource ordering table");
}
mLock = PR_NewLock();
if (!mLock) {
NS_RUNTIMEABORT("couldn't allocate deadlock detector lock");
@ -350,7 +310,6 @@ public:
*/
~DeadlockDetector()
{
PL_HashTableDestroy(mOrdering);
PR_DestroyLock(mLock);
}
@ -406,10 +365,11 @@ public:
NS_ASSERTION(aProposed, "null resource");
PRAutoLock _(mLock);
PLHashEntry* second = *GetEntry(aProposed);
OrderingEntry* e = static_cast<OrderingEntry*>(second->value);
if (CallStack::kNone == e->mFirstSeen) {
e->mFirstSeen = aCallContext;
OrderingEntry* proposed = mOrdering.Get(aProposed);
NS_ASSERTION(proposed, "missing ordering entry");
if (CallStack::kNone == proposed->mFirstSeen) {
proposed->mFirstSeen = aCallContext;
}
if (!aLast) {
@ -417,33 +377,34 @@ public:
return 0;
}
PLHashEntry* first = *GetEntry(aLast);
OrderingEntry* current = mOrdering.Get(aLast);
NS_ASSERTION(current, "missing ordering entry");
// this is the crux of the deadlock detector algorithm
if (first == second) {
if (current == proposed) {
// reflexive deadlock. fastpath b/c InTransitiveClosure is
// not applicable here.
ResourceAcquisitionArray* cycle = new ResourceAcquisitionArray();
if (!cycle) {
NS_RUNTIMEABORT("can't allocate dep. cycle array");
}
cycle->AppendElement(MakeResourceAcquisition(first));
cycle->AppendElement(MakeResourceAcquisition(current));
cycle->AppendElement(ResourceAcquisition(aProposed,
aCallContext));
return cycle;
}
if (InTransitiveClosure(first, second)) {
if (InTransitiveClosure(current, proposed)) {
// we've already established |aLast < aProposed|. all is well.
return 0;
}
if (InTransitiveClosure(second, first)) {
if (InTransitiveClosure(proposed, current)) {
// the order |aProposed < aLast| has been deduced, perhaps
// transitively. we're attempting to violate that
// constraint by acquiring resources in the order
// |aLast < aProposed|, and thus we may deadlock under the
// right conditions.
ResourceAcquisitionArray* cycle = GetDeductionChain(second, first);
ResourceAcquisitionArray* cycle = GetDeductionChain(proposed, current);
// show how acquiring |aProposed| would complete the cycle
cycle->AppendElement(ResourceAcquisition(aProposed,
aCallContext));
@ -452,7 +413,7 @@ public:
// |aLast|, |aProposed| are unordered according to our
// poset. this is fine, but we now need to add this
// ordering constraint.
AddOrder(first, second);
AddOrder(current, proposed);
return 0;
}
@ -462,8 +423,8 @@ public:
*
* @precondition |aStart != aTarget|
*/
bool InTransitiveClosure(const PLHashEntry* aStart,
const PLHashEntry* aTarget) const
bool InTransitiveClosure(const OrderingEntry* aStart,
const OrderingEntry* aTarget) const
{
if (IsOrdered(aStart, aTarget)) {
return true;
@ -471,7 +432,7 @@ public:
index_type i = 0;
size_type len = NumOrders(aStart);
for (const PLHashEntry* const* it = GetOrders(aStart); i < len; ++i, ++it) {
for (const OrderingEntry* const* it = GetOrders(aStart); i < len; ++i, ++it) {
if (InTransitiveClosure(*it, aTarget)) {
return true;
}
@ -495,8 +456,8 @@ public:
*
* @precondition |aStart != aTarget|
*/
ResourceAcquisitionArray* GetDeductionChain(const PLHashEntry* aStart,
const PLHashEntry* aTarget)
ResourceAcquisitionArray* GetDeductionChain(const OrderingEntry* aStart,
const OrderingEntry* aTarget)
{
ResourceAcquisitionArray* chain = new ResourceAcquisitionArray();
if (!chain) {
@ -511,8 +472,8 @@ public:
// precondition: |aStart != aTarget|
// invariant: |aStart| is the last element in |aChain|
bool GetDeductionChain_Helper(const PLHashEntry* aStart,
const PLHashEntry* aTarget,
bool GetDeductionChain_Helper(const OrderingEntry* aStart,
const OrderingEntry* aTarget,
ResourceAcquisitionArray* aChain)
{
if (IsOrdered(aStart, aTarget)) {
@ -522,7 +483,7 @@ public:
index_type i = 0;
size_type len = NumOrders(aStart);
for (const PLHashEntry* const* it = GetOrders(aStart); i < len; ++i, ++it) {
for (const OrderingEntry* const* it = GetOrders(aStart); i < len; ++i, ++it) {
aChain->AppendElement(MakeResourceAcquisition(*it));
if (GetDeductionChain_Helper(*it, aTarget, aChain)) {
return true;
@ -536,7 +497,8 @@ public:
* The partial order on resource acquisitions used by the deadlock
* detector.
*/
PLHashTable* mOrdering; // T* -> PLHashEntry<OrderingEntry>
nsClassHashtable<nsPtrHashKey<const T>, OrderingEntry> mOrdering;
/**
* Protects contentious methods.
@ -551,13 +513,6 @@ private:
};
template<typename T>
const PLHashAllocOps DeadlockDetector<T>::kAllocOps = {
DeadlockDetector<T>::TableAlloc, DeadlockDetector<T>::TableFree,
DeadlockDetector<T>::EntryAlloc, DeadlockDetector<T>::EntryFree
};
template<typename T>
// FIXME bug 456272: tune based on average workload
const uint32_t DeadlockDetector<T>::kDefaultNumBuckets = 64;