@@ -618,42 +618,80 @@ Perl_upg_version(pTHX_ SV *ver, bool qv)
618
618
goto VER_PV ;
619
619
}
620
620
#endif
621
-
622
621
#ifdef USE_LOCALE_NUMERIC
622
+
623
623
{
624
- const char * const cur_numeric = setlocale (LC_NUMERIC , NULL );
625
- assert (cur_numeric );
626
-
627
- /* XS code can set the locale without us knowing. To protect the
628
- * version number parsing, which requires the radix character to be a
629
- * dot, update our records as to what the locale is, so that our
630
- * existing macro mechanism can correctly change it to a dot and back
631
- * if necessary. This code is extremely unlikely to be in a loop, so
632
- * the extra work will have a negligible performance impact. See [perl
633
- * #121930].
634
- *
635
- * If the current locale is a standard one, but we are expecting it to
636
- * be a different, underlying locale, update our records to make the
637
- * underlying locale this (standard) one. If the current locale is not
638
- * a standard one, we should be expecting a non-standard one, the same
639
- * one that we have recorded as the underlying locale. If not, update
640
- * our records. */
641
- if (strEQ (cur_numeric , "C" ) || strEQ (cur_numeric , "POSIX" )) {
642
- if (! PL_numeric_standard ) {
643
- new_numeric (cur_numeric );
644
- }
645
- }
646
- else if (PL_numeric_standard
647
- || ! PL_numeric_name
648
- || strNE (PL_numeric_name , cur_numeric ))
649
- {
650
- new_numeric (cur_numeric );
624
+ /* This may or may not be called from code that has switched
625
+ * locales without letting perl know, therefore we have to find it
626
+ * from first principals. See [perl #121930]. */
627
+
628
+ /* In windows, or not threaded, or not thread-safe, if it isn't C,
629
+ * set it to C. */
630
+
631
+ # ifndef USE_POSIX_2008_LOCALE
632
+
633
+ const char * locale_name_on_entry ;
634
+
635
+ LC_NUMERIC_LOCK (0 ); /* Start critical section */
636
+
637
+ locale_name_on_entry = setlocale (LC_NUMERIC , NULL );
638
+ if ( strNE (locale_name_on_entry , "C" )
639
+ && strNE (locale_name_on_entry , "POSIX" ))
640
+ {
641
+ setlocale (LC_NUMERIC , "C" );
642
+ }
643
+ else { /* This value indicates to the restore code that we didn't
644
+ change the locale */
645
+ locale_name_on_entry = NULL ;
646
+ }
647
+
648
+ # else
649
+
650
+ const locale_t locale_obj_on_entry = uselocale ((locale_t ) 0 );
651
+ const char * locale_name_on_entry = NULL ;
652
+ DECLARATION_FOR_LC_NUMERIC_MANIPULATION ;
653
+
654
+ if (locale_obj_on_entry == LC_GLOBAL_LOCALE ) {
655
+
656
+ /* in the global locale, we can call system setlocale and if it
657
+ * isn't C, set it to C. */
658
+ LC_NUMERIC_LOCK (0 );
659
+
660
+ locale_name_on_entry = setlocale (LC_NUMERIC , NULL );
661
+ if ( strNE (locale_name_on_entry , "C" )
662
+ && strNE (locale_name_on_entry , "POSIX" ))
663
+ {
664
+ setlocale (LC_NUMERIC , "C" );
665
+ }
666
+ else { /* This value indicates to the restore code that we
667
+ didn't change the locale */
668
+ locale_name_on_entry = NULL ;
651
669
}
652
670
}
671
+ else if (locale_obj_on_entry == PL_underlying_numeric_obj ) {
672
+ /* Here, the locale appears to have been changed to use the
673
+ * program's underlying locale. Just use our mechanisms to
674
+ * switch back to C. It might be possible for this pointer to
675
+ * actually refer to something else if it got released and
676
+ * reused somehow. But it doesn't matter, our mechanisms will
677
+ * work even so */
678
+ STORE_LC_NUMERIC_SET_STANDARD ();
679
+ }
680
+ else if (locale_obj_on_entry != PL_C_locale_obj ) {
681
+ /* The C object should be unchanged during a program's
682
+ * execution, so it should be safe to assume it means what it
683
+ * says, so if we are in it, no locale change is required.
684
+ * Otherwise, simply use the thread-safe operation. */
685
+ uselocale (PL_C_locale_obj );
686
+ }
687
+
688
+ # endif
689
+
690
+ /* Prevent recursed calls from trying to change back */
691
+ LOCK_LC_NUMERIC_STANDARD ();
692
+
653
693
#endif
654
- { /* Braces needed because macro just below declares a variable */
655
- STORE_NUMERIC_LOCAL_SET_STANDARD ();
656
- LOCK_NUMERIC_STANDARD ();
694
+
657
695
if (sv ) {
658
696
Perl_sv_catpvf (aTHX_ sv , "%.9" NVff , SvNVX (ver ));
659
697
len = SvCUR (sv );
@@ -663,9 +701,38 @@ Perl_upg_version(pTHX_ SV *ver, bool qv)
663
701
len = my_snprintf (tbuf , sizeof (tbuf ), "%.9" NVff , SvNVX (ver ));
664
702
buf = tbuf ;
665
703
}
666
- UNLOCK_NUMERIC_STANDARD ();
667
- RESTORE_NUMERIC_LOCAL ();
704
+
705
+ #ifdef USE_LOCALE_NUMERIC
706
+
707
+ UNLOCK_LC_NUMERIC_STANDARD ();
708
+
709
+ # ifndef USE_POSIX_2008_LOCALE
710
+
711
+ if (locale_name_on_entry ) {
712
+ setlocale (LC_NUMERIC , locale_name_on_entry );
713
+ }
714
+
715
+ LC_NUMERIC_UNLOCK ; /* End critical section */
716
+
717
+ # else
718
+
719
+ if (locale_name_on_entry ) {
720
+ setlocale (LC_NUMERIC , locale_name_on_entry );
721
+ LC_NUMERIC_UNLOCK ;
722
+ }
723
+ else if (locale_obj_on_entry == PL_underlying_numeric_obj ) {
724
+ RESTORE_LC_NUMERIC ();
725
+ }
726
+ else if (locale_obj_on_entry != PL_C_locale_obj ) {
727
+ uselocale (locale_obj_on_entry );
728
+ }
729
+
730
+ # endif
731
+
668
732
}
733
+
734
+ #endif /* USE_LOCALE_NUMERIC */
735
+
669
736
while (buf [len - 1 ] == '0' && len > 0 ) len -- ;
670
737
if ( buf [len - 1 ] == '.' ) len -- ; /* eat the trailing decimal */
671
738
version = savepvn (buf , len );
0 commit comments