Comment 1 for bug 1316201

Revision history for this message
Alek Kowalczyk (alek-kowalczyk) wrote :

I've found the root cause. Is there anyone who could checkin the fix?

The bug is caused by Compiler thread unsafety.

pybars._compiler.Compiler docstring says "The compiler is not threadsafe: you need one per thread".
It is not true - even one instance per thread is not threadsafe, because class-level fields are initialized in module load time
so creating separate instances won't do anyway:

class Compiler:
    """A handlebars template compiler.
    The compiler is not threadsafe: you need one per thread because of the
    state in CodeBuilder.
    """
    _handlebars = OMeta.makeGrammar(handlebars_grammar, {}, 'handlebars')
    _builder = CodeBuilder()
    _compiler = OMeta.makeGrammar(compile_grammar, {'builder': _builder})
    # ...

Here is the fixed version of the above code fragment which works fine in multithreading code:

class Compiler:
    """The really thread-safe handlebars template compiler."""

    _local = threading.local()

    @property
    def _handlebars(self):
        if getattr(self._local, 'handlebars', None) is None:
            self._local.handlebars = OMeta.makeGrammar(handlebars_grammar, {}, 'handlebars')
        return self._local.handlebars

    @property
    def _builder(self):
        if getattr(self._local, 'builder', None) is None:
            self._local.builder = CodeBuilder()
        return self._local.builder

    @property
    def _compiler(self):
        if getattr(self._local, 'compiler', None) is None:
            self._local.compiler = OMeta.makeGrammar(compile_grammar, {'builder': self._builder})
        return self._local.compiler
    # ...