[PATCH stable] fileset, revset: do not use global parser object for thread safety
Yuya Nishihara
yuya at tcha.org
Fri Dec 20 22:15:00 CST 2013
Hi,
This problem was originally reported as a bug of TortoiseHg, but hgweb would
have the same issue.
https://bitbucket.org/tortoisehg/thg/issue/3533/
script to reproduce the exception:
import threading
from mercurial import revset
spec = '()'
for i in xrange(10):
spec = '(%s or %d)' % (spec, i)
print spec
for _i in xrange(50):
th = threading.Thread(target=lambda: revset.parse(spec))
th.start()
# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1387597459 -32400
# Sat Dec 21 12:44:19 2013 +0900
# Branch stable
# Node ID 61a47fd64f308ecf18696f06f84918bef9564d7c
# Parent d4be314b20711b6ecfa7a6f8b46970231134004a
fileset, revset: do not use global parser object for thread safety
parse() cannot be called at the same time because a parser object keeps its
states. This is no problem for command-line hg client, but it would cause
strange errors in multi-threaded hgweb.
Creating parser object is not too expensive.
original:
% python -m timeit -s 'from mercurial import revset' 'revset.parse("0::tip")'
100000 loops, best of 3: 11.3 usec per loop
thread-safe:
% python -m timeit -s 'from mercurial import revset' 'revset.parse("0::tip")'
100000 loops, best of 3: 13.1 usec per loop
diff --git a/mercurial/fileset.py b/mercurial/fileset.py
--- a/mercurial/fileset.py
+++ b/mercurial/fileset.py
@@ -78,7 +78,9 @@ def tokenize(program):
pos += 1
yield ('end', None, pos)
-parse = parser.parser(tokenize, elements).parse
+def parse(expr):
+ p = parser.parser(tokenize, elements)
+ return p.parse(expr)
def getstring(x, err):
if x and (x[0] == 'string' or x[0] == 'symbol'):
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -1880,7 +1880,9 @@ def findaliases(ui, tree):
aliases[alias.name] = alias
return _expandaliases(aliases, tree, [], {})
-parse = parser.parser(tokenize, elements).parse
+def parse(spec):
+ p = parser.parser(tokenize, elements)
+ return p.parse(spec)
def match(ui, spec):
if not spec:
More information about the Mercurial-devel
mailing list