8000 Backport PR #14535: Invalidate FT2Font cache when fork()ing. · matplotlib/matplotlib@b81f833 · GitHub
[go: up one dir, main page]

Skip to content

Commit b81f833

Browse files
tacaswellMeeseeksDev[bot]
authored andcommitted
Backport PR #14535: Invalidate FT2Font cache when fork()ing.
1 parent 010aa96 commit b81f833

File tree

2 files changed

+24
-1
lines changed

2 files changed

+24
-1
lines changed

lib/matplotlib/font_manager.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1321,12 +1321,20 @@ def is_opentype_cff_font(filename):
13211321
return False
13221322

13231323

1324-
_get_font = lru_cache(64)(ft2font.FT2Font)
13251324
_fmcache = os.path.join(
13261325
mpl.get_cachedir(), 'fontlist-v{}.json'.format(FontManager.__version__))
13271326
fontManager = None
13281327

13291328

1329+
_get_font = lru_cache(64)(ft2font.FT2Font)
1330+
# FT2Font objects cannot be used across fork()s because they reference the same
1331+
# FT_Library object. While invalidating *all* existing FT2Fonts after a fork
1332+
# would be too complicated to be worth it, the main way FT2Fonts get reused is
1333+
# via the cache of _get_font, which we can empty upon forking (in Py3.7+).
1334+
if hasattr(os, "register_at_fork"):
1335+
os.register_at_fork(after_in_child=_get_font.cache_clear)
1336+
1337+
13301338
def get_font(filename, hinting_factor=None):
13311339
if hinting_factor is None:
13321340
hinting_factor = rcParams['text.hinting_factor']

lib/matplotlib/tests/test_font_manager.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from io import BytesIO
2+
import multiprocessing
23
import os
34
from pathlib import Path
45
import shutil
@@ -184,3 +185,17 @@ def test_user_fonts_win32():
184185
# Now, the font should be available
185186
fonts = findSystemFonts()
186187
assert any(font_test_file in font for font in fonts)
188+
189+
190+
def _model_handler(_):
191+
fig, ax = plt.subplots()
192+
fig.savefig(BytesIO(), format="pdf")
193+
plt.close()
194+
195+
196+
@pytest.mark.skipif(not hasattr(os, "register_at_fork"),
197+
reason="Cannot register at_fork handlers")
198+
def test_fork():
199+
_model_handler(0) # Make sure the font cache is filled.
200+
ctx = multiprocessing.get_context("fork")
201+
ctx.Pool(processes=2).map(_model_handler, range(2))

0 commit comments

Comments
 (0)
0