Skip to content

Commit

Permalink
use threading Lock to protect class level font cache
Browse files Browse the repository at this point in the history
  • Loading branch information
jdh2358 committed Mar 24, 2012
1 parent cc628ce commit a00e510
Showing 1 changed file with 23 additions and 26 deletions.
49 changes: 23 additions & 26 deletions lib/matplotlib/backends/backend_agg.py
Expand Up @@ -21,7 +21,7 @@
* integrate screen dpi w/ ppi and text
"""
from __future__ import division

import threading
import numpy as np

from matplotlib import verbose, rcParams
Expand Down Expand Up @@ -51,19 +51,19 @@ class RendererAgg(RendererBase):
# multiple figures are created we can reuse them. This helps with
# a bug on windows where the creation of too many figures leads to
# too many open file handles. However, storing them at the class
# level is not thread safe. The solution here is to cache the
# fonts at class level, but for a given renderer to slurp them
# down into the instance cache "_fontd_instance" from the
# "_fontd_class" in a call to pre_draw_hook (managed by the
# FigureCanvas) and to restore them to the fontd_class in the
# post_draw_hook.
_fontd_class = maxdict(50)
# level is not thread safe. The solution here is to let the
# FigureCanvas acquire a lock on the fontd at the start of the
# draw, and release it when it is done. This allows multiple
# renderers to share the cached fonts, but only one figure can
# draw at at time and so the font cache is used by only one
# renderer at a time

lock = threading.Lock()
_fontd = maxdict(50)
def __init__(self, width, height, dpi):
if __debug__: verbose.report('RendererAgg.__init__', 'debug-annoying')
RendererBase.__init__(self)
self.texd = maxdict(50) # a cache of tex image rasters
self._fontd_instance = maxdict(50)


self.dpi = dpi
self.width = width
Expand All @@ -82,15 +82,6 @@ def __init__(self, width, height, dpi):
if __debug__: verbose.report('RendererAgg.__init__ done',
'debug-annoying')

def pre_draw_hook(self):
'called by FigureCanvas right before draw; slurp in the class level cache'
self._fontd_instance = RendererAgg._fontd_class
RendererAgg._fontd_class = {}

def post_draw_hook(self):
'called by FigureCanvas right after draw; restore the class level cache'
RendererAgg._fontd_class = self._fontd_instance
self._fontd_instance = {}

def _get_hinting_flag(self):
if rcParams['text.hinting']:
Expand Down Expand Up @@ -238,16 +229,16 @@ def _get_agg_font(self, prop):
'debug-annoying')

key = hash(prop)
font = self._fontd_instance.get(key)
font = RendererAgg._fontd.get(key)

if font is None:
fname = findfont(prop)
font = self._fontd_instance.get(fname)
font = RendererAgg._fontd.get(fname)
if font is None:
font = FT2Font(str(fname))
self._fontd_instance[fname] = font
RendererAgg._fontd[fname] = font

self._fontd_instance[key] = font
RendererAgg._fontd[key] = font

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

self.renderer = self.get_renderer()
self.renderer.pre_draw_hook()
self.figure.draw(self.renderer)
self.renderer.post_draw_hook()
# acquire a lock on the shared font cache
RendererAgg.lock.acquire()

try:
self.figure.draw(self.renderer)
finally:
RendererAgg.lock.release()



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

0 comments on commit a00e510

Please sign in to comment.