These three patches improve the Windows LCID lookup cache.
2025-12-10 Bruno Haible <[email protected]> localename-unsafe: Improve Windows LCID lookup cache. * lib/localename-unsafe.c (get_lcid): Use an LRU cache with 6 elements instead of a cache with only one element. localename-unsafe: Improve Windows LCID lookup cache. Reported by Bryan Green <[email protected]> in <https://savannah.gnu.org/bugs/?67781>. * lib/localename-unsafe.c (get_lcid): Cache also the failed LCID lookups. localename-unsafe: Improve Windows LCID lookup cache. * lib/localename-unsafe.c (get_lcid): Don't overrun the last_locale array.
>From 17d873bddaba8776fca8ceb2d5c180355deffd8e Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Wed, 10 Dec 2025 22:22:39 +0100 Subject: [PATCH 1/3] localename-unsafe: Improve Windows LCID lookup cache. * lib/localename-unsafe.c (get_lcid): Don't overrun the last_locale array. --- ChangeLog | 5 +++++ lib/localename-unsafe.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 52ac60d385..d9e20d56b2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2025-12-10 Bruno Haible <[email protected]> + + localename-unsafe: Improve Windows LCID lookup cache. + * lib/localename-unsafe.c (get_lcid): Don't overrun the last_locale array. + 2025-12-10 Bruno Haible <[email protected]> canonicalize-lgpl: Fix UMR introduced in recent change. diff --git a/lib/localename-unsafe.c b/lib/localename-unsafe.c index 9177b6939d..f70cedafbc 100644 --- a/lib/localename-unsafe.c +++ b/lib/localename-unsafe.c @@ -2586,7 +2586,7 @@ get_lcid (const char *locale_name) { /* A simple cache. */ static LCID last_lcid; - static char last_locale[1000]; + static char last_locale[sizeof (lname)]; /* Lock while looking for an LCID, to protect access to static variables: last_lcid, last_locale, found_lcid, and lname. */ @@ -2603,7 +2603,7 @@ get_lcid (const char *locale_name) if (found_lcid > 0) { last_lcid = found_lcid; - strcpy (last_locale, locale_name); + strcpy (last_locale, lname); } glwthread_mutex_unlock (&get_lcid_lock); return found_lcid; -- 2.52.0
>From fdbdc06db49643d838ba91c5084a4707fcdd87bf Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Wed, 10 Dec 2025 22:23:45 +0100 Subject: [PATCH 2/3] localename-unsafe: Improve Windows LCID lookup cache. Reported by Bryan Green <[email protected]> in <https://savannah.gnu.org/bugs/?67781>. * lib/localename-unsafe.c (get_lcid): Cache also the failed LCID lookups. --- ChangeLog | 6 ++++++ lib/localename-unsafe.c | 27 +++++++++++++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index d9e20d56b2..6e07253e4b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2025-12-10 Bruno Haible <[email protected]> + localename-unsafe: Improve Windows LCID lookup cache. + Reported by Bryan Green <[email protected]> in + <https://savannah.gnu.org/bugs/?67781>. + * lib/localename-unsafe.c (get_lcid): Cache also the failed LCID + lookups. + localename-unsafe: Improve Windows LCID lookup cache. * lib/localename-unsafe.c (get_lcid): Don't overrun the last_locale array. diff --git a/lib/localename-unsafe.c b/lib/localename-unsafe.c index f70cedafbc..009c044663 100644 --- a/lib/localename-unsafe.c +++ b/lib/localename-unsafe.c @@ -2580,33 +2580,36 @@ static glwthread_mutex_t get_lcid_lock = GLWTHREAD_MUTEX_INIT; /* Return the Locale ID (LCID) number given the locale's name, a string, in LOCALE_NAME. This works by enumerating all the locales supported by the system, until we find one whose name matches - LOCALE_NAME. */ + LOCALE_NAME. Return 0 if LOCALE_NAME does not correspond to a locale + supported by the system. */ static LCID get_lcid (const char *locale_name) { /* A simple cache. */ + static unsigned int last_cached /* = 0 */; static LCID last_lcid; static char last_locale[sizeof (lname)]; /* Lock while looking for an LCID, to protect access to static variables: last_lcid, last_locale, found_lcid, and lname. */ glwthread_mutex_lock (&get_lcid_lock); - if (last_lcid > 0 && streq (locale_name, last_locale)) - { - glwthread_mutex_unlock (&get_lcid_lock); - return last_lcid; - } - strncpy (lname, locale_name, sizeof (lname) - 1); - lname[sizeof (lname) - 1] = '\0'; - found_lcid = 0; - EnumSystemLocales (enum_locales_fn, LCID_SUPPORTED); - if (found_lcid > 0) + + if (!(last_cached && streq (locale_name, last_locale))) { + strncpy (lname, locale_name, sizeof (lname) - 1); + lname[sizeof (lname) - 1] = '\0'; + + found_lcid = 0; + EnumSystemLocales (enum_locales_fn, LCID_SUPPORTED); + last_lcid = found_lcid; strcpy (last_locale, lname); + last_cached = 1; } + + LCID result = last_lcid; glwthread_mutex_unlock (&get_lcid_lock); - return found_lcid; + return result; } # endif -- 2.52.0
>From e26598f163ed5e9ddfa41885fa479b5bc9f0a327 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Thu, 11 Dec 2025 00:55:13 +0100 Subject: [PATCH 3/3] localename-unsafe: Improve Windows LCID lookup cache. * lib/localename-unsafe.c (get_lcid): Use an LRU cache with 6 elements instead of a cache with only one element. --- ChangeLog | 4 ++ lib/localename-unsafe.c | 101 ++++++++++++++++++++++++++++++++++------ 2 files changed, 92 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6e07253e4b..6cf490da82 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2025-12-10 Bruno Haible <[email protected]> + localename-unsafe: Improve Windows LCID lookup cache. + * lib/localename-unsafe.c (get_lcid): Use an LRU cache with 6 elements + instead of a cache with only one element. + localename-unsafe: Improve Windows LCID lookup cache. Reported by Bryan Green <[email protected]> in <https://savannah.gnu.org/bugs/?67781>. diff --git a/lib/localename-unsafe.c b/lib/localename-unsafe.c index 009c044663..6af08beb19 100644 --- a/lib/localename-unsafe.c +++ b/lib/localename-unsafe.c @@ -2585,29 +2585,104 @@ static glwthread_mutex_t get_lcid_lock = GLWTHREAD_MUTEX_INIT; static LCID get_lcid (const char *locale_name) { - /* A simple cache. */ - static unsigned int last_cached /* = 0 */; - static LCID last_lcid; - static char last_locale[sizeof (lname)]; - - /* Lock while looking for an LCID, to protect access to static - variables: last_lcid, last_locale, found_lcid, and lname. */ + /* A least-recently-used cache with at most N = 6 entries. + (Because there are 6 locale categories.) */ + + /* Number of bits for a index into the cache. */ + enum { nbits = 3 }; + /* Maximum number of entries in the cache. */ + enum { N = 6 }; /* <= (1 << nbits) */ + /* An entry in the cache. */ + typedef struct { LCID e_lcid; char e_locale[sizeof (lname)]; } entry_t; + /* An unsigned integer type with at least N * nbits bits. + Used as an array: + element [0] = bits nbits-1 .. 0, + element [1] = bits 2*nbits-1 .. nbits, + element [2] = bits 3*nbits-1 .. 2*nbits, + and so on. */ + typedef unsigned int indices_t; + + /* Number of entries in the cache. */ + static size_t n; /* <= N */ + /* The entire cache. Only elements 0..n-1 are in use. */ + static entry_t lru[N]; + /* Indices of used cache entries. Only elements 0..n-1 are in use. */ + static indices_t indices; + + /* Lock while looking for an LCID, to protect access to static variables: + found_lcid, lname, and the cache. */ glwthread_mutex_lock (&get_lcid_lock); - if (!(last_cached && streq (locale_name, last_locale))) + /* Look up locale_name in the cache. */ + size_t found = (size_t)(-1); + size_t i; + for (i = 0; i < n; i++) + { + size_t j = /* indices[i] */ + (indices >> (nbits * i)) & ((1U << nbits) - 1U); + if (streq (locale_name, lru[j].e_locale)) + { + found = j; + break; + } + } + LCID result; + if (i < n) { + /* We have a cache hit. 0 <= found < n. */ + result = lru[found].e_lcid; + if (i > 0) + { + /* Perform these assignments in parallel: + indices[0] := indices[i] + indices[1] := indices[0] + ... + indices[i] := indices[i-1] */ + indices = (indices & (-1U << (nbits * (i + 1)))) + | ((indices & ((1U << (nbits * i)) - 1U)) << nbits) + | found; + } + } + else + { + /* We have a cache miss. */ strncpy (lname, locale_name, sizeof (lname) - 1); lname[sizeof (lname) - 1] = '\0'; found_lcid = 0; EnumSystemLocales (enum_locales_fn, LCID_SUPPORTED); - last_lcid = found_lcid; - strcpy (last_locale, lname); - last_cached = 1; - } + size_t j; + if (n < N) + { + /* There is still room in the cache. */ + j = n; + /* Perform these assignments in parallel: + indices[0] := n + indices[1] := indices[0] + ... + indices[n] := indices[n-1] */ + indices = (indices << nbits) | n; + n++; + } + else /* n == N */ + { + /* The cache is full. Drop the least recently used entry and + reuse it. */ + j = /* indices[N-1] */ + (indices >> (nbits * (N - 1))) & ((1U << nbits) - 1U); + /* Perform these assignments in parallel: + indices[0] := j + indices[1] := indices[0] + ... + indices[N-1] := indices[N-2] */ + indices = ((indices & ((1U << (nbits * (N - 1))) - 1U)) << nbits) | j; + } + strcpy (lru[j].e_locale, lname); + lru[j].e_lcid = found_lcid; - LCID result = last_lcid; + result = found_lcid; + } glwthread_mutex_unlock (&get_lcid_lock); return result; } -- 2.52.0
