mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-11 15:06:14 +01:00
gdatetime: Fix incorrect alt-digits being used after changing locale
The alt-digits are loaded from `nl_langinfo()` in a `GOnce` section, which means `nl_langinfo()` is not re-queried after the process changes locale (if that happens). So, change the `GOnce` to a mutex and store the locale of the alt-digits alongside them. This will introduce contention when calling `format_number()` is called, but how often are multiple threads trying to format dates at the same time? If this does get highlighted as a performance problem, the other approach I considered was a `GPrivate` struct containing all the locale-specific cached data. That comes at the cost of using a `GPrivate` slot (although that’s only particularly expensive on Windows, and the locale code is quite different for Windows, so perhaps that could be avoided entirely). It does mean that all locale printing could be lock-free and still safely update cached data on a locale change. Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
This commit is contained in:
parent
592be87b7a
commit
afd8dde13f
@ -28,6 +28,7 @@ RUN dnf -y update \
|
||||
glibc-langpack-es \
|
||||
glibc-langpack-fa \
|
||||
glibc-langpack-fr \
|
||||
glibc-langpack-gu \
|
||||
glibc-langpack-hr \
|
||||
glibc-langpack-ja \
|
||||
glibc-langpack-lt \
|
||||
|
@ -54,6 +54,7 @@
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <locale.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -2820,6 +2821,9 @@ format_z (GString *outstr,
|
||||
/* Initializes the array with UTF-8 encoded alternate digits suitable for use
|
||||
* in current locale. Returns NULL when current locale does not use alternate
|
||||
* digits or there was an error converting them to UTF-8.
|
||||
*
|
||||
* This needs external locking, so must only be called from within
|
||||
* format_number().
|
||||
*/
|
||||
static const gchar * const *
|
||||
initialize_alt_digits (void)
|
||||
@ -2874,6 +2878,9 @@ format_number (GString *str,
|
||||
const gchar * const *digits = ascii_digits;
|
||||
const gchar *tmp[10];
|
||||
gint i = 0;
|
||||
#ifdef HAVE_LANGINFO_OUTDIGIT
|
||||
static GMutex alt_digits_mutex;
|
||||
#endif
|
||||
|
||||
g_return_if_fail (width <= 10);
|
||||
|
||||
@ -2881,16 +2888,21 @@ format_number (GString *str,
|
||||
if (use_alt_digits)
|
||||
{
|
||||
static const gchar * const *alt_digits = NULL;
|
||||
static gsize initialised;
|
||||
static char *alt_digits_locale = NULL;
|
||||
const char *current_ctype_locale = setlocale (LC_CTYPE, NULL);
|
||||
|
||||
if G_UNLIKELY (g_once_init_enter (&initialised))
|
||||
/* Lock so we can initialise (or re-initialise, if the locale has changed)
|
||||
* and hold access to the digits buffer until done formatting. */
|
||||
g_mutex_lock (&alt_digits_mutex);
|
||||
|
||||
if (g_strcmp0 (alt_digits_locale, current_ctype_locale) != 0)
|
||||
{
|
||||
alt_digits = initialize_alt_digits ();
|
||||
|
||||
if (alt_digits == NULL)
|
||||
alt_digits = ascii_digits;
|
||||
|
||||
g_once_init_leave (&initialised, TRUE);
|
||||
alt_digits_locale = g_strdup (current_ctype_locale);
|
||||
}
|
||||
|
||||
digits = alt_digits;
|
||||
@ -2907,6 +2919,11 @@ format_number (GString *str,
|
||||
while (pad && i < width)
|
||||
tmp[i++] = *pad == '0' ? digits[0] : pad;
|
||||
|
||||
#ifdef HAVE_LANGINFO_OUTDIGIT
|
||||
if (use_alt_digits)
|
||||
g_mutex_unlock (&alt_digits_mutex);
|
||||
#endif
|
||||
|
||||
/* should really be impossible */
|
||||
g_assert (i <= 10);
|
||||
|
||||
|
@ -1834,6 +1834,7 @@ test_modifiers (void)
|
||||
TEST_PRINTF_TIME (1, 0, 0, "%#P", "am");
|
||||
|
||||
oldlocale = g_strdup (setlocale (LC_ALL, NULL));
|
||||
|
||||
setlocale (LC_ALL, "fa_IR.utf-8");
|
||||
#ifdef HAVE_LANGINFO_OUTDIGIT
|
||||
if (strstr (setlocale (LC_ALL, NULL), "fa_IR") != NULL)
|
||||
@ -1852,6 +1853,26 @@ test_modifiers (void)
|
||||
#else
|
||||
g_test_skip ("langinfo not available, skipping O modifier tests");
|
||||
#endif
|
||||
|
||||
setlocale (LC_ALL, "gu_IN.utf-8");
|
||||
#ifdef HAVE_LANGINFO_OUTDIGIT
|
||||
if (strstr (setlocale (LC_ALL, NULL), "gu_IN") != NULL)
|
||||
{
|
||||
TEST_PRINTF_TIME (23, 0, 0, "%OH", "૨૩"); /* '23' */
|
||||
TEST_PRINTF_TIME (23, 0, 0, "%OI", "૧૧"); /* '11' */
|
||||
TEST_PRINTF_TIME (23, 0, 0, "%OM", "૦૦"); /* '00' */
|
||||
|
||||
TEST_PRINTF_DATE (2011, 7, 1, "%Om", "૦૭"); /* '07' */
|
||||
TEST_PRINTF_DATE (2011, 7, 1, "%0Om", "૦૭"); /* '07' */
|
||||
TEST_PRINTF_DATE (2011, 7, 1, "%-Om", "૭"); /* '7' */
|
||||
TEST_PRINTF_DATE (2011, 7, 1, "%_Om", " ૭"); /* ' 7' */
|
||||
}
|
||||
else
|
||||
g_test_skip ("locale gu_IN not available, skipping O modifier tests");
|
||||
#else
|
||||
g_test_skip ("langinfo not available, skipping O modifier tests");
|
||||
#endif
|
||||
|
||||
setlocale (LC_ALL, oldlocale);
|
||||
g_free (oldlocale);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user