Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f1eeeab

Browse files
committed
Invalidate FT2Font cache when fork()ing.
1 parent ef3a17a commit f1eeeab

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

lib/matplotlib/font_manager.py

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

12661266

1267-
_get_font = lru_cache(64)(ft2font.FT2Font)
12681267
_fmcache = os.path.join(
12691268
mpl.get_cachedir(), 'fontlist-v{}.json'.format(FontManager.__version__))
12701269
fontManager = None
12711270

12721271

1272+
_get_font = lru_cache(64)(ft2font.FT2Font)
1273+
# FT2Font objects cannot be used across fork()s because they reference the same
1274+
# FT_Library object. While invalidating *all* existing FT2Fonts after a fork
1275+
# would be too complicated to be worth it, the main way FT2Fonts get reused is
1276+
# via the cache of _get_font, which we can empty upon forking (in Py3.7+).
1277+
if hasattr(os, "register_at_fork"):
1278+
os.register_at_fork(after_in_child=_get_font.cache_clear)
1279+
1280+
12731281
def get_font(filename, hinting_factor=None):
12741282
if hinting_factor is None:
12751283
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)