Skip to content

use class level rather than instance level caching of fontd #798

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 13, 2012
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
use threading Lock to protect class level font cache
  • Loading branch information
jdh2358 committed Mar 24, 2012
commit a00e510e7f3bcb113a55b47469a68f35b6b7c16b
49 changes: 23 additions & 26 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
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