Skip to content

Commit c501751

Browse files
committed
Fix race condition in cache eviction
1 parent 1c0f538 commit c501751

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

jmespath/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ def _raise_parse_error_maybe_eof(self, expected_type, token):
490490

491491
def _free_cache_entries(self):
492492
for key in random.sample(self._CACHE.keys(), int(self._MAX_SIZE / 2)):
493-
del self._CACHE[key]
493+
self._CACHE.pop(key, None)
494494

495495
@classmethod
496496
def purge(cls):

tests/test_parser.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python
2-
2+
from concurrent.futures.thread import ThreadPoolExecutor
33
import re
44
from tests import unittest, OrderedDict
55

@@ -322,6 +322,31 @@ def test_cache_purge(self):
322322
self.assertEqual(first.parsed,
323323
cached.parsed)
324324

325+
def test_free_cache_entries(self):
326+
"""
327+
Test for a race that occurs during cache eviction. The parser cache is
328+
shared by all threads in a process so if two threads randomly select to
329+
evict the same entry, one of them would raise a KeyError if they used
330+
`del` to evict it.
331+
"""
332+
num_workers = 32
333+
num_repetitions = 4096
334+
335+
def worker():
336+
for _ in range(num_repetitions):
337+
p = parser.Parser()
338+
p.parse('foo')
339+
p.parse('bar')
340+
p.parse('fubaz')
341+
342+
parser.Parser._MAX_SIZE, original_size = 2, parser.Parser._MAX_SIZE,
343+
try:
344+
with ThreadPoolExecutor(max_workers=num_workers) as tpe:
345+
futures = [tpe.submit(worker) for _ in range(num_workers)]
346+
self.assertTrue(all(f.result() is None for f in futures))
347+
finally:
348+
parser.Parser._MAX_SIZE = original_size
349+
325350

326351
class TestParserAddsExpressionAttribute(unittest.TestCase):
327352
def test_expression_available_from_parser(self):

0 commit comments

Comments
 (0)