mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
b945b71cfa
This change splits PLDHashTable::Iterator::NextEntry() into two separate functions, which allow you to get the current element and advance the iterator separately, which means you can use a for-loop to iterate instead of a while-loop. As part of this change, the internals of PLDHashTable::Iterator were significantly changed and simplified (and modelled after js::HashTable's equivalent code). It's no longer duplicating code from PL_DHashTableEnumerator. The chaos mode code was a casualty of this, but given how unreliable that code has proven to be (see bug 1173212, bug 1174046) this is for the best. (We can reimplement chaos mode once PLDHashTable::Iterator is back on more solid footing again, if we think it's important.) All these changes will make it much easier to add an alternative Iterator that removes elements, which was turning out to be difficult with the prior code. In order to make the for-loop header usually fit on a single line, I deliberately renamed a bunch of things to have shorter names. In summary, you used to write this: PLDHashTable::Iterator iter(&table); while (iter.HasMoreEntries()) { auto entry = static_cast<FooEntry*>(iter.NextEntry()); // ... do stuff with |entry| ... } // iter's scope extends beyond here and now you write this: for (auto iter = table.Iter(); !iter.Done(); iter.Next()) { auto entry = static_cast<FooEntry*>(iter.Get()); // ... do stuff with |entry| ... } // iter's scope doesn't reach here
380 lines
11 KiB
C++
380 lines
11 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/MemoryReporting.h"
|
|
#include "nsCache.h"
|
|
#include <limits.h>
|
|
|
|
#include "nscore.h"
|
|
#include "nsDiskCacheBinding.h"
|
|
#include "nsCacheService.h"
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
* static hash table callback functions
|
|
*
|
|
*****************************************************************************/
|
|
struct HashTableEntry : PLDHashEntryHdr {
|
|
nsDiskCacheBinding * mBinding;
|
|
};
|
|
|
|
|
|
static PLDHashNumber
|
|
HashKey( PLDHashTable *table, const void *key)
|
|
{
|
|
return (PLDHashNumber) NS_PTR_TO_INT32(key);
|
|
}
|
|
|
|
|
|
static bool
|
|
MatchEntry(PLDHashTable * /* table */,
|
|
const PLDHashEntryHdr * header,
|
|
const void * key)
|
|
{
|
|
HashTableEntry * hashEntry = (HashTableEntry *) header;
|
|
return (hashEntry->mBinding->mRecord.HashNumber() == (PLDHashNumber) NS_PTR_TO_INT32(key));
|
|
}
|
|
|
|
static void
|
|
MoveEntry(PLDHashTable * /* table */,
|
|
const PLDHashEntryHdr * src,
|
|
PLDHashEntryHdr * dst)
|
|
{
|
|
((HashTableEntry *)dst)->mBinding = ((HashTableEntry *)src)->mBinding;
|
|
}
|
|
|
|
|
|
static void
|
|
ClearEntry(PLDHashTable * /* table */,
|
|
PLDHashEntryHdr * header)
|
|
{
|
|
((HashTableEntry *)header)->mBinding = nullptr;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* Utility Functions
|
|
*****************************************************************************/
|
|
nsDiskCacheBinding *
|
|
GetCacheEntryBinding(nsCacheEntry * entry)
|
|
{
|
|
return (nsDiskCacheBinding *) entry->Data();
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* nsDiskCacheBinding
|
|
*****************************************************************************/
|
|
|
|
NS_IMPL_ISUPPORTS0(nsDiskCacheBinding)
|
|
|
|
nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record)
|
|
: mCacheEntry(entry)
|
|
, mStreamIO(nullptr)
|
|
, mDeactivateEvent(nullptr)
|
|
{
|
|
NS_ASSERTION(record->ValidRecord(), "bad record");
|
|
PR_INIT_CLIST(this);
|
|
mRecord = *record;
|
|
mDoomed = entry->IsDoomed();
|
|
mGeneration = record->Generation(); // 0 == uninitialized, or data & meta using block files
|
|
}
|
|
|
|
nsDiskCacheBinding::~nsDiskCacheBinding()
|
|
{
|
|
// Grab the cache lock since the binding is stored in nsCacheEntry::mData
|
|
// and it is released using nsCacheService::ReleaseObject_Locked() which
|
|
// releases the object outside the cache lock.
|
|
nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHEBINDING_DESTRUCTOR));
|
|
|
|
NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list");
|
|
if (!PR_CLIST_IS_EMPTY(this))
|
|
PR_REMOVE_LINK(this); // XXX why are we still on a list?
|
|
|
|
// sever streamIO/binding link
|
|
if (mStreamIO) {
|
|
if (NS_FAILED(mStreamIO->ClearBinding()))
|
|
nsCacheService::DoomEntry(mCacheEntry);
|
|
NS_RELEASE(mStreamIO);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsDiskCacheBinding::EnsureStreamIO()
|
|
{
|
|
if (!mStreamIO) {
|
|
mStreamIO = new nsDiskCacheStreamIO(this);
|
|
if (!mStreamIO) return NS_ERROR_OUT_OF_MEMORY;
|
|
NS_ADDREF(mStreamIO);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* nsDiskCacheBindery
|
|
*
|
|
* Keeps track of bound disk cache entries to detect for collisions.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
const PLDHashTableOps nsDiskCacheBindery::ops =
|
|
{
|
|
HashKey,
|
|
MatchEntry,
|
|
MoveEntry,
|
|
ClearEntry
|
|
};
|
|
|
|
|
|
nsDiskCacheBindery::nsDiskCacheBindery()
|
|
: table(&ops, sizeof(HashTableEntry), kInitialTableLength)
|
|
, initialized(false)
|
|
{
|
|
}
|
|
|
|
|
|
nsDiskCacheBindery::~nsDiskCacheBindery()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
|
|
void
|
|
nsDiskCacheBindery::Init()
|
|
{
|
|
table.ClearAndPrepareForLength(kInitialTableLength);
|
|
initialized = true;
|
|
}
|
|
|
|
void
|
|
nsDiskCacheBindery::Reset()
|
|
{
|
|
if (initialized) {
|
|
table.ClearAndPrepareForLength(kInitialTableLength);
|
|
initialized = false;
|
|
}
|
|
}
|
|
|
|
|
|
nsDiskCacheBinding *
|
|
nsDiskCacheBindery::CreateBinding(nsCacheEntry * entry,
|
|
nsDiskCacheRecord * record)
|
|
{
|
|
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
|
nsCOMPtr<nsISupports> data = entry->Data();
|
|
if (data) {
|
|
NS_ERROR("cache entry already has bind data");
|
|
return nullptr;
|
|
}
|
|
|
|
nsDiskCacheBinding * binding = new nsDiskCacheBinding(entry, record);
|
|
if (!binding) return nullptr;
|
|
|
|
// give ownership of the binding to the entry
|
|
entry->SetData(binding);
|
|
|
|
// add binding to collision detection system
|
|
nsresult rv = AddBinding(binding);
|
|
if (NS_FAILED(rv)) {
|
|
entry->SetData(nullptr);
|
|
return nullptr;
|
|
}
|
|
|
|
return binding;
|
|
}
|
|
|
|
|
|
/**
|
|
* FindActiveEntry : to find active colliding entry so we can doom it
|
|
*/
|
|
nsDiskCacheBinding *
|
|
nsDiskCacheBindery::FindActiveBinding(uint32_t hashNumber)
|
|
{
|
|
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
|
// find hash entry for key
|
|
HashTableEntry * hashEntry;
|
|
hashEntry =
|
|
(HashTableEntry *) PL_DHashTableSearch(&table,
|
|
(void*)(uintptr_t) hashNumber);
|
|
if (!hashEntry) return nullptr;
|
|
|
|
// walk list looking for active entry
|
|
NS_ASSERTION(hashEntry->mBinding, "hash entry left with no binding");
|
|
nsDiskCacheBinding * binding = hashEntry->mBinding;
|
|
while (binding->mCacheEntry->IsDoomed()) {
|
|
binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
|
|
if (binding == hashEntry->mBinding) return nullptr;
|
|
}
|
|
return binding;
|
|
}
|
|
|
|
|
|
/**
|
|
* AddBinding
|
|
*
|
|
* Called from FindEntry() if we read an entry off of disk
|
|
* - it may already have a generation number
|
|
* - a generation number conflict is an error
|
|
*
|
|
* Called from BindEntry()
|
|
* - a generation number needs to be assigned
|
|
*/
|
|
nsresult
|
|
nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(binding);
|
|
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
|
|
|
// find hash entry for key
|
|
HashTableEntry * hashEntry;
|
|
hashEntry = (HashTableEntry *)
|
|
PL_DHashTableAdd(&table,
|
|
(void *)(uintptr_t) binding->mRecord.HashNumber(),
|
|
fallible);
|
|
if (!hashEntry) return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
if (hashEntry->mBinding == nullptr) {
|
|
hashEntry->mBinding = binding;
|
|
if (binding->mGeneration == 0)
|
|
binding->mGeneration = 1; // if generation uninitialized, set it to 1
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
// insert binding in generation order
|
|
nsDiskCacheBinding * p = hashEntry->mBinding;
|
|
bool calcGeneration = (binding->mGeneration == 0); // do we need to calculate generation?
|
|
if (calcGeneration) binding->mGeneration = 1; // initialize to 1 if uninitialized
|
|
while (1) {
|
|
|
|
if (binding->mGeneration < p->mGeneration) {
|
|
// here we are
|
|
PR_INSERT_BEFORE(binding, p);
|
|
if (hashEntry->mBinding == p)
|
|
hashEntry->mBinding = binding;
|
|
break;
|
|
}
|
|
|
|
if (binding->mGeneration == p->mGeneration) {
|
|
if (calcGeneration) ++binding->mGeneration; // try the next generation
|
|
else {
|
|
NS_ERROR("### disk cache: generations collide!");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
p = (nsDiskCacheBinding *)PR_NEXT_LINK(p);
|
|
if (p == hashEntry->mBinding) {
|
|
// end of line: insert here or die
|
|
p = (nsDiskCacheBinding *)PR_PREV_LINK(p); // back up and check generation
|
|
if (p->mGeneration == 255) {
|
|
NS_WARNING("### disk cache: generation capacity at full");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
PR_INSERT_BEFORE(binding, hashEntry->mBinding);
|
|
break;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* RemoveBinding : remove binding from collision detection on deactivation
|
|
*/
|
|
void
|
|
nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
|
|
{
|
|
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
|
if (!initialized) return;
|
|
|
|
HashTableEntry * hashEntry;
|
|
void * key = (void *)(uintptr_t)binding->mRecord.HashNumber();
|
|
|
|
hashEntry = (HashTableEntry*) PL_DHashTableSearch(&table,
|
|
(void*)(uintptr_t) key);
|
|
if (!hashEntry) {
|
|
NS_WARNING("### disk cache: binding not in hashtable!");
|
|
return;
|
|
}
|
|
|
|
if (binding == hashEntry->mBinding) {
|
|
if (PR_CLIST_IS_EMPTY(binding)) {
|
|
// remove this hash entry
|
|
PL_DHashTableRemove(&table,
|
|
(void*)(uintptr_t) binding->mRecord.HashNumber());
|
|
return;
|
|
|
|
} else {
|
|
// promote next binding to head, and unlink this binding
|
|
hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
|
|
}
|
|
}
|
|
PR_REMOVE_AND_INIT_LINK(binding);
|
|
}
|
|
|
|
/**
|
|
* ActiveBindings: return true if any bindings have open descriptors.
|
|
*/
|
|
bool
|
|
nsDiskCacheBindery::ActiveBindings()
|
|
{
|
|
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
|
if (!initialized) return false;
|
|
|
|
for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
|
|
auto entry = static_cast<HashTableEntry*>(iter.Get());
|
|
nsDiskCacheBinding* binding = entry->mBinding;
|
|
nsDiskCacheBinding* head = binding;
|
|
do {
|
|
if (binding->IsActive()) {
|
|
return true;
|
|
}
|
|
binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
|
|
} while (binding != head);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* SizeOfExcludingThis: return the amount of heap memory (bytes) being used by
|
|
* the bindery.
|
|
*/
|
|
size_t
|
|
nsDiskCacheBindery::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
|
{
|
|
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
|
if (!initialized) return 0;
|
|
|
|
size_t size = 0;
|
|
|
|
for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
|
|
auto entry = static_cast<HashTableEntry*>(iter.Get());
|
|
nsDiskCacheBinding* binding = entry->mBinding;
|
|
|
|
nsDiskCacheBinding* head = binding;
|
|
do {
|
|
size += aMallocSizeOf(binding);
|
|
if (binding->mStreamIO) {
|
|
size += binding->mStreamIO->SizeOfIncludingThis(aMallocSizeOf);
|
|
}
|
|
|
|
// No good way to get at mDeactivateEvent internals for proper
|
|
// size, so we use this as an estimate.
|
|
if (binding->mDeactivateEvent) {
|
|
size += aMallocSizeOf(binding->mDeactivateEvent);
|
|
}
|
|
binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
|
|
} while (binding != head);
|
|
}
|
|
|
|
return size;
|
|
}
|