b=605872 refactor mFontTableCache, providing a sharing/saving distinction to fix a hb_blob_t leak r=jfkthame a=blocking

--HG--
extra : transplant_source : u%FB%E2%9D%A8g%1ASG%FB%A4%12%A2kGi%882%ED9
This commit is contained in:
Karl Tomlinson 2010-11-26 14:07:09 +13:00
Родитель 95764752f5
Коммит 735f50e2ac
2 изменённых файлов: 207 добавлений и 74 удалений

Просмотреть файл

@ -189,26 +189,132 @@ gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, PRBool aNeedsBold)
return f;
}
gfxFontEntry::FontTableCacheEntry::FontTableCacheEntry
(nsTArray<PRUint8>& aBuffer,
PRUint32 aTag,
nsClassHashtable<nsUint32HashKey,FontTableCacheEntry>& aCache)
: mTag(aTag), mCache(aCache)
/**
* FontTableBlobData
*
* See FontTableHashEntry for the general strategy.
*/
class gfxFontEntry::FontTableBlobData {
public:
// Adopts the content of aBuffer.
// Pass a non-null aHashEntry only if it should be cleared if/when this
// FontTableBlobData is deleted.
FontTableBlobData(nsTArray<PRUint8>& aBuffer,
FontTableHashEntry *aHashEntry)
: mHashEntry(aHashEntry), mHashtable()
{
MOZ_COUNT_CTOR(FontTableBlobData);
mTableData.SwapElements(aBuffer);
}
~FontTableBlobData() {
MOZ_COUNT_DTOR(FontTableBlobData);
if (mHashEntry) {
if (mHashtable) {
mHashtable->RemoveEntry(mHashEntry->GetKey());
} else {
mHashEntry->Clear();
}
}
}
// Useful for creating blobs
const char *GetTable() const
{
return reinterpret_cast<const char*>(mTableData.Elements());
}
PRUint32 GetTableLength() const { return mTableData.Length(); }
// Tell this FontTableBlobData to remove the HashEntry when this is
// destroyed.
void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable)
{
mHashtable = aHashtable;
}
// Disconnect from the HashEntry (because the blob has already been
// removed from the hashtable).
void ForgetHashEntry()
{
mHashEntry = nsnull;
}
private:
// The font table data block, owned (via adoption)
nsTArray<PRUint8> mTableData;
// The blob destroy function needs to know the hashtable entry,
FontTableHashEntry *mHashEntry;
// and the owning hashtable, so that it can remove the entry.
nsTHashtable<FontTableHashEntry> *mHashtable;
// not implemented
FontTableBlobData(const FontTableBlobData&);
};
void
gfxFontEntry::FontTableHashEntry::SaveTable(nsTArray<PRUint8>& aTable)
{
MOZ_COUNT_CTOR(FontTableCacheEntry);
mData.SwapElements(aBuffer);
mBlob = hb_blob_create((const char*)mData.Elements(), mData.Length(),
Clear();
// adopts elements of aTable
FontTableBlobData *data = new FontTableBlobData(aTable, nsnull);
mBlob = hb_blob_create(data->GetTable(), data->GetTableLength(),
HB_MEMORY_MODE_READONLY,
gfxFontEntry::FontTableCacheEntry::Destroy,
this);
DeleteFontTableBlobData, data);
}
/* static */ void
gfxFontEntry::FontTableCacheEntry::Destroy(void *aUserData)
hb_blob_t *
gfxFontEntry::FontTableHashEntry::
ShareTableAndGetBlob(nsTArray<PRUint8>& aTable,
nsTHashtable<FontTableHashEntry> *aHashtable)
{
gfxFontEntry::FontTableCacheEntry *ftce =
static_cast<gfxFontEntry::FontTableCacheEntry*>(aUserData);
ftce->mCache.Remove(ftce->mTag);
Clear();
// adopts elements of aTable
mSharedBlobData = new FontTableBlobData(aTable, this);
mBlob = hb_blob_create(mSharedBlobData->GetTable(),
mSharedBlobData->GetTableLength(),
HB_MEMORY_MODE_READONLY,
DeleteFontTableBlobData, mSharedBlobData);
if (!mSharedBlobData) {
// The FontTableBlobData was destroyed during hb_blob_create().
// The (empty) blob is still be held in the hashtable with a strong
// reference.
return hb_blob_reference(mBlob);
}
// Tell the FontTableBlobData to remove this hash entry when destroyed.
// The hashtable does not keep a strong reference.
mSharedBlobData->ManageHashEntry(aHashtable);
return mBlob;
}
void
gfxFontEntry::FontTableHashEntry::Clear()
{
// If the FontTableBlobData is managing the hash entry, then the blob is
// not owned by this HashEntry; otherwise there is strong reference to the
// blob that must be removed.
if (mSharedBlobData) {
mSharedBlobData->ForgetHashEntry();
mSharedBlobData = nsnull;
} else if (mBlob) {
hb_blob_destroy(mBlob);
}
mBlob = nsnull;
}
// a hb_destroy_func for hb_blob_create
/* static */ void
gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData)
{
delete static_cast<FontTableBlobData*>(aBlobData);
}
hb_blob_t *
gfxFontEntry::FontTableHashEntry::GetBlob() const
{
return hb_blob_reference(mBlob);
}
hb_blob_t *
@ -220,26 +326,22 @@ gfxFontEntry::GetFontTable(PRUint32 aTag)
mFontTableCache.Init(10);
}
FontTableCacheEntry *entry = nsnull;
if (!mFontTableCache.Get(aTag, &entry)) {
nsTArray<PRUint8> buffer;
if (NS_SUCCEEDED(GetFontTable(aTag, buffer))) {
entry = new FontTableCacheEntry(buffer, // adopts buffer elements
aTag, mFontTableCache);
if (mFontTableCache.Put(aTag, entry)) {
return entry->GetBlob();
}
hb_blob_destroy(entry->GetBlob());
delete entry; // we failed to cache it!
return nsnull;
}
}
FontTableHashEntry *entry = mFontTableCache.GetEntry(aTag);
if (entry) {
return hb_blob_reference(entry->GetBlob());
return entry->GetBlob();
}
return nsnull;
entry = mFontTableCache.PutEntry(aTag);
if (NS_UNLIKELY(!entry)) { // OOM
return nsnull;
}
nsTArray<PRUint8> buffer;
if (NS_FAILED(GetFontTable(aTag, buffer))) {
return nsnull; // leaves the null entry cached in the hashtable
}
return entry->ShareTableAndGetBlob(buffer, &mFontTableCache);
}
void
@ -252,19 +354,13 @@ gfxFontEntry::PreloadFontTable(PRUint32 aTag, nsTArray<PRUint8>& aTable)
mFontTableCache.Init(3);
}
FontTableCacheEntry *entry = nsnull;
if (mFontTableCache.Get(aTag, &entry)) {
// this should never happen - it's a logic error in the calling code
// (so ignore the fact that we'll leak the elements of aTable here)
NS_NOTREACHED("can't preload table, already present in cache!");
FontTableHashEntry *entry = mFontTableCache.PutEntry(aTag);
if (NS_UNLIKELY(!entry)) { // OOM
return;
}
// this adopts the buffer elements of aTable
entry = new FontTableCacheEntry(aTable, aTag, mFontTableCache);
if (!mFontTableCache.Put(aTag, entry)) {
NS_WARNING("failed to cache font table!");
}
// adopts elements of aTable
entry->SaveTable(aTable);
}
//////////////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -48,7 +48,6 @@
#include "gfxFontUtils.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "gfxSkipChars.h"
#include "gfxRect.h"
@ -356,8 +355,10 @@ protected:
gfxFontFamily *mFamily;
/*
* Font table cache, to support GetFontTable for harfbuzz.
private:
/**
* Font table hashtable, to support GetFontTable for harfbuzz.
*
* The harfbuzz shaper (and potentially other clients) needs access to raw
* font table data. This needs to be cached so that it can be used
@ -368,50 +369,86 @@ protected:
* Because we may instantiate many gfxFonts for the same physical font
* file (at different sizes), we should ensure that they can share a
* single cached copy of the font tables. To do this, we implement table
* access and caching on the fontEntry rather than the font itself.
* access and sharing on the fontEntry rather than the font itself.
*
* The default implementation uses GetFontTable() to read font table
* data into byte arrays, and caches these in a hashtable along with
* hb_blob_t wrappers. The entry can then return blobs to harfbuzz.
* data into byte arrays, and wraps them in blobs which are registered in
* a hashtable. The hashtable can then return pre-existing blobs to
* harfbuzz.
*
* Harfbuzz will "destroy" the blobs when it is finished with them;
* they are created with a destroy callback that removes them from
* the hashtable when all references are released.
* Harfbuzz will "destroy" the blobs when it is finished with them. When
* the last blob reference is removed, the FontTableBlobData user data
* will remove the blob from the hashtable if still registered.
*/
class FontTableCacheEntry {
public:
// create a cache entry by adopting the content of an existing buffer
FontTableCacheEntry(nsTArray<PRUint8>& aBuffer,
PRUint32 aTag,
nsClassHashtable<nsUint32HashKey,FontTableCacheEntry>& aCache);
~FontTableCacheEntry() {
MOZ_COUNT_DTOR(FontTableCacheEntry);
class FontTableBlobData;
/**
* FontTableHashEntry manages the entries of hb_blob_ts for two
* different situations:
*
* The common situation is to share font table across fonts with the same
* font entry (but different sizes) for use by HarfBuzz. The hashtable
* does not own a strong reference to the blob, but keeps a weak pointer,
* managed by FontTableBlobData. Similarly FontTableBlobData keeps only a
* weak pointer to the hashtable, managed by FontTableHashEntry.
*
* Some font tables are saved here before they would get stripped by OTS
* sanitizing. These are retained for harfbuzz, which does its own
* sanitizing. The hashtable owns a reference, so ownership is simple.
*/
class FontTableHashEntry : public nsUint32HashKey
{
public:
// Declarations for nsTHashtable
typedef nsUint32HashKey KeyClass;
typedef KeyClass::KeyType KeyType;
typedef KeyClass::KeyTypePointer KeyTypePointer;
FontTableHashEntry(KeyTypePointer aTag)
: KeyClass(aTag), mBlob() { };
// Copying transfers blob association.
FontTableHashEntry(FontTableHashEntry& toCopy)
: KeyClass(toCopy), mBlob(toCopy.mBlob)
{
toCopy.mBlob = nsnull;
}
hb_blob_t *GetBlob() const { return mBlob; }
~FontTableHashEntry() { Clear(); }
protected:
// the data block, owned (via adoption) by the entry
nsTArray<PRUint8> mData;
// a harfbuzz blob wrapper that we can return to clients
hb_blob_t *mBlob;
// the blob destroy function needs to know the table tag
// and the owning hashtable, so that it can remove the entry
PRUint32 mTag;
nsClassHashtable<nsUint32HashKey,FontTableCacheEntry>&
mCache;
// FontTable/Blob API
// Transfer (not copy) elements of aTable to a new hb_blob_t and
// return ownership to the caller. A weak reference to the blob is
// recorded in the hashtable entry so that others may use the same
// table.
hb_blob_t *
ShareTableAndGetBlob(nsTArray<PRUint8>& aTable,
nsTHashtable<FontTableHashEntry> *aHashtable);
// Transfer (not copy) elements of aTable to a new hb_blob_t that is
// owned by the hashtable entry.
void SaveTable(nsTArray<PRUint8>& aTable);
// Return a strong reference to the blob.
// Callers must hb_blob_destroy the returned blob.
hb_blob_t *GetBlob() const;
void Clear();
private:
static void DeleteFontTableBlobData(void *aBlobData);
// not implemented
FontTableCacheEntry(const FontTableCacheEntry&);
FontTableHashEntry& operator=(FontTableHashEntry& toCopy);
static void Destroy(void *aUserData);
FontTableBlobData *mSharedBlobData;
hb_blob_t *mBlob;
};
nsClassHashtable<nsUint32HashKey,FontTableCacheEntry> mFontTableCache;
nsTHashtable<FontTableHashEntry> mFontTableCache;
private:
gfxFontEntry(const gfxFontEntry&);
gfxFontEntry& operator=(const gfxFontEntry&);
};