These patches add a module 'setlocale_null-unlocked', that allows code to do the equivalent of setlocale(_,NULL) — with platform-specific workarounds — when we know that it does not need locking.
2024-02-15 Bruno Haible <br...@clisp.org> setlocale_null-unlocked: Add tests. * tests/test-setlocale_null-unlocked.c: New file, based on tests/test-setlocale_null.c. * modules/setlocale-null-unlocked-tests: New file. setlocale_null-unlocked: New module. * lib/setlocale_null.h (setlocale_null_r_unlocked, setlocale_null_unlocked): New declarations. * lib/setlocale_null-unlocked.c: New file, based on lib/setlocale_null.c. * lib/setlocale_null.c: Don't include <wchar.h>. (setlocale_null_unlocked, setlocale_null_r_unlocked): Remove functions. * modules/setlocale-null-unlocked: New file. * modules/setlocale-null (Depends-on): Add setlocale-null-unlocked. 2024-02-15 Bruno Haible <br...@clisp.org> setlocale-null: Refactor. * lib/setlocale_null.c (setlocale_null_r_with_lock): Renamed from setlocale_null_with_lock. (setlocale_null_r_unlocked): Renamed from setlocale_null_unlocked. (setlocale_null_unlocked): Renamed from setlocale_null_androidfix.
>From c75f99155342338ae9de19b164fd3e255bf96da4 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Thu, 15 Feb 2024 09:47:08 +0100 Subject: [PATCH 1/7] setlocale-null: Refactor. * lib/setlocale_null.c (setlocale_null_r_with_lock): Renamed from setlocale_null_with_lock. (setlocale_null_r_unlocked): Renamed from setlocale_null_unlocked. (setlocale_null_unlocked): Renamed from setlocale_null_androidfix. --- ChangeLog | 8 ++++++++ lib/setlocale_null.c | 42 +++++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index 05732c7e9d..4ba5cbfc28 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2024-02-15 Bruno Haible <br...@clisp.org> + + setlocale-null: Refactor. + * lib/setlocale_null.c + (setlocale_null_r_with_lock): Renamed from setlocale_null_with_lock. + (setlocale_null_r_unlocked): Renamed from setlocale_null_unlocked. + (setlocale_null_unlocked): Renamed from setlocale_null_androidfix. + 2024-02-15 Bruno Haible <br...@clisp.org> localename-unsafe: New module. diff --git a/lib/setlocale_null.c b/lib/setlocale_null.c index 697502fbfe..152452e04f 100644 --- a/lib/setlocale_null.c +++ b/lib/setlocale_null.c @@ -63,7 +63,7 @@ #undef setlocale static const char * -setlocale_null_androidfix (int category) +setlocale_null_unlocked (int category) { const char *result = setlocale (category, NULL); @@ -94,7 +94,7 @@ setlocale_null_androidfix (int category) } static int -setlocale_null_unlocked (int category, char *buf, size_t bufsize) +setlocale_null_r_unlocked (int category, char *buf, size_t bufsize) { #if defined _WIN32 && !defined __CYGWIN__ && defined _MSC_VER /* On native Windows, nowadays, the setlocale() implementation is based @@ -143,7 +143,7 @@ setlocale_null_unlocked (int category, char *buf, size_t bufsize) } } #else - const char *result = setlocale_null_androidfix (category); + const char *result = setlocale_null_unlocked (category); if (result == NULL) { @@ -181,7 +181,7 @@ setlocale_null_unlocked (int category, char *buf, size_t bufsize) #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */ -/* Use a lock, so that no two threads can invoke setlocale_null_unlocked +/* Use a lock, so that no two threads can invoke setlocale_null_r_unlocked at the same time. */ /* Prohibit renaming this symbol. */ @@ -190,20 +190,20 @@ setlocale_null_unlocked (int category, char *buf, size_t bufsize) # if AVOID_ANY_THREADS /* The option '--disable-threads' explicitly requests no locking. */ -# define setlocale_null_with_lock setlocale_null_unlocked +# define setlocale_null_r_with_lock setlocale_null_r_unlocked # elif defined _WIN32 && !defined __CYGWIN__ extern __declspec(dllimport) CRITICAL_SECTION *gl_get_setlocale_null_lock (void); static int -setlocale_null_with_lock (int category, char *buf, size_t bufsize) +setlocale_null_r_with_lock (int category, char *buf, size_t bufsize) { CRITICAL_SECTION *lock = gl_get_setlocale_null_lock (); int ret; EnterCriticalSection (lock); - ret = setlocale_null_unlocked (category, buf, bufsize); + ret = setlocale_null_r_unlocked (category, buf, bufsize); LeaveCriticalSection (lock); return ret; @@ -234,7 +234,7 @@ extern # endif static int -setlocale_null_with_lock (int category, char *buf, size_t bufsize) +setlocale_null_r_with_lock (int category, char *buf, size_t bufsize) { if (pthread_in_use()) { @@ -243,14 +243,14 @@ setlocale_null_with_lock (int category, char *buf, size_t bufsize) if (pthread_mutex_lock (lock)) abort (); - ret = setlocale_null_unlocked (category, buf, bufsize); + ret = setlocale_null_r_unlocked (category, buf, bufsize); if (pthread_mutex_unlock (lock)) abort (); return ret; } else - return setlocale_null_unlocked (category, buf, bufsize); + return setlocale_null_r_unlocked (category, buf, bufsize); } # elif HAVE_THREADS_H @@ -258,14 +258,14 @@ setlocale_null_with_lock (int category, char *buf, size_t bufsize) extern mtx_t *gl_get_setlocale_null_lock (void); static int -setlocale_null_with_lock (int category, char *buf, size_t bufsize) +setlocale_null_r_with_lock (int category, char *buf, size_t bufsize) { mtx_t *lock = gl_get_setlocale_null_lock (); int ret; if (mtx_lock (lock) != thrd_success) abort (); - ret = setlocale_null_unlocked (category, buf, bufsize); + ret = setlocale_null_r_unlocked (category, buf, bufsize); if (mtx_unlock (lock) != thrd_success) abort (); @@ -282,27 +282,27 @@ setlocale_null_r (int category, char *buf, size_t bufsize) #if SETLOCALE_NULL_ALL_MTSAFE # if SETLOCALE_NULL_ONE_MTSAFE - return setlocale_null_unlocked (category, buf, bufsize); + return setlocale_null_r_unlocked (category, buf, bufsize); # else if (category == LC_ALL) - return setlocale_null_unlocked (category, buf, bufsize); + return setlocale_null_r_unlocked (category, buf, bufsize); else - return setlocale_null_with_lock (category, buf, bufsize); + return setlocale_null_r_with_lock (category, buf, bufsize); # endif #else # if SETLOCALE_NULL_ONE_MTSAFE if (category == LC_ALL) - return setlocale_null_with_lock (category, buf, bufsize); + return setlocale_null_r_with_lock (category, buf, bufsize); else - return setlocale_null_unlocked (category, buf, bufsize); + return setlocale_null_r_unlocked (category, buf, bufsize); # else - return setlocale_null_with_lock (category, buf, bufsize); + return setlocale_null_r_with_lock (category, buf, bufsize); # endif #endif @@ -312,7 +312,7 @@ const char * setlocale_null (int category) { #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE - return setlocale_null_androidfix (category); + return setlocale_null_unlocked (category); #else /* This call must be multithread-safe. To achieve this without using @@ -328,7 +328,7 @@ setlocale_null (int category) if (category == LC_ALL) { # if SETLOCALE_NULL_ALL_MTSAFE - return setlocale_null_androidfix (LC_ALL); + return setlocale_null_unlocked (LC_ALL); # else char buf[SETLOCALE_NULL_ALL_MAX]; static char resultbuf[SETLOCALE_NULL_ALL_MAX]; @@ -342,7 +342,7 @@ setlocale_null (int category) else { # if SETLOCALE_NULL_ONE_MTSAFE - return setlocale_null_androidfix (category); + return setlocale_null_unlocked (category); # else enum { -- 2.34.1
>From 2edefe4fef4034fa409041a568fa92539b4af04f Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Thu, 15 Feb 2024 10:30:54 +0100 Subject: [PATCH 2/7] setlocale_null-unlocked: New module. * lib/setlocale_null.h (setlocale_null_r_unlocked, setlocale_null_unlocked): New declarations. * lib/setlocale_null-unlocked.c: New file, based on lib/setlocale_null.c. * lib/setlocale_null.c: Don't include <wchar.h>. (setlocale_null_unlocked, setlocale_null_r_unlocked): Remove functions. * modules/setlocale-null-unlocked: New file. * modules/setlocale-null (Depends-on): Add setlocale-null-unlocked. --- ChangeLog | 12 +++ lib/setlocale_null-unlocked.c | 149 ++++++++++++++++++++++++++++++++ lib/setlocale_null.c | 123 -------------------------- lib/setlocale_null.h | 28 ++++++ modules/setlocale-null | 1 + modules/setlocale-null-unlocked | 27 ++++++ 6 files changed, 217 insertions(+), 123 deletions(-) create mode 100644 lib/setlocale_null-unlocked.c create mode 100644 modules/setlocale-null-unlocked diff --git a/ChangeLog b/ChangeLog index 4ba5cbfc28..b2668b99bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2024-02-15 Bruno Haible <br...@clisp.org> + + setlocale_null-unlocked: New module. + * lib/setlocale_null.h (setlocale_null_r_unlocked, + setlocale_null_unlocked): New declarations. + * lib/setlocale_null-unlocked.c: New file, based on + lib/setlocale_null.c. + * lib/setlocale_null.c: Don't include <wchar.h>. + (setlocale_null_unlocked, setlocale_null_r_unlocked): Remove functions. + * modules/setlocale-null-unlocked: New file. + * modules/setlocale-null (Depends-on): Add setlocale-null-unlocked. + 2024-02-15 Bruno Haible <br...@clisp.org> setlocale-null: Refactor. diff --git a/lib/setlocale_null-unlocked.c b/lib/setlocale_null-unlocked.c new file mode 100644 index 0000000000..0a86f0df78 --- /dev/null +++ b/lib/setlocale_null-unlocked.c @@ -0,0 +1,149 @@ +/* Query the name of the current global locale, without locking. + Copyright (C) 2019-2024 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2019. */ + +#include <config.h> + +/* Specification. */ +#include "setlocale_null.h" + +#include <errno.h> +#include <locale.h> +#include <string.h> +#if defined _WIN32 && !defined __CYGWIN__ +# include <wchar.h> +#endif + +/* Use the system's setlocale() function, not the gnulib override, here. */ +#undef setlocale + +const char * +setlocale_null_unlocked (int category) +{ + const char *result = setlocale (category, NULL); + +#ifdef __ANDROID__ + if (result == NULL) + switch (category) + { + case LC_CTYPE: + case LC_NUMERIC: + case LC_TIME: + case LC_COLLATE: + case LC_MONETARY: + case LC_MESSAGES: + case LC_ALL: + case LC_PAPER: + case LC_NAME: + case LC_ADDRESS: + case LC_TELEPHONE: + case LC_MEASUREMENT: + result = "C"; + break; + default: + break; + } +#endif + + return result; +} + +int +setlocale_null_r_unlocked (int category, char *buf, size_t bufsize) +{ +#if defined _WIN32 && !defined __CYGWIN__ && defined _MSC_VER + /* On native Windows, nowadays, the setlocale() implementation is based + on _wsetlocale() and uses malloc() for the result. We are better off + using _wsetlocale() directly. */ + const wchar_t *result = _wsetlocale (category, NULL); + + if (result == NULL) + { + /* CATEGORY is invalid. */ + if (bufsize > 0) + /* Return an empty string in BUF. + This is a convenience for callers that don't want to write explicit + code for handling EINVAL. */ + buf[0] = '\0'; + return EINVAL; + } + else + { + size_t length = wcslen (result); + if (length < bufsize) + { + size_t i; + + /* Convert wchar_t[] -> char[], assuming plain ASCII. */ + for (i = 0; i <= length; i++) + buf[i] = result[i]; + + return 0; + } + else + { + if (bufsize > 0) + { + /* Return a truncated result in BUF. + This is a convenience for callers that don't want to write + explicit code for handling ERANGE. */ + size_t i; + + /* Convert wchar_t[] -> char[], assuming plain ASCII. */ + for (i = 0; i < bufsize; i++) + buf[i] = result[i]; + buf[bufsize - 1] = '\0'; + } + return ERANGE; + } + } +#else + const char *result = setlocale_null_unlocked (category); + + if (result == NULL) + { + /* CATEGORY is invalid. */ + if (bufsize > 0) + /* Return an empty string in BUF. + This is a convenience for callers that don't want to write explicit + code for handling EINVAL. */ + buf[0] = '\0'; + return EINVAL; + } + else + { + size_t length = strlen (result); + if (length < bufsize) + { + memcpy (buf, result, length + 1); + return 0; + } + else + { + if (bufsize > 0) + { + /* Return a truncated result in BUF. + This is a convenience for callers that don't want to write + explicit code for handling ERANGE. */ + memcpy (buf, result, bufsize - 1); + buf[bufsize - 1] = '\0'; + } + return ERANGE; + } + } +#endif +} diff --git a/lib/setlocale_null.c b/lib/setlocale_null.c index 152452e04f..5ecf413de0 100644 --- a/lib/setlocale_null.c +++ b/lib/setlocale_null.c @@ -25,9 +25,6 @@ #include <locale.h> #include <stdlib.h> #include <string.h> -#if defined _WIN32 && !defined __CYGWIN__ -# include <wchar.h> -#endif #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) @@ -59,126 +56,6 @@ #endif -/* Use the system's setlocale() function, not the gnulib override, here. */ -#undef setlocale - -static const char * -setlocale_null_unlocked (int category) -{ - const char *result = setlocale (category, NULL); - -#ifdef __ANDROID__ - if (result == NULL) - switch (category) - { - case LC_CTYPE: - case LC_NUMERIC: - case LC_TIME: - case LC_COLLATE: - case LC_MONETARY: - case LC_MESSAGES: - case LC_ALL: - case LC_PAPER: - case LC_NAME: - case LC_ADDRESS: - case LC_TELEPHONE: - case LC_MEASUREMENT: - result = "C"; - break; - default: - break; - } -#endif - - return result; -} - -static int -setlocale_null_r_unlocked (int category, char *buf, size_t bufsize) -{ -#if defined _WIN32 && !defined __CYGWIN__ && defined _MSC_VER - /* On native Windows, nowadays, the setlocale() implementation is based - on _wsetlocale() and uses malloc() for the result. We are better off - using _wsetlocale() directly. */ - const wchar_t *result = _wsetlocale (category, NULL); - - if (result == NULL) - { - /* CATEGORY is invalid. */ - if (bufsize > 0) - /* Return an empty string in BUF. - This is a convenience for callers that don't want to write explicit - code for handling EINVAL. */ - buf[0] = '\0'; - return EINVAL; - } - else - { - size_t length = wcslen (result); - if (length < bufsize) - { - size_t i; - - /* Convert wchar_t[] -> char[], assuming plain ASCII. */ - for (i = 0; i <= length; i++) - buf[i] = result[i]; - - return 0; - } - else - { - if (bufsize > 0) - { - /* Return a truncated result in BUF. - This is a convenience for callers that don't want to write - explicit code for handling ERANGE. */ - size_t i; - - /* Convert wchar_t[] -> char[], assuming plain ASCII. */ - for (i = 0; i < bufsize; i++) - buf[i] = result[i]; - buf[bufsize - 1] = '\0'; - } - return ERANGE; - } - } -#else - const char *result = setlocale_null_unlocked (category); - - if (result == NULL) - { - /* CATEGORY is invalid. */ - if (bufsize > 0) - /* Return an empty string in BUF. - This is a convenience for callers that don't want to write explicit - code for handling EINVAL. */ - buf[0] = '\0'; - return EINVAL; - } - else - { - size_t length = strlen (result); - if (length < bufsize) - { - memcpy (buf, result, length + 1); - return 0; - } - else - { - if (bufsize > 0) - { - /* Return a truncated result in BUF. - This is a convenience for callers that don't want to write - explicit code for handling ERANGE. */ - memcpy (buf, result, bufsize - 1); - buf[bufsize - 1] = '\0'; - } - return ERANGE; - } - } -#endif -} - #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */ /* Use a lock, so that no two threads can invoke setlocale_null_r_unlocked diff --git a/lib/setlocale_null.h b/lib/setlocale_null.h index defa2b41a6..966c53cfaa 100644 --- a/lib/setlocale_null.h +++ b/lib/setlocale_null.h @@ -44,6 +44,34 @@ extern "C" { 55+5*58. */ #define SETLOCALE_NULL_ALL_MAX (148+12*256+1) +/* setlocale_null_r_unlocked (CATEGORY, BUF, BUFSIZE) is like + setlocale (CATEGORY, NULL), except that + - it returns the resulting locale category name or locale name in the + user-supplied buffer BUF, which must be BUFSIZE bytes long. + The recommended minimum buffer size is + - SETLOCALE_NULL_MAX for CATEGORY != LC_ALL, and + - SETLOCALE_NULL_ALL_MAX for CATEGORY == LC_ALL. + The return value is an error code: 0 if the call is successful, EINVAL if + CATEGORY is invalid, or ERANGE if BUFSIZE is smaller than the length needed + size (including the trailing NUL byte). In the latter case, a truncated + result is returned in BUF, but still NUL-terminated if BUFSIZE > 0. + This call is guaranteed to be multithread-safe only if + - CATEGORY != LC_ALL and SETLOCALE_NULL_ONE_MTSAFE is true, or + - CATEGORY == LC_ALL and SETLOCALE_NULL_ALL_MTSAFE is true, + and the other threads must not make other setlocale invocations (since + changing the global locale has side effects on all threads). */ +extern int setlocale_null_r_unlocked (int category, char *buf, size_t bufsize) + _GL_ARG_NONNULL ((2)); + +/* setlocale_null_unlocked (CATEGORY) is like setlocale (CATEGORY, NULL). + The return value is NULL if CATEGORY is invalid. + This call is guaranteed to be multithread-safe only if + - CATEGORY != LC_ALL and SETLOCALE_NULL_ONE_MTSAFE is true, or + - CATEGORY == LC_ALL and SETLOCALE_NULL_ALL_MTSAFE is true, + and the other threads must not make other setlocale invocations (since + changing the global locale has side effects on all threads). */ +extern const char *setlocale_null_unlocked (int category); + /* setlocale_null_r (CATEGORY, BUF, BUFSIZE) is like setlocale (CATEGORY, NULL), except that - it is guaranteed to be multithread-safe, diff --git a/modules/setlocale-null b/modules/setlocale-null index 91efd51483..782e29ad8d 100644 --- a/modules/setlocale-null +++ b/modules/setlocale-null @@ -13,6 +13,7 @@ m4/visibility.m4 Depends-on: locale snippet/arg-nonnull +setlocale-null-unlocked configure.ac: gl_FUNC_SETLOCALE_NULL diff --git a/modules/setlocale-null-unlocked b/modules/setlocale-null-unlocked new file mode 100644 index 0000000000..21d9611501 --- /dev/null +++ b/modules/setlocale-null-unlocked @@ -0,0 +1,27 @@ +Description: +setlocale_null_unlocked() function: query the name of the current global locale, +without locking. + +Files: +lib/setlocale_null.h +lib/setlocale_null-unlocked.c + +Depends-on: +locale +snippet/arg-nonnull + +configure.ac: + +Makefile.am: +lib_SOURCES += setlocale_null-unlocked.c + +Include: +#include "setlocale_null.h" + +Link: + +License: +LGPLv2+ + +Maintainer: +Bruno Haible -- 2.34.1
>From af029caa3537b78d2e62e2674cfc82381bb29e66 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Thu, 15 Feb 2024 10:32:57 +0100 Subject: [PATCH 3/7] setlocale_null-unlocked: Add tests. * tests/test-setlocale_null-unlocked.c: New file, based on tests/test-setlocale_null.c. * modules/setlocale-null-unlocked-tests: New file. --- ChangeLog | 5 ++++ modules/setlocale-null-unlocked-tests | 10 ++++++++ tests/test-setlocale_null-unlocked.c | 35 +++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 modules/setlocale-null-unlocked-tests create mode 100644 tests/test-setlocale_null-unlocked.c diff --git a/ChangeLog b/ChangeLog index b2668b99bf..761f51bc36 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2024-02-15 Bruno Haible <br...@clisp.org> + setlocale_null-unlocked: Add tests. + * tests/test-setlocale_null-unlocked.c: New file, based on + tests/test-setlocale_null.c. + * modules/setlocale-null-unlocked-tests: New file. + setlocale_null-unlocked: New module. * lib/setlocale_null.h (setlocale_null_r_unlocked, setlocale_null_unlocked): New declarations. diff --git a/modules/setlocale-null-unlocked-tests b/modules/setlocale-null-unlocked-tests new file mode 100644 index 0000000000..1fbfb895a0 --- /dev/null +++ b/modules/setlocale-null-unlocked-tests @@ -0,0 +1,10 @@ +Files: +tests/test-setlocale_null-unlocked.c + +Depends-on: + +configure.ac: + +Makefile.am: +TESTS += test-setlocale_null-unlocked +check_PROGRAMS += test-setlocale_null-unlocked diff --git a/tests/test-setlocale_null-unlocked.c b/tests/test-setlocale_null-unlocked.c new file mode 100644 index 0000000000..d06dec6071 --- /dev/null +++ b/tests/test-setlocale_null-unlocked.c @@ -0,0 +1,35 @@ +/* Test of setlocale_null_r_unlocked function. + Copyright (C) 2019-2024 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2019. */ + +#include <config.h> + +/* Specification. */ +#include "setlocale_null.h" + +#include <locale.h> + +/* Check that SETLOCALE_NULL_ALL_MAX is a constant expression. */ +static char buf[SETLOCALE_NULL_ALL_MAX]; + +int +main () +{ + /* Check that setlocale_null_r_unlocked() can be used without any + libraries. */ + return setlocale_null_r_unlocked (LC_ALL, buf, sizeof (buf)) != 0; +} -- 2.34.1