bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: setlocale: make calls with NULL argument multithread-safe


From: Bruno Haible
Subject: Re: setlocale: make calls with NULL argument multithread-safe
Date: Wed, 18 Dec 2019 17:07:46 +0100
User-agent: KMail/5.1.3 (Linux/4.4.0-166-generic; KDE/5.18.0; x86_64; ; )

Part of this new code in setlocale.c would also be useful for localename.c.
I'm therefore moving it to module 'setlocale-null'. At the same time, I'm
renaming the function setlocale_null to setlocale_null_r (in analogy with
getlogin_r, ttyname_r, ptsname_r).


2019-12-18  Bruno Haible  <address@hidden>

        setlocale-null: Make API more useful.
        * lib/locale.in.h (setlocale_null_r): Renamed from setlocale_null. All
        callers changed.
        (setlocale_null): New declaration.
        * lib/setlocale_null.c (setlocale_null_androidfix): New function,
        extracted from setlocale_null_unlocked.
        (setlocale_null_unlocked): Invoke it.
        (setlocale_null_r): Renamed from setlocale_null.
        (setlocale_null): New function, extracted from setlocale_mtsafe in
        setlocale.c.
        * lib/setlocale.c: Don't include <errno.h>.
        (setlocale_mtsafe): Invoke setlocale_null.
        * lib/setlocale-lock.c: Update comments.
        * doc/posix-functions/setlocale.texi: Mention both functions.

diff --git a/lib/locale.in.h b/lib/locale.in.h
index 67e6020..f4eccc2 100644
--- a/lib/locale.in.h
+++ b/lib/locale.in.h
@@ -220,7 +220,7 @@ _GL_WARN_ON_USE (setlocale, "setlocale works differently on 
native Windows - "
    In native Windows, there are 5 categories, and the maximum total length is
    55+5*58.  */
 # define SETLOCALE_NULL_ALL_MAX (148+12*256+1)
-/* setlocale_null (CATEGORY, BUF, BUFSIZE) is like setlocale (CATEGORY, NULL),
+/* setlocale_null_r (CATEGORY, BUF, BUFSIZE) is like setlocale (CATEGORY, 
NULL),
    except that
      - it is guaranteed to be multithread-safe,
      - it returns the resulting locale category name or locale name in the
@@ -234,14 +234,25 @@ _GL_WARN_ON_USE (setlocale, "setlocale works differently 
on native Windows - "
    result is returned in BUF, but still NUL-terminated if BUFSIZE > 0.
    For this call to be multithread-safe, *all* calls to
    setlocale (CATEGORY, NULL) in all other threads must have been converted
-   to use setlocale_null as well, and the other threads must not make other
-   setlocale invocations (since changing the global locale has side effects
-   on all threads).  */
-_GL_FUNCDECL_SYS (setlocale_null, int,
+   to use setlocale_null_r or setlocale_null as well, and the other threads
+   must not make other setlocale invocations (since changing the global locale
+   has side effects on all threads).  */
+_GL_FUNCDECL_SYS (setlocale_null_r, int,
                   (int category, char *buf, size_t bufsize)
                   _GL_ARG_NONNULL ((2)));
-_GL_CXXALIAS_SYS (setlocale_null, int,
+_GL_CXXALIAS_SYS (setlocale_null_r, int,
                   (int category, char *buf, size_t bufsize));
+_GL_CXXALIASWARN (setlocale_null_r);
+/* setlocale_null (CATEGORY) is like setlocale (CATEGORY, NULL), except that
+   it is guaranteed to be multithread-safe.
+   The return value is NULL if CATEGORY is invalid.
+   For this call to be multithread-safe, *all* calls to
+   setlocale (CATEGORY, NULL) in all other threads must have been converted
+   to use setlocale_null_r or setlocale_null as well, and the other threads
+   must not make other setlocale invocations (since changing the global locale
+   has side effects on all threads).  */
+_GL_FUNCDECL_SYS (setlocale_null, const char *, (int category));
+_GL_CXXALIAS_SYS (setlocale_null, const char *, (int category));
 _GL_CXXALIASWARN (setlocale_null);
 #endif
 
diff --git a/lib/setlocale_null.c b/lib/setlocale_null.c
index 8072a45..c201257 100644
--- a/lib/setlocale_null.c
+++ b/lib/setlocale_null.c
@@ -55,6 +55,37 @@
 /* Use the system's setlocale() function, not the gnulib override, here.  */
 #undef setlocale
 
+static const char *
+setlocale_null_androidfix (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_unlocked (int category, char *buf, size_t bufsize)
 {
@@ -105,30 +136,7 @@ setlocale_null_unlocked (int category, char *buf, size_t 
bufsize)
         }
     }
 #else
-  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
+  const char *result = setlocale_null_androidfix (category);
 
   if (result == NULL)
     {
@@ -257,7 +265,7 @@ setlocale_null_with_lock (int category, char *buf, size_t 
bufsize)
 #endif
 
 int
-setlocale_null (int category, char *buf, size_t bufsize)
+setlocale_null_r (int category, char *buf, size_t bufsize)
 {
 #if SETLOCALE_NULL_ALL_MTSAFE
 # if SETLOCALE_NULL_ONE_MTSAFE
@@ -287,3 +295,116 @@ setlocale_null (int category, char *buf, size_t bufsize)
 # endif
 #endif
 }
+
+const char *
+setlocale_null (int category)
+{
+#if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE
+  return setlocale_null_androidfix (category);
+#else
+
+  /* This call must be multithread-safe.  To achieve this without using
+     thread-local storage:
+       1. We use a specific static buffer for each possible CATEGORY
+          argument.  So that different threads can call setlocale_mtsafe
+          with different CATEGORY arguments, without interfering.
+       2. We use a simple strcpy or memcpy to fill this static buffer.
+          Filling it through, for example, strcpy + strcat would not be
+          guaranteed to leave the buffer's contents intact if another thread
+          is currently accessing it.  If necessary, the contents is first
+          assembled in a stack-allocated buffer.  */
+  if (category == LC_ALL)
+    {
+# if SETLOCALE_NULL_ALL_MTSAFE
+      return setlocale_null_androidfix (LC_ALL);
+# else
+      char buf[SETLOCALE_NULL_ALL_MAX];
+      static char resultbuf[SETLOCALE_NULL_ALL_MAX];
+
+      if (setlocale_null_r (LC_ALL, buf, sizeof (buf)))
+        return "C";
+      strcpy (resultbuf, buf);
+      return resultbuf;
+# endif
+    }
+  else
+    {
+# if SETLOCALE_NULL_ONE_MTSAFE
+      return setlocale_null_androidfix (category);
+# else
+      enum
+        {
+          LC_CTYPE_INDEX,
+          LC_NUMERIC_INDEX,
+          LC_TIME_INDEX,
+          LC_COLLATE_INDEX,
+          LC_MONETARY_INDEX,
+          LC_MESSAGES_INDEX,
+#  ifdef LC_PAPER
+          LC_PAPER_INDEX,
+#  endif
+#  ifdef LC_NAME
+          LC_NAME_INDEX,
+#  endif
+#  ifdef LC_ADDRESS
+          LC_ADDRESS_INDEX,
+#  endif
+#  ifdef LC_TELEPHONE
+          LC_TELEPHONE_INDEX,
+#  endif
+#  ifdef LC_MEASUREMENT
+          LC_MEASUREMENT_INDEX,
+#  endif
+#  ifdef LC_IDENTIFICATION
+          LC_IDENTIFICATION_INDEX,
+#  endif
+          LC_INDICES_COUNT
+        }
+        i;
+      char buf[SETLOCALE_NULL_MAX];
+      static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX];
+      int err;
+
+      err = setlocale_null_r (category, buf, sizeof (buf));
+      if (err == EINVAL)
+        return NULL;
+      if (err)
+        return "C";
+
+      switch (category)
+        {
+        case LC_CTYPE:          i = LC_CTYPE_INDEX;          break;
+        case LC_NUMERIC:        i = LC_NUMERIC_INDEX;        break;
+        case LC_TIME:           i = LC_TIME_INDEX;           break;
+        case LC_COLLATE:        i = LC_COLLATE_INDEX;        break;
+        case LC_MONETARY:       i = LC_MONETARY_INDEX;       break;
+        case LC_MESSAGES:       i = LC_MESSAGES_INDEX;       break;
+#  ifdef LC_PAPER
+        case LC_PAPER:          i = LC_PAPER_INDEX;          break;
+#  endif
+#  ifdef LC_NAME
+        case LC_NAME:           i = LC_NAME_INDEX;           break;
+#  endif
+#  ifdef LC_ADDRESS
+        case LC_ADDRESS:        i = LC_ADDRESS_INDEX;        break;
+#  endif
+#  ifdef LC_TELEPHONE
+        case LC_TELEPHONE:      i = LC_TELEPHONE_INDEX;      break;
+#  endif
+#  ifdef LC_MEASUREMENT
+        case LC_MEASUREMENT:    i = LC_MEASUREMENT_INDEX;    break;
+#  endif
+#  ifdef LC_IDENTIFICATION
+        case LC_IDENTIFICATION: i = LC_IDENTIFICATION_INDEX; break;
+#  endif
+        default:
+          /* If you get here, a #ifdef LC_xxx is missing.  */
+          abort ();
+        }
+
+      strcpy (resultbuf[i], buf);
+      return resultbuf[i];
+# endif
+    }
+#endif
+}
diff --git a/lib/setlocale.c b/lib/setlocale.c
index da28a1a..55b8fa4 100644
--- a/lib/setlocale.c
+++ b/lib/setlocale.c
@@ -29,7 +29,6 @@
 /* Specification.  */
 #include <locale.h>
 
-#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -71,111 +70,7 @@ char *
 setlocale_mtsafe (int category, const char *locale)
 {
   if (locale == NULL)
-    {
-      /* This call must be multithread-safe.  To achieve this without using
-         thread-local storage:
-           1. We use a specific static buffer for each possible CATEGORY
-              argument.  So that different threads can call setlocale_mtsafe
-              with different CATEGORY arguments, without interfering.
-           2. We use a simple strcpy or memcpy to fill this static buffer.
-              Filling it through, for example, strcpy + strcat would not be
-              guaranteed to leave the buffer's contents intact if another 
thread
-              is currently accessing it.  If necessary, the contents is first
-              assembled in a stack-allocated buffer.  */
-      if (category == LC_ALL)
-        {
-#  if SETLOCALE_NULL_ALL_MTSAFE
-          return setlocale (LC_ALL, NULL);
-#  else
-          char buf[SETLOCALE_NULL_ALL_MAX];
-          static char resultbuf[SETLOCALE_NULL_ALL_MAX];
-
-          if (setlocale_null (LC_ALL, buf, sizeof (buf)))
-            return (char *) "C";
-          strcpy (resultbuf, buf);
-          return resultbuf;
-#  endif
-        }
-      else
-        {
-#  if SETLOCALE_NULL_ONE_MTSAFE
-          return setlocale (category, NULL);
-#  else
-          enum
-            {
-              LC_CTYPE_INDEX,
-              LC_NUMERIC_INDEX,
-              LC_TIME_INDEX,
-              LC_COLLATE_INDEX,
-              LC_MONETARY_INDEX,
-              LC_MESSAGES_INDEX,
-#   ifdef LC_PAPER
-              LC_PAPER_INDEX,
-#   endif
-#   ifdef LC_NAME
-              LC_NAME_INDEX,
-#   endif
-#   ifdef LC_ADDRESS
-              LC_ADDRESS_INDEX,
-#   endif
-#   ifdef LC_TELEPHONE
-              LC_TELEPHONE_INDEX,
-#   endif
-#   ifdef LC_MEASUREMENT
-              LC_MEASUREMENT_INDEX,
-#   endif
-#   ifdef LC_IDENTIFICATION
-              LC_IDENTIFICATION_INDEX,
-#   endif
-              LC_INDICES_COUNT
-            }
-            i;
-          char buf[SETLOCALE_NULL_MAX];
-          static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX];
-          int err;
-
-          err = setlocale_null (category, buf, sizeof (buf));
-          if (err == EINVAL)
-            return NULL;
-          if (err)
-            return (char *) "C";
-
-          switch (category)
-            {
-            case LC_CTYPE:          i = LC_CTYPE_INDEX;          break;
-            case LC_NUMERIC:        i = LC_NUMERIC_INDEX;        break;
-            case LC_TIME:           i = LC_TIME_INDEX;           break;
-            case LC_COLLATE:        i = LC_COLLATE_INDEX;        break;
-            case LC_MONETARY:       i = LC_MONETARY_INDEX;       break;
-            case LC_MESSAGES:       i = LC_MESSAGES_INDEX;       break;
-#   ifdef LC_PAPER
-            case LC_PAPER:          i = LC_PAPER_INDEX;          break;
-#   endif
-#   ifdef LC_NAME
-            case LC_NAME:           i = LC_NAME_INDEX;           break;
-#   endif
-#   ifdef LC_ADDRESS
-            case LC_ADDRESS:        i = LC_ADDRESS_INDEX;        break;
-#   endif
-#   ifdef LC_TELEPHONE
-            case LC_TELEPHONE:      i = LC_TELEPHONE_INDEX;      break;
-#   endif
-#   ifdef LC_MEASUREMENT
-            case LC_MEASUREMENT:    i = LC_MEASUREMENT_INDEX;    break;
-#   endif
-#   ifdef LC_IDENTIFICATION
-            case LC_IDENTIFICATION: i = LC_IDENTIFICATION_INDEX; break;
-#   endif
-            default:
-              /* If you get here, a #ifdef LC_xxx is missing.  */
-              abort ();
-            }
-
-          strcpy (resultbuf[i], buf);
-          return resultbuf[i];
-#  endif
-        }
-    }
+    return (char *) setlocale_null (category);
   else
     return setlocale (category, locale);
 }
diff --git a/lib/duplocale.c b/lib/duplocale.c
index d22a447..da94d44 100644
--- a/lib/duplocale.c
+++ b/lib/duplocale.c
@@ -72,7 +72,7 @@ rpl_duplocale (locale_t locale)
       locale_t base_copy;
       unsigned int i;
 
-      err = setlocale_null (LC_CTYPE, base_name, sizeof (base_name));
+      err = setlocale_null_r (LC_CTYPE, base_name, sizeof (base_name));
       if (err)
         {
           errno = err;
@@ -88,7 +88,7 @@ rpl_duplocale (locale_t locale)
           int category_mask = categories[i].mask;
           char name[SETLOCALE_NULL_MAX];
 
-          err = setlocale_null (category, name, sizeof (name));
+          err = setlocale_null_r (category, name, sizeof (name));
           if (err)
             {
               errno = err;
diff --git a/lib/hard-locale.c b/lib/hard-locale.c
index 4a2adab..5eaa7b6 100644
--- a/lib/hard-locale.c
+++ b/lib/hard-locale.c
@@ -28,7 +28,7 @@ hard_locale (int category)
 {
   char locale[SETLOCALE_NULL_MAX];
 
-  if (setlocale_null (category, locale, sizeof (locale)))
+  if (setlocale_null_r (category, locale, sizeof (locale)))
     return false;
 
   return !(strcmp (locale, "C") == 0 || strcmp (locale, "POSIX") == 0);
diff --git a/lib/nl_langinfo.c b/lib/nl_langinfo.c
index ff0e936..2c16511 100644
--- a/lib/nl_langinfo.c
+++ b/lib/nl_langinfo.c
@@ -51,7 +51,7 @@ ctype_codeset (void)
   char *codeset;
   size_t codesetlen;
 
-  if (setlocale_null (LC_CTYPE, locale, sizeof (locale)))
+  if (setlocale_null_r (LC_CTYPE, locale, sizeof (locale)))
     locale[0] = '\0';
 
   codeset = buf;
diff --git a/lib/setlocale-lock.c b/lib/setlocale-lock.c
index ed6ab9f..9c47afc 100644
--- a/lib/setlocale-lock.c
+++ b/lib/setlocale-lock.c
@@ -1,4 +1,4 @@
-/* Return the internal lock used by setlocale_null.
+/* Return the internal lock used by setlocale_null_r.
    Copyright (C) 2019 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -18,7 +18,7 @@
 
 #include <config.h>
 
-/* This file defines the internal lock used by setlocale_null.
+/* This file defines the internal lock used by setlocale_null_r.
    It is a separate compilation unit, so that only one copy of it is
    present when linking statically.  */
 
@@ -41,7 +41,7 @@ __declspec(dllexport) CRITICAL_SECTION 
*gl_get_setlocale_null_lock (void);
 static glwthread_initguard_t guard = GLWTHREAD_INITGUARD_INIT;
 static CRITICAL_SECTION lock;
 
-/* Returns the internal lock used by setlocale_null.  */
+/* Returns the internal lock used by setlocale_null_r.  */
 CRITICAL_SECTION *
 gl_get_setlocale_null_lock (void)
 {
@@ -77,7 +77,7 @@ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 __declspec(dllexport) pthread_mutex_t *gl_get_setlocale_null_lock (void);
 # endif
 
-/* Returns the internal lock used by setlocale_null.  */
+/* Returns the internal lock used by setlocale_null_r.  */
 pthread_mutex_t *
 gl_get_setlocale_null_lock (void)
 {
@@ -101,7 +101,7 @@ atomic_init (void)
   init_needed = 0;
 }
 
-/* Returns the internal lock used by setlocale_null.  */
+/* Returns the internal lock used by setlocale_null_r.  */
 mtx_t *
 gl_get_setlocale_null_lock (void)
 {
diff --git a/tests/test-setlocale_null-all.c b/tests/test-setlocale_null-all.c
index 45eb27e..abf2df4 100644
--- a/tests/test-setlocale_null-all.c
+++ b/tests/test-setlocale_null-all.c
@@ -1,4 +1,4 @@
-/* Multithread-safety test for setlocale_null (LC_ALL, ...).
+/* Multithread-safety test for setlocale_null_r (LC_ALL, ...).
    Copyright (C) 2019 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -66,7 +66,7 @@ thread1_func (void *arg)
     {
       char buf[SETLOCALE_NULL_ALL_MAX];
 
-      if (setlocale_null (LC_ALL, buf, sizeof (buf)))
+      if (setlocale_null_r (LC_ALL, buf, sizeof (buf)))
         abort ();
       if (strcmp (expected, buf) != 0)
         {
@@ -86,8 +86,8 @@ thread2_func (void *arg)
     {
       char buf[SETLOCALE_NULL_ALL_MAX];
 
-      setlocale_null (LC_NUMERIC, buf, sizeof (buf));
-      setlocale_null (LC_ALL, buf, sizeof (buf));
+      setlocale_null_r (LC_NUMERIC, buf, sizeof (buf));
+      setlocale_null_r (LC_ALL, buf, sizeof (buf));
     }
 
   /*NOTREACHED*/
diff --git a/tests/test-setlocale_null-one.c b/tests/test-setlocale_null-one.c
index 9e9bc12..a83e485 100644
--- a/tests/test-setlocale_null-one.c
+++ b/tests/test-setlocale_null-one.c
@@ -1,4 +1,4 @@
-/* Multithread-safety test for setlocale_null (LC_xxx, ...).
+/* Multithread-safety test for setlocale_null_r (LC_xxx, ...).
    Copyright (C) 2019 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -66,7 +66,7 @@ thread1_func (void *arg)
     {
       char buf[SETLOCALE_NULL_MAX];
 
-      if (setlocale_null (LC_NUMERIC, buf, sizeof (buf)))
+      if (setlocale_null_r (LC_NUMERIC, buf, sizeof (buf)))
         abort ();
       if (strcmp (expected, buf) != 0)
         {
@@ -86,8 +86,8 @@ thread2_func (void *arg)
     {
       char buf[SETLOCALE_NULL_MAX];
 
-      setlocale_null (LC_NUMERIC, buf, sizeof (buf));
-      setlocale_null (LC_TIME, buf, sizeof (buf));
+      setlocale_null_r (LC_NUMERIC, buf, sizeof (buf));
+      setlocale_null_r (LC_TIME, buf, sizeof (buf));
     }
 
   /*NOTREACHED*/
diff --git a/tests/test-setlocale_null.c b/tests/test-setlocale_null.c
index 003cfc9..4244804 100644
--- a/tests/test-setlocale_null.c
+++ b/tests/test-setlocale_null.c
@@ -1,4 +1,4 @@
-/* Test of setlocale_null function.
+/* Test of setlocale_null_r function.
    Copyright (C) 2019 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -27,6 +27,6 @@ static char buf[SETLOCALE_NULL_ALL_MAX];
 int
 main ()
 {
-  /* Check that setlocale_null () can be used with $(LIB_SETLOCALE_NULL).  */
-  return setlocale_null (LC_ALL, buf, sizeof (buf)) != 0;
+  /* Check that setlocale_null_r() can be used with $(LIB_SETLOCALE_NULL).  */
+  return setlocale_null_r (LC_ALL, buf, sizeof (buf)) != 0;
 }
diff --git a/doc/posix-functions/setlocale.texi 
b/doc/posix-functions/setlocale.texi
index d8282b6..11364d3 100644
--- a/doc/posix-functions/setlocale.texi
+++ b/doc/posix-functions/setlocale.texi
@@ -34,7 +34,7 @@ platforms:
 musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin.
 To make these invocations multithread-safe, you need the Gnulib module
 @code{setlocale}, or you need to change the code to invoke 
@code{setlocale_null}
-instead.
+or @code{setlocale_null_r} instead.
 @end itemize
 
 Portability problems not fixed by Gnulib:




reply via email to

[Prev in Thread] Current Thread [Next in Thread]