Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix fonts leaks #536

Merged
merged 7 commits into from
Oct 22, 2023
Merged

fix fonts leaks #536

merged 7 commits into from
Oct 22, 2023

Conversation

benoit-pierre
Copy link
Contributor

@benoit-pierre benoit-pierre commented Oct 12, 2023

Main 2 changes:

  • ensure a self-referencing font can still possibly be garbage collected.
  • check and ensure we don't remove the last reference (from the font manager internal list of instances / registrations) to a font with a reference count >1 (leak).

This change is Reviewable

crengine/src/lvfntman.cpp Outdated Show resolved Hide resolved
crengine/src/lvfntman.cpp Show resolved Hide resolved
crengine/src/lvtinydom.cpp Show resolved Hide resolved
crengine/src/lvfntman.cpp Outdated Show resolved Hide resolved
crengine/src/lvfntman.cpp Outdated Show resolved Hide resolved
@poire-z
Copy link
Contributor

poire-z commented Oct 12, 2023

Regarding this commit:

lvfntman: prefer font references
Over using direct pointers.

Again just asking: why ? For consistency, or real technical need by your following commits?

@Frenzie
Copy link
Member

Frenzie commented Oct 12, 2023

I think in theory it's supposed to make it a touch harder to accidentally make a certain class of mistakes. I have no (sufficiently informed) opinion on whether it succeeds at that.

@benoit-pierre
Copy link
Contributor Author

Yes, the whole point of using references is to simplify the instance lifecycle: share access to the fonts while ensuring it's kept alive as long as needed and only destroy the instance when the last reference is destroyed. So it's better to avoid "breaking out" of that by returning a direct pointer.

Clear all font references before calling `UnregisterDocumentFonts`
(so each font reference count can be checked for leaks).
Over using direct pointers.
Ensure each font internal font references are cleared to avoid
circular reference loops that would prevent garbage collecting
some fonts and/or leaking others.
@poire-z poire-z merged commit a32b9ed into koreader:master Oct 22, 2023
1 check passed
if (droppedCount) {
// We need more than 1 gc() for a complete cleanup: to drop font
// instances that were only referenced by dropped fonts (fallbacks,
// bullet, decimal, …). The performance impact of reinstantiating
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This reads asbullet, decimal, â~@¦). for people from the previous century still using latin1 terminals.)

@poire-z
Copy link
Contributor

poire-z commented Mar 16, 2024

@benoit-pierre:
I was scripting the opening of all the books from my history, to check for some crengine log.
I got a crash with some old book I found once on mobilread I think - probably free, the latest version at https://books.djazz.se/fallout-equestria/ doesn't crash - but this one does:
Fallout Equestria Official Ebook190405.zip

03/16/24-22:28:39 DEBUG CreDocument: loading document...
luajit: crengine/src/lvfntman.cpp:7489: void LVFontCache::_removeDocumentFonts(int, LVPtrVector<LVFontCacheItem>&): Assertion `!item->_fnt || item->_fnt.getRefCount() == 1' failed.
#0  0x00007ffff7d62e2c in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff7d13fb2 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x00007ffff7cfe472 in abort () from /lib/x86_64-linux-gnu/libc.so.6
#3  0x00007ffff7cfe395 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#4  0x00007ffff7d0ceb2 in __assert_fail () from /lib/x86_64-linux-gnu/libc.so.6
#5  0x00007fffe050b633 in LVFontCache::_removeDocumentFonts (this=this@entry=0x555556e41fc0, documentId=documentId@entry=4, list=...)
    at crengine/src/lvfntman.cpp:7489
#6  0x00007fffe050b6e0 in LVFontCache::removeDocumentFonts (this=0x555556e41fc0, documentId=4) at crengine/src/lvfntman.cpp:7501
#7  0x00007fffe05161e1 in LVFreeTypeFontManager::UnregisterDocumentFonts (this=<optimized out>, documentId=<optimized out>)
    at crengine/src/lvfntman.cpp:6383
#8  0x00007fffe049babd in ldomDocument::unregisterEmbeddedFonts (this=this@entry=0x55555714b0b0) at crengine/src/lvtinydom.cpp:20294
#9  0x00007fffe05854d3 in ImportEpubDocument (stream=..., m_doc=0x55555714b0b0, progressCallback=0x555556f85cc0, formatCallback=formatCallback@entry=0x5555574917e0,
    metadataOnly=<optimized out>) at crengine/src/epubfmt.cpp:1852

Probably related to this PR (it does not crash with v2023.06.1).
Interested in having a look?

@benoit-pierre
Copy link
Contributor Author

It does not crash on my version thanks to this commit. I haven't investigated further.

@poire-z
Copy link
Contributor

poire-z commented Mar 16, 2024

Feels like PR'ing some of your fixes ?

@benoit-pierre
Copy link
Contributor Author

This commit was not meant to fix that assert, but to prevent hundreds of unnecessary font registrations on some books (FWIR, resulting in 7x slower loads). After reverting that commit, those books still load fine, unlike your EPUB. So there's still a need to investigate why.

@poire-z
Copy link
Contributor

poire-z commented Mar 23, 2024

Not really familiar with all this embeddedfont stuff, I hope you can follow up.
Anyway, I pinpoint what makes it crash:
One of the xhtml fragment of this EPUB (chapter_45.xhtml) has in it:

  <link rel="stylesheet" type="text/css" href="../Styles/style.css"/>
[...]
<style>
@font-face {
        font-family: "Adobe Garamond Pro (embedded)";
        font-weight: normal;
        font-style: normal;
        src: url(../Fonts/AdobeGaramondPro-FoE-Regular.otf);
}
</style>
</head>

It's a duplicated font, also present in style.css - but I don't think the duplication of a same font is the reason.
But the presence of a @font-face in <style> causes this to trigger:

if ( fontList.length() != fontList_nb_before_head_parsing ) {
// New fonts met when parsing <head><style> of some DocFragments
m_doc->unregisterEmbeddedFonts();
// set document font list, and register fonts
m_doc->getEmbeddedFontList().set(fontList);
m_doc->registerEmbeddedFonts();

and calling unregisterEmbeddedFonts() have your assert met all the fonts with a refcount of 2.
(If I skip the assert, it pass through that and render the document, and the normal unregisterEmbeddedFonts() called later on met all the fonts with a refcount of 1.

As it is a codepath rarely taken, it's possible that you could reproduce the crash with your build even if you just add to some fragment:

<style>
@font-face {
        font-family: "crap";
        font-weight: normal;
        font-style: normal;
        src: url(../Fonts/non-existant-font.otf);
}
</style>

@poire-z
Copy link
Contributor

poire-z commented Mar 23, 2024

Or.... may be the fix is logical and simple :) :

--- a/crengine/src/epubfmt.cpp
+++ b/crengine/src/epubfmt.cpp
@@ -2038,14 +2038,15 @@ bool ImportEpubDocument( LVStreamRef stream, ldomDocument * m_doc, LVDocViewCall

     if ( fontList.length() != fontList_nb_before_head_parsing ) {
         // New fonts met when parsing <head><style> of some DocFragments
+        // Drop styles (before unregistering fonts, as they may reference them)
+        m_doc->forceReinitStyles();
+            // todo: we could avoid forceReinitStyles() when embedded fonts are disabled
+            // (but being here is quite rare - and having embedded font disabled even more)
         m_doc->unregisterEmbeddedFonts();
         // set document font list, and register fonts
         m_doc->getEmbeddedFontList().set(fontList);
         m_doc->registerEmbeddedFonts();
         printf("CRE: document loaded, but styles re-init needed (cause: embedded fonts)\n");
-        m_doc->forceReinitStyles();
-        // todo: we could avoid forceReinitStyles() when embedded fonts are disabled
-        // (but being here is quite rare - and having embedded font disabled even more)
     }

     // fragmentCount is not fool proof, best to check if we really made

@benoit-pierre
Copy link
Contributor Author

LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants