Kaydet (Commit) b0203670 authored tarafından Mike Kaganski's avatar Mike Kaganski

Try to handle fonts orphaned from cache gracefully

ImplFontCache::Invalidate deletes unused entries (with zero ref
count), and keeps other entries, but clears everything (including
still used fonts) from its instance list. In the same time, those
fonts' mpFontCache pointers kept pointing to this cache object.
External clients released font instance by calling its cache's
Release method; this itself allows for broken invariants that
cache's mnRef0Count is equal to number of unused font instances
in its list. Also, those fonts never got released, leaking because
ImplFontCache only ever deletes objects in its list.

What is worse, sometimes font caches get deleted after invalidation
(see OutputDevice::ImplClearFontData). As the instance list of the
cache is empty at the point of delete, the cache destructor doesn't
delete those fonts that were orphaned at the moment of invalidation
(those fonts are still used by some client objects, so deleting
them is clearly wrong). But since the font instances still have
cache pointer referring the already deleted cache, releasing the
instances (by calling deleted cache's Release member function)
must lead do some weird results.

This patch moves the Acquire/Release to LogicalFontInstance, which
now checks if its cache pointer is valid, and if it is, the cache
is used to do the work (as before); otherwise, the font handles
its lifetime itself, and deletes itself when its reference counter
is zero. The cache invalidation clears the cache pointer of the
still-used instances.

Change-Id: I29811272dda814cbc81f14668d63e385ce772332
Reviewed-on: https://gerrit.libreoffice.org/47111Reviewed-by: 's avatarMike Kaganski <mike.kaganski@collabora.com>
Tested-by: 's avatarMike Kaganski <mike.kaganski@collabora.com>
üst b6e6c05b
......@@ -36,17 +36,16 @@ class VCL_PLUGIN_PUBLIC LogicalFontInstance
// just declaring the factory function doesn't work AKA
// friend LogicalFontInstance* PhysicalFontFace::CreateFontInstance(const FontSelectPattern&) const;
friend class PhysicalFontFace;
friend class ImplFontCache;
public: // TODO: make data members private
virtual ~LogicalFontInstance();
ImplFontCache * mpFontCache;
FontSelectPattern maFontSelData; // FontSelectionData
ImplFontMetricDataRef mxFontMetric; // Font attributes
const ConvertChar* mpConversion; // used e.g. for StarBats->StarSymbol
long mnLineHeight;
sal_uInt32 mnRefCount;
short mnOwnOrientation; // text angle if lower layers don't rotate text themselves
short mnOrientation; // text angle in 3600 system
bool mbInit; // true if maFontMetric member is valid
......@@ -55,6 +54,9 @@ public: // TODO: make data members private
bool GetFallbackForUnicode( sal_UCS4, FontWeight eWeight, OUString* pFontName ) const;
void IgnoreFallbackForUnicode( sal_UCS4, FontWeight eWeight, const OUString& rFontName );
void Acquire();
void Release();
protected:
explicit LogicalFontInstance(const FontSelectPattern&);
......@@ -64,6 +66,8 @@ private:
// TODO: at least the ones which just differ in orientation, stretching or height
typedef ::std::unordered_map< ::std::pair<sal_UCS4,FontWeight>, OUString > UnicodeFallbackList;
UnicodeFallbackList* mpUnicodeFallbackList;
ImplFontCache * mpFontCache;
sal_uInt32 mnRefCount;
};
#endif // INCLUDED_VCL_INC_FONTINSTANCE_HXX
......
......@@ -33,6 +33,8 @@ class PhysicalFontCollection;
class ImplFontCache
{
// For access to Acquire and Release
friend class LogicalFontInstance;
private:
LogicalFontInstance* mpFirstEntry;
int mnRef0Count; // number of unreferenced LogicalFontInstances
......@@ -44,6 +46,12 @@ private:
FontInstanceList maFontInstanceList;
int CountUnreferencedEntries() const;
bool IsFontInList(const LogicalFontInstance* pFont) const;
/// Increase the refcount of the given LogicalFontInstance.
void Acquire(LogicalFontInstance*);
/// Decrease the refcount and potentially cleanup the entries with zero refcount from the cache.
void Release(LogicalFontInstance*);
public:
ImplFontCache();
......@@ -55,11 +63,6 @@ public:
LogicalFontInstance* GetGlyphFallbackFont( PhysicalFontCollection const *, FontSelectPattern&,
int nFallbackLevel, OUString& rMissingCodes );
/// Increase the refcount of the given LogicalFontInstance.
void Acquire(LogicalFontInstance*);
/// Decrease the refcount and potentially cleanup the entries with zero refcount from the cache.
void Release(LogicalFontInstance*);
void Invalidate();
};
......
......@@ -168,7 +168,7 @@ LogicalFontInstance* ImplFontCache::GetFontInstance( PhysicalFontCollection cons
if( pFontInstance ) // cache hit => use existing font instance
{
// increase the font instance's reference count
Acquire(pFontInstance);
pFontInstance->Acquire();
}
if (!pFontInstance && pFontData)// still no cache hit => create a new font instance
......@@ -243,6 +243,7 @@ LogicalFontInstance* ImplFontCache::GetGlyphFallbackFont( PhysicalFontCollection
void ImplFontCache::Acquire(LogicalFontInstance* pFontInstance)
{
assert(pFontInstance->mpFontCache == this);
assert(IsFontInList(pFontInstance) && "ImplFontCache::Acquire() - font absent in the cache");
if (0 == pFontInstance->mnRefCount++)
--mnRef0Count;
......@@ -252,6 +253,8 @@ void ImplFontCache::Release(LogicalFontInstance* pFontInstance)
{
static const int FONTCACHE_MAX = getenv("LO_TESTNAME") ? 1 : 50;
assert(pFontInstance->mpFontCache == this);
assert(IsFontInList(pFontInstance) && "ImplFontCache::Release() - font absent in the cache");
assert(pFontInstance->mnRefCount > 0 && "ImplFontCache::Release() - font refcount underflow");
if( --pFontInstance->mnRefCount > 0 )
return;
......@@ -282,6 +285,12 @@ void ImplFontCache::Release(LogicalFontInstance* pFontInstance)
assert(mnRef0Count==0 && "ImplFontCache::Release() - refcount0 mismatch");
}
bool ImplFontCache::IsFontInList(const LogicalFontInstance* pFont) const
{
auto Pred = [pFont](const FontInstanceList::value_type& el) -> bool { return el.second == pFont; };
return std::find_if(maFontInstanceList.begin(), maFontInstanceList.end(), Pred) != maFontInstanceList.end();
}
int ImplFontCache::CountUnreferencedEntries() const
{
size_t nCount = 0;
......@@ -307,7 +316,12 @@ void ImplFontCache::Invalidate()
{
LogicalFontInstance* pFontEntry = (*it).second;
if( pFontEntry->mnRefCount > 0 )
{
// These fonts will become orphans after clearing the list below;
// allow them to control their life from now on and wish good luck :)
pFontEntry->mpFontCache = nullptr;
continue;
}
delete pFontEntry;
--mnRef0Count;
......
......@@ -38,16 +38,16 @@ namespace std
LogicalFontInstance::LogicalFontInstance( const FontSelectPattern& rFontSelData )
: mpFontCache(nullptr)
, maFontSelData( rFontSelData )
: maFontSelData( rFontSelData )
, mxFontMetric( new ImplFontMetricData( rFontSelData ))
, mpConversion( nullptr )
, mnLineHeight( 0 )
, mnRefCount( 1 )
, mnOwnOrientation( 0 )
, mnOrientation( 0 )
, mbInit( false )
, mpUnicodeFallbackList( nullptr )
, mpFontCache( nullptr )
, mnRefCount( 1 )
{
maFontSelData.mpFontInstance = this;
}
......@@ -59,6 +59,27 @@ LogicalFontInstance::~LogicalFontInstance()
mxFontMetric = nullptr;
}
void LogicalFontInstance::Acquire()
{
assert(mnRefCount < std::numeric_limits<decltype(mnRefCount)>::max()
&& "LogicalFontInstance::Release() - refcount overflow");
if (mpFontCache)
mpFontCache->Acquire(this);
else
++mnRefCount;
}
void LogicalFontInstance::Release()
{
assert(mnRefCount > 0 && "LogicalFontInstance::Release() - refcount underflow");
if (mpFontCache)
mpFontCache->Release(this);
else
if (--mnRefCount == 0)
delete this;
}
void LogicalFontInstance::AddFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, const OUString& rFontName )
{
if( !mpUnicodeFallbackList )
......
......@@ -593,7 +593,7 @@ void Printer::ImplReleaseFonts()
if ( mpFontInstance )
{
mpFontCache->Release( mpFontInstance );
mpFontInstance->Release();
mpFontInstance = nullptr;
}
......@@ -970,7 +970,7 @@ void Printer::dispose()
// TODO: consolidate duplicate cleanup by Printer and OutputDevice
if ( mpFontInstance )
{
mpFontCache->Release( mpFontInstance );
mpFontInstance->Release();
mpFontInstance = nullptr;
}
if ( mpDeviceFontList )
......@@ -1118,7 +1118,7 @@ bool Printer::SetPrinterProps( const Printer* pPrinter )
pSVData->mpDefInst->DestroyInfoPrinter( mpInfoPrinter );
if ( mpFontInstance )
{
mpFontCache->Release( mpFontInstance );
mpFontInstance->Release();
mpFontInstance = nullptr;
}
if ( mpDeviceFontList )
......@@ -1161,7 +1161,7 @@ bool Printer::SetPrinterProps( const Printer* pPrinter )
if ( mpFontInstance )
{
mpFontCache->Release( mpFontInstance );
mpFontInstance->Release();
mpFontInstance = nullptr;
}
if ( mpDeviceFontList )
......
......@@ -495,7 +495,7 @@ void VirtualDevice::ImplSetReferenceDevice( RefDevMode i_eRefDevMode, sal_Int32
// => clean up the original font lists before getting new ones
if ( mpFontInstance )
{
mpFontCache->Release( mpFontInstance );
mpFontInstance->Release();
mpFontInstance = nullptr;
}
if ( mpDeviceFontList )
......
......@@ -479,7 +479,7 @@ void OutputDevice::ImplClearFontData( const bool bNewFontLists )
// the currently selected logical font is no longer needed
if ( mpFontInstance )
{
mpFontCache->Release( mpFontInstance );
mpFontInstance->Release();
mpFontInstance = nullptr;
}
......@@ -900,7 +900,7 @@ vcl::Font OutputDevice::GetDefaultFont( DefaultFontType nType, LanguageType eLan
aFont.SetFamilyName( pFontInstance->maFontSelData.mpFontData->GetFamilyName() );
else
aFont.SetFamilyName( pFontInstance->maFontSelData.maTargetName );
pOutDev->mpFontCache->Release(pFontInstance);
pFontInstance->Release();
}
}
}
......@@ -1047,7 +1047,7 @@ bool OutputDevice::ImplNewFont() const
LogicalFontInstance* pOldFontInstance = mpFontInstance;
mpFontInstance = mpFontCache->GetFontInstance( mpFontCollection, maFont, aSize, fExactHeight );
if( pOldFontInstance )
mpFontCache->Release( pOldFontInstance );
pOldFontInstance->Release();
LogicalFontInstance* pFontInstance = mpFontInstance;
......@@ -1375,7 +1375,7 @@ std::unique_ptr<SalLayout> OutputDevice::ImplGlyphFallbackLayout( std::unique_pt
if( mpFontInstance->maFontSelData.mpFontData == aFontSelData.mpFontData &&
aMissingCodes.indexOf(0x202F) == -1 )
{
mpFontCache->Release( pFallbackFont );
pFallbackFont->Release();
continue;
}
}
......@@ -1393,7 +1393,7 @@ std::unique_ptr<SalLayout> OutputDevice::ImplGlyphFallbackLayout( std::unique_pt
pMultiSalLayout->SetIncomplete(true);
}
mpFontCache->Release( pFallbackFont );
pFallbackFont->Release();
// break when this fallback was sufficient
if( !rLayoutArgs.PrepareFallback() )
......
......@@ -175,7 +175,7 @@ void OutputDevice::dispose()
// release the active font instance
if( mpFontInstance )
mpFontCache->Release( mpFontInstance );
mpFontInstance->Release();
// remove cached results of GetDevFontList/GetDevSizeList
// TODO: use smart pointers for them
......
......@@ -625,7 +625,7 @@ void OutputDevice::ImplReleaseFonts()
if ( mpFontInstance )
{
mpFontCache->Release( mpFontInstance );
mpFontInstance->Release();
mpFontInstance = nullptr;
}
......
......@@ -1768,7 +1768,7 @@ void Window::ImplNewInputContext()
pFocusWin->ImplGetFrame()->SetInputContext( &aNewContext );
if ( pFontInstance )
pFocusWin->mpFontCache->Release( pFontInstance );
pFontInstance->Release();
}
void Window::doLazyDelete()
......
......@@ -890,7 +890,7 @@ void WinSalGraphics::SetFont( const FontSelectPattern* pFont, int nFallbackLevel
mhFonts[ i ] = nullptr;
if (mpWinFontEntry[i])
{
GetWinFontEntry(i)->mpFontCache->Release(GetWinFontEntry(i));
GetWinFontEntry(i)->Release();
}
mpWinFontEntry[i] = nullptr;
mpWinFontData[i] = nullptr;
......@@ -902,13 +902,13 @@ void WinSalGraphics::SetFont( const FontSelectPattern* pFont, int nFallbackLevel
assert(pFont->mpFontData);
if (mpWinFontEntry[nFallbackLevel])
{
GetWinFontEntry(nFallbackLevel)->mpFontCache->Release(GetWinFontEntry(nFallbackLevel));
GetWinFontEntry(nFallbackLevel)->Release();
}
// WinSalGraphics::GetEmbedFontData does not set mpFontInstance
// since it is interested in font file data only.
if (pFont->mpFontInstance)
{
pFont->mpFontInstance->mpFontCache->Acquire(pFont->mpFontInstance);
pFont->mpFontInstance->Acquire();
}
mpWinFontEntry[ nFallbackLevel ] = reinterpret_cast<WinFontInstance*>( pFont->mpFontInstance );
mpWinFontData[ nFallbackLevel ] = static_cast<const WinFontFace*>( pFont->mpFontData );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment