setlocale: create LC_ALL string when changing locale

This patch is for the sake of gnulib.

gnulib implements some form of a thread-safe setlocale variant
called setlocale_null_r, which is supposed to return the locale
strings in a thread-safe manner.  This only succeeds if the system's
setlocale already handles this thread-safe, otherwise gnulib adds
some locking on its own.

Newlib's setlocale always writes the global string array holding the
LC_ALL value anew on each invocation of setlocale(LC_ALL, NULL).
Since that doesn't allow to call setlocale(LC_ALL, NULL) in a
thread-safe manner, so locking in gnulib is required.

And here's the problem...

The lock is decorated as dllexport when building for Cygwin.  This
collides with the default behaviour of ld to export all symbols.
If it finds one decorated symbol, it will only export this symbol
to the DLL import lib.

Change setlocale so that it writes the global string array
holding the LC_ALL value at the time the locale gets changed.
On setlocale(LC_ALL, NULL), just return the pointer to the
global LC_ALL string array, just as in GLibc.  The burden of
doing so is negligibly for all targets, but adds thread-safety
for gnulib's setlocal_null_r() function, and gnulib can drop
the lock entirely when building for Cygwin.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2023-02-06 11:27:44 +01:00
parent c6e601de84
commit 23e49b18ce
1 changed files with 10 additions and 4 deletions

View File

@ -289,7 +289,8 @@ struct __locale_t __global_locale =
/* Renamed from current_locale_string to make clear this is only the /* Renamed from current_locale_string to make clear this is only the
*global* string for setlocale (LC_ALL, NULL). There's no equivalent *global* string for setlocale (LC_ALL, NULL). There's no equivalent
functionality for uselocale. */ functionality for uselocale. */
static char global_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)]; static char global_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)]
= "C";
static char *currentlocale (void); static char *currentlocale (void);
#endif /* _MB_CAPABLE */ #endif /* _MB_CAPABLE */
@ -312,6 +313,7 @@ _setlocale_r (struct _reent *p,
static char saved_categories[_LC_LAST][ENCODING_LEN + 1]; static char saved_categories[_LC_LAST][ENCODING_LEN + 1];
int i, j, len, saverr; int i, j, len, saverr;
const char *env, *r; const char *env, *r;
char *ret;
if (category < LC_ALL || category >= _LC_LAST) if (category < LC_ALL || category >= _LC_LAST)
{ {
@ -321,7 +323,7 @@ _setlocale_r (struct _reent *p,
if (locale == NULL) if (locale == NULL)
return category != LC_ALL ? __get_global_locale ()->categories[category] return category != LC_ALL ? __get_global_locale ()->categories[category]
: currentlocale(); : global_locale_string;
/* /*
* Default to the current locale for everything. * Default to the current locale for everything.
@ -415,8 +417,12 @@ _setlocale_r (struct _reent *p,
} }
if (category != LC_ALL) if (category != LC_ALL)
return __loadlocale (__get_global_locale (), category, {
ret = __loadlocale (__get_global_locale (), category,
new_categories[category]); new_categories[category]);
currentlocale ();
return ret;
}
for (i = 1; i < _LC_LAST; ++i) for (i = 1; i < _LC_LAST; ++i)
{ {