i3 / i3lock

improved screen locker
https://i3wm.org/i3lock
BSD 3-Clause "New" or "Revised" License
921 stars 404 forks source link

memleak in cairo_select_font_face #167

Closed PandorasFox closed 5 years ago

PandorasFox commented 6 years ago

I've squashed all the memleaks in my fork so far except for the small handful that are in libfontconfig.so, which are also . They all seem to be coming from the calls to cairo_select_font_face, which is still apparently just a 'toy' function.

I'll be poking at using cairo glyphs for the text instead, which should be a bit more robust (and not have memory leaks), and should have a fix soonish, if that's alright.

Airblader commented 6 years ago

I'm sorry, I'm a bit confused. What's the issue in i3lock here?

PandorasFox commented 6 years ago

ASAN complains some about memory errors in fontconfig due to cairo's show_text and select_font_face functions being toy/non-production functions. (I think that with a regular version of fontconfig, only ~3 memory leaks are reported, but with a version compiled with ASAN there's... A few thousand).

Additionally, since i3lock keeps setting the font face through cairo's toy functions on each redraw, it's a bit inefficient and hits up fontconfig on each tick. Depending on the version of fontconfig at runtime, this might cause a memory error on each tick.

Airblader commented 6 years ago

Thanks for the explanation; so the i3lock issue here is the unnecessary calls, right? The memory errors seem to be an issue of cairo if I understand you correctly. :-)

PandorasFox commented 6 years ago

Pretty much. The real issue with i3lock is using the "toy" Cairo functions, which cause the memory leaks.

I'm awful at naming things, including issues :)

Airblader commented 6 years ago

I haven't looked into those functions, but if they don't do anything we can also remove the calls.

PandorasFox commented 6 years ago

They're currently necessary for displaying the "Verifying..." /"Incorrect" /etc text, but they're basically the quick/inefficient way to get text done with Cairo.

The more correct way is for i3lock to lookup the font, then supply it to Cairo and cache that, to avoid using the toy functions and repeated lookups, since the toy functions don't include reference counting on the fonts they return/don't guarantee longevity.

Airblader commented 6 years ago

If the efforts to do so are reasonable, and I assume they are, I think it'd make sense to change it to what you described. PRs welcome, but I think you already mentioned you're preparing for one, right? :-)

PandorasFox commented 6 years ago

I made #168 last night :)

ghost commented 6 years ago

Is #168 going to be merged soon?

psychon commented 6 years ago

Additionally, since i3lock keeps setting the font face through cairo's toy functions on each redraw, it's a bit inefficient and hits up fontconfig on each tick.

Are you sure? From a quick look at cairo's code, there is a hash table caching things. Also, do you know about cairo_debug_reset_static_data()? (That clears all of cairo's caches and most likely explodes spectacularly if you still have a reference to anything cairo-y)

Also, the above comments sound like you believe there is a memory leak in cairo. Could you provide some details? If you want to make me really happy, please provide a self-contained C program showing the leak (which calls cairo_debug_reset_static_data() at the end).

The more correct way is for i3lock to lookup the font, then supply it to Cairo and cache that, to avoid using the toy functions and repeated lookups, since the toy functions don't include reference counting on the fonts they return/don't guarantee longevity.

First member of cairo_toy_font_face_t is a cairo_font_face_t and third member of that is a cairo_reference_count_t ref_count;. Also, cairo-toy-font-face.c has a function _cairo_toy_font_face_hash_table_lock() that returns a pointer to the hash table used for caching toy font instances.

The "don't guarantee longevity" seems to be correct. Toy font faces are destroyed when their last reference is dropped, while freetype font faces have lots of complicated magic to keep alive already-destroyed instances. So, just caching the toy font face might be enough for what you want to do?

PandorasFox commented 6 years ago

Also, do you know about cairo_debug_reset_static_data()? (That clears all of cairo's caches and most likely explodes spectacularly if you still have a reference to anything cairo-y)

I didn't know about this, and it does fix the problem for i3lock, apparently. I feel like just clearing Cairo's cache to avoid ASAN's warnings shouldn't be the solution - i3lock should just use its own, then.

So, just caching the toy font face might be enough for what you want to do?

I tried that and couldn't get it to work, although I may have just been unaware of Cairo's internals to do it the right way.

psychon commented 6 years ago

Cairo also caches freetype font faces. Cairo even caches the allocation for a cairo_t instances. Really, without cairo_debug_reset_static_data() you should get lots of false-positive memory leak reports. No idea why "something from freetype" is special according to ASAN.

Vanclief commented 6 years ago

Is there any update to this?

PandorasFox commented 5 years ago

Issues seem to have resolved themselves - it was an issue with a bad fontconfig update.