This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Add thread-safe locale handling
[perl5.git] / vutil.c
diff --git a/vutil.c b/vutil.c
index af5f263..a66d6ef 100644 (file)
--- a/vutil.c
+++ b/vutil.c
@@ -625,7 +625,11 @@ VER_NV:
              * locales without letting perl know, therefore we have to find it
              * from first principals.  See [perl #121930]. */
 
-            /* if it isn't C, set it to C. */
+            /* In windows, or not threaded, or not thread-safe, if it isn't C,
+             * set it to C. */
+
+#  ifndef USE_POSIX_2008_LOCALE
+
             const char * locale_name_on_entry;
 
             LC_NUMERIC_LOCK(0);    /* Start critical section */
@@ -641,6 +645,48 @@ VER_NV:
                 locale_name_on_entry = NULL;
             }
 
+# else
+
+            const locale_t locale_obj_on_entry = uselocale((locale_t) 0);
+            const char * locale_name_on_entry = NULL;
+            DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
+
+            if (locale_obj_on_entry == LC_GLOBAL_LOCALE) {
+
+                /* in the global locale, we can call system setlocale and if it
+                 * isn't C, set it to C. */
+                LC_NUMERIC_LOCK(0);
+
+                locale_name_on_entry = setlocale(LC_NUMERIC, NULL);
+                if (   strNE(locale_name_on_entry, "C")
+                    && strNE(locale_name_on_entry, "POSIX"))
+                {
+                    setlocale(LC_NUMERIC, "C");
+                }
+                else {  /* This value indicates to the restore code that we
+                           didn't change the locale */
+                    locale_name_on_entry = NULL;
+                }
+            }
+            else if (locale_obj_on_entry == PL_underlying_numeric_obj) {
+                /* Here, the locale appears to have been changed to use the
+                 * program's underlying locale.  Just use our mechanisms to
+                 * switch back to C.   It might be possible for this pointer to
+                 * actually refer to something else if it got released and
+                 * reused somehow.  But it doesn't matter, our mechanisms will
+                 * work even so */
+                STORE_LC_NUMERIC_SET_STANDARD();
+            }
+            else if (locale_obj_on_entry != PL_C_locale_obj) {
+                /* The C object should be unchanged during a program's
+                 * execution, so it should be safe to assume it means what it
+                 * says, so if we are in it, no locale change is required.
+                 * Otherwise, simply use the thread-safe operation. */
+                uselocale(PL_C_locale_obj);
+            }
+
+# endif
+
             /* Prevent recursed calls from trying to change back */
             LOCK_LC_NUMERIC_STANDARD();
 
@@ -660,12 +706,29 @@ VER_NV:
 
             UNLOCK_LC_NUMERIC_STANDARD();
 
+#  ifndef USE_POSIX_2008_LOCALE
+
             if (locale_name_on_entry) {
                 setlocale(LC_NUMERIC, locale_name_on_entry);
             }
 
             LC_NUMERIC_UNLOCK;  /* End critical section */
 
+#  else
+
+            if (locale_name_on_entry) {
+                setlocale(LC_NUMERIC, locale_name_on_entry);
+                LC_NUMERIC_UNLOCK;
+            }
+            else if (locale_obj_on_entry == PL_underlying_numeric_obj) {
+                RESTORE_LC_NUMERIC();
+            }
+            else if (locale_obj_on_entry != PL_C_locale_obj) {
+                uselocale(locale_obj_on_entry);
+            }
+
+#  endif
+
         }
 
 #endif  /* USE_LOCALE_NUMERIC */