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

Skip to content

Commit a00e510

Browse files
committed
use threading Lock to protect class level font cache
1 parent cc628ce commit a00e510

File tree

1 file changed

+23
-26
lines changed

1 file changed

+23
-26
lines changed

lib/matplotlib/backends/backend_agg.py

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* integrate screen dpi w/ ppi and text
2222
"""
2323
from __future__ import division
24-
24+
import threading
2525
import numpy as np
2626

2727
from matplotlib import verbose, rcParams
@@ -51,19 +51,19 @@ class RendererAgg(RendererBase):
5151
# multiple figures are created we can reuse them. This helps with
5252
# a bug on windows where the creation of too many figures leads to
5353
# too many open file handles. However, storing them at the class
54-
# level is not thread safe. The solution here is to cache the
55-
# fonts at class level, but for a given renderer to slurp them
56-
# down into the instance cache "_fontd_instance" from the
57-
# "_fontd_class" in a call to pre_draw_hook (managed by the
58-
# FigureCanvas) and to restore them to the fontd_class in the
59-
# post_draw_hook.
60-
_fontd_class = maxdict(50)
54+
# level is not thread safe. The solution here is to let the
55+
# FigureCanvas acquire a lock on the fontd at the start of the
56+
# draw, and release it when it is done. This allows multiple
57+
# renderers to share the cached fonts, but only one figure can
58+
# draw at at time and so the font cache is used by only one
59+
# renderer at a time
60+
61+
lock = threading.Lock()
62+
_fontd = maxdict(50)
6163
def __init__(self, width, height, dpi):
6264
if __debug__: verbose.report('RendererAgg.__init__', 'debug-annoying')
6365
RendererBase.__init__(self)
6466
self.texd = maxdict(50) # a cache of tex image rasters
65-
self._fontd_instance = maxdict(50)
66-
6767

6868
self.dpi = dpi
6969
self.width = width
@@ -82,15 +82,6 @@ def __init__(self, width, height, dpi):
8282
if __debug__: verbose.report('RendererAgg.__init__ done',
8383
'debug-annoying')
8484

85-
def pre_draw_hook(self):
86-
'called by FigureCanvas right before draw; slurp in the class level cache'
87-
self._fontd_instance = RendererAgg._fontd_class
88-
RendererAgg._fontd_class = {}
89-
90-
def post_draw_hook(self):
91-
'called by FigureCanvas right after draw; restore the class level cache'
92-
RendererAgg._fontd_class = self._fontd_instance
93-
self._fontd_instance = {}
9485

9586
def _get_hinting_flag(self):
9687
if rcParams['text.hinting']:
@@ -238,16 +229,16 @@ def _get_agg_font(self, prop):
238229
'debug-annoying')
239230

240231
key = hash(prop)
241-
font = self._fontd_instance.get(key)
232+
font = RendererAgg._fontd.get(key)
242233

243234
if font is None:
244235
fname = findfont(prop)
245-
font = self._fontd_instance.get(fname)
236+
font = RendererAgg._fontd.get(fname)
246237
if font is None:
247238
font = FT2Font(str(fname))
248-
self._fontd_instance[fname] = font
239+
RendererAgg._fontd[fname] = font
249240

250-
self._fontd_instance[key] = font
241+
RendererAgg._fontd[key] = font
251242

252243
font.clear()
253244
size = prop.get_size_in_points()
@@ -423,9 +414,15 @@ def draw(self):
423414
if __debug__: verbose.report('FigureCanvasAgg.draw', 'debug-annoying')
424415

425416
self.renderer = self.get_renderer()
426-
self.renderer.pre_draw_hook()
427-
self.figure.draw(self.renderer)
428-
self.renderer.post_draw_hook()
417+
# acquire a lock on the shared font cache
418+
RendererAgg.lock.acquire()
419+
420+
try:
421+
self.figure.draw(self.renderer)
422+
finally:
423+
RendererAgg.lock.release()
424+
425+
429426

430427
def get_renderer(self):
431428
l, b, w, h = self.figure.bbox.bounds

0 commit comments

Comments
 (0)