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

Fontconfig: Make init and teardown more robust. #1793

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

saurtron
Copy link
Collaborator

@saurtron saurtron commented Dec 2, 2024

Work done

  • Remove duplicate initialization from CheckGenFontConfigFast and CheckGenFontConfigFull.
    • now it's all InitFontconfig.
  • Better check and report of error conditions on fontconfig initialization and destructor.
  • Rework fontconfig initialization since it wasn't doing it well. See this comment for more info.

Related issues

Explanation

The idea here is make the whole thing more robust, this might fix crashes on linux when fontconfig can't init properly due to environment incompatibilities, or at least to workaround using UseFontConfigLib = 0.

Also it should fix fontconfig still loading when UseFontConfigLib = 0 option is in use (the duplicated init at CheckGenFontConfigFast wasn't checking the flag).

This should be functionally equivalent to previous code, just more error checks, less duplicated init and proper fontconfig initialization procedure.

The initialization has been reworked quite a bit (see comment below), so it will need some windows testing.

Turns out we were doing it very badly, since half the methods we use
already were calling the other methods, still having multiple
initialization.

This should be a really redundancy (and crash) free configuration, while
doing best possible use of system and local cache.
@saurtron
Copy link
Collaborator Author

saurtron commented Dec 2, 2024

Hey @lhog and @sprunk I had to update this branch again with a new fontconfig initialization ordering.

After a lot more digging and testing on borked situations like the ZorinOs one, I have realized we were still not doing it properly. Also noticed no matter how much error catching and checking we try to do, ZorinOs situation doesn't even report any errors, it's all warnings :P.

Fact is FcInit doesn't even load the library itself, what it does is load everything (the chain is FcInit -> FcConfigInit -> FcConfigEnsure -> FcInitLoadConfigAndFonts ). (a call to FcInitLoadConfigAndFonts in disguise)

FcInitLoadConfigAndFonts basically does FcConfigCreate, FcConfigParseAndLoad, and FcConfigBuildFonts.

All before we can configure a thing. The would be fine if not because we want to include our own cache in the middle, could use (FcCreateConfig + FcInitLoadConfigAndFonts) it but it's a bit awkward because even if it does use any FcConfig previously created, it doesn't accept it as parameter, also doesn't return any success info That's why I call FcConfigParseAndLoad and FcConfigBuildFonts, as the code looks more rational and understandable when providing info and having a result to check.

Althouth tbh, not sure all the passing the config ptr around is doing much good either, fontconfig keeps its own pointer after all.

Regarding FcConfigBuildFonts, docs say "Note that any changes to the configuration after this call (through FcConfigParseAndLoad or FcConfigParseAndLoadFromMemory) have indeterminate effects.". Before this PR it was actually run probably 4 times or more.

I have been digging through fontconfig code to see what the code actually does, both for old versions and current main, just to make sure, because the api docs are really sparse and they really don't tell the story. By comparing old and new fontconfig code I can be reasonably secure my conclusions are correct and not spurious.

I propose a new initialization here (the one implemented in this PR).

  1. FcConfigCreate: First create a clean config object
  2. FcConfigParseAndLoadFromMemory: Add our own cache, the system one will still be used if present
  3. FcConfigParseAndLoad: Load system config files
  4. FcConfigBuildFonts: Build system caches if needed (this normally shouldn't be needed, but in cases where the system cache isn't workable like on bad linux versions it will be done the first time, this takes really long :P)

That's it. After that, goes the app dir init like before.

There's other path we could take:

  • (b) FcConfigCreate + FcConfigParseAndLoadFromMemory + FcConfigSetCurrent + FcInitLoadConfigAndFonts

This does a couple more things than what I'm doing rn so might be better. It's just what I said before: It doesn't accept the config arg, and doesn't return a bool. On the other hand this gives us a couple more functionality apparently not available with the other methods: It does FcConfigParseOnly (config, (const FcChar8 *)FC_TEMPLATEDIR, FcFalse) and FcInitDebug() are being called there, and also it does some kind of ad-hoc fallback cache thing.

I'm torn here, but still like the PR code more than the other option. Thing with option (b), is it calls FcInitDebug, which can be useful, although it's situational it gives access to some env vars that can help sometimes. Not being able to check errors puts me off, although tbh it doesn't usually throw errors. Actually, seems like FcInitDebug gets called by fontconfig ft related methods, like FcFreeTypeQueryFace or FcFreeTypeQuery, so probably not a big issue.

With this initialization, I managed to run on the broken situations like ZorinOs, as well as normal situation and seems to work fine. On ZorinOs it will get stuck the first time a bit building the caches, but after that it goes on and fontconfig even works! XD. I could use the fallback fonts and everything.

Without this reorganization ZorinOs would work, but get stuck loading without having a usable cache, so always taking too long and getting watchdogged by the engine... works in the end but the tracebacks at infolog.txt look very bad and always take long to start too. It's bad.

This should be tested on windows though, that's for sure. I'm pretty sure it's quite optimal though.

@saurtron saurtron added the fonts label Dec 2, 2024
@saurtron
Copy link
Collaborator Author

saurtron commented Dec 3, 2024

Even more churn here :_D

After a bit of windows testing seems the way I determined to be most compatible on linux doesn't work on windows (works but doesn't load system fonts).

Here for now I reverted to previous init, but I intend to test a more ellaborate method that I think will work in all situations, for now I put it at a separate branch to avoid too much churn here: robust-fontconfig-init-destroy-v2, branch comparison: comparison

The idea there is try to load system config, if not available (as happens in windows), revert to FcInitLoadConfigAndFonts that has some fallback methods not available through other api.

@saurtron
Copy link
Collaborator Author

saurtron commented Dec 4, 2024

After further testing on windows, newer linux and older linux, I think I finally have an init mechanism that works fine for all cases.

Updated the branch with that.

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

Successfully merging this pull request may close these issues.

Freeze on Zorin OS with Fontconfig error
2 participants