From b6662c2650bd765b62bc5b2d4e76aff52034be33 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 24 Apr 2022 14:08:46 +0530 Subject: [PATCH] ICU collators must not be used across threads --- src/calibre/utils/icu.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/calibre/utils/icu.py b/src/calibre/utils/icu.py index 5a4ae65bdd..fba0ff42ae 100644 --- a/src/calibre/utils/icu.py +++ b/src/calibre/utils/icu.py @@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en' # Setup code {{{ import codecs import sys -from functools import lru_cache +import threading from calibre.utils.config_base import prefs, tweaks from calibre_extensions import icu as _icu @@ -48,7 +48,9 @@ except: del is_ascii -@lru_cache(maxsize=32) +thread_local_collator_cache = threading.local() + + def collator(strength=None, numeric=None, ignore_alternate_chars=None, upper_first=None): global _locale if _locale is None: @@ -57,7 +59,15 @@ def collator(strength=None, numeric=None, ignore_alternate_chars=None, upper_fir else: from calibre.utils.localization import get_lang _locale = get_lang() - if strength is None and numeric is None and ignore_alternate_chars is None and upper_first is None: + key = strength, numeric, ignore_alternate_chars, upper_first + try: + ans = thread_local_collator_cache.cache.get(key) + except AttributeError: + thread_local_collator_cache.cache = {} + ans = None + if ans is not None: + return ans + if all(x is None for x in key): try: ans = _icu.Collator(_locale) except Exception as e: @@ -77,13 +87,17 @@ def collator(strength=None, numeric=None, ignore_alternate_chars=None, upper_fir except AttributeError: pass # people running from source without latest binary + thread_local_collator_cache.cache[key] = ans return ans def change_locale(locale=None): global _locale _locale = locale - collator.cache_clear() + try: + thread_local_collator_cache.cache.clear() + except AttributeError: + pass def primary_collator():