diff --git a/embedvar.h b/embedvar.h index 8c6584722f17..de295d2da250 100644 --- a/embedvar.h +++ b/embedvar.h @@ -87,6 +87,7 @@ #define PL_cop_seqmax (vTHX->Icop_seqmax) #define PL_ctype_name (vTHX->Ictype_name) #define PL_cur_LC_ALL (vTHX->Icur_LC_ALL) +#define PL_cur_locale_obj (vTHX->Icur_locale_obj) #define PL_curcop (vTHX->Icurcop) #define PL_curcopdb (vTHX->Icurcopdb) #define PL_curlocales (vTHX->Icurlocales) diff --git a/intrpvar.h b/intrpvar.h index 6a3b752a2450..bdc7e83173b8 100644 --- a/intrpvar.h +++ b/intrpvar.h @@ -739,6 +739,9 @@ PERLVAR(I, constpadix, PADOFFSET) /* lowest unused for constants */ PERLVAR(I, padix_floor, PADOFFSET) /* how low may inner block reset padix */ +#if defined(USE_POSIX_2008_LOCALE) && defined(MULTIPLICITY) +PERLVARI(I, cur_locale_obj, locale_t, NULL) +#endif #ifdef USE_PL_CURLOCALES /* This is the most number of categories we've encountered so far on any diff --git a/locale.c b/locale.c index 5e909f17da41..5083c3b98a09 100644 --- a/locale.c +++ b/locale.c @@ -1372,6 +1372,10 @@ S_emulate_setlocale_i(pTHX_ DEBUG_Lv(PerlIO_printf(Perl_debug_log, "(%" LINE_Tf "): emulate_setlocale_i now using %p\n", line, new_obj)); +#ifdef MULTIPLICITY + PL_cur_locale_obj = new_obj; +#endif + /* We are done, except for updating our records (if the system doesn't keep * them) and in the case of locale "", we don't actually know what the * locale that got switched to is, as it came from the environment. So @@ -6726,31 +6730,28 @@ S_my_setlocale_debug_string_i(pTHX_ void Perl_thread_locale_init(pTHX) { - /* Called from a thread on startup*/ #ifdef USE_THREAD_SAFE_LOCALE +# ifdef USE_POSIX_2008_LOCALE + + /* Called from a thread on startup. + * + * The operations here have to be done from within the calling thread, as + * they affect libc's knowledge of the thread; libc has no knowledge of + * aTHX */ DEBUG_L(PerlIO_printf(Perl_debug_log, "new thread, initial locale is %s;" " calling setlocale(LC_ALL, \"C\")\n", get_LC_ALL_display())); -# ifdef WIN32 - /* On Windows, make sure new thread has per-thread locales enabled */ - _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); - -# endif -# if defined(LC_ALL) + uselocale(PL_C_locale_obj); - /* This thread starts off in the C locale. Use the full Perl_setlocale() - * to make sure no ill-advised shortcuts get taken on this new thread, */ - Perl_setlocale(LC_ALL, "C"); +# elif defined(WIN32) -# else - - for (unsigned i = 0; i < NOMINAL_LC_ALL_INDEX; i++) { - Perl_setlocale(categories[i], "C"); - } + /* On Windows, make sure new thread has per-thread locales enabled */ + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + void_setlocale_c(LC_ALL, "C"); # endif #endif @@ -6760,20 +6761,34 @@ Perl_thread_locale_init(pTHX) void Perl_thread_locale_term(pTHX) { - /* Called from a thread as it gets ready to terminate */ + /* Called from a thread as it gets ready to terminate. + * + * The operations here have to be done from within the calling thread, as + * they affect libc's knowledge of the thread; libc has no knowledge of + * aTHX */ #ifdef USE_POSIX_2008_LOCALE /* C starts the new thread in the global C locale. If we are thread-safe, * we want to not be in the global locale */ - { /* Free up */ - locale_t cur_obj = uselocale(LC_GLOBAL_LOCALE); - if (cur_obj != LC_GLOBAL_LOCALE && cur_obj != PL_C_locale_obj) { - freelocale(cur_obj); - } + /* Free up */ + locale_t actual_obj = uselocale(LC_GLOBAL_LOCALE); + if (actual_obj != LC_GLOBAL_LOCALE && actual_obj != PL_C_locale_obj) { + freelocale(actual_obj); } + /* Prevent leaks even if something has gone wrong */ + locale_t expected_obj = PL_cur_locale_obj; + if (UNLIKELY( expected_obj != actual_obj + && expected_obj != LC_GLOBAL_LOCALE + && expected_obj != PL_C_locale_obj)) + { + freelocale(expected_obj); + } + + PL_cur_locale_obj = LC_GLOBAL_LOCALE; + #endif } diff --git a/makedef.pl b/makedef.pl index ca30c43d646f..acea937ebcca 100644 --- a/makedef.pl +++ b/makedef.pl @@ -386,6 +386,7 @@ sub readvar { ++$skip{$_} foreach qw( PL_keyword_plugin_mutex PL_check_mutex + PL_cur_locale_obj PL_op_mutex PL_regex_pad PL_regex_padav diff --git a/perl.c b/perl.c index 0a3ec39811ce..417381683c1e 100644 --- a/perl.c +++ b/perl.c @@ -1129,15 +1129,17 @@ perl_destruct(pTHXx) { /* This also makes sure we aren't using a locale object that gets freed * below */ - const locale_t old_locale = uselocale(LC_GLOBAL_LOCALE); - if ( old_locale != LC_GLOBAL_LOCALE -# ifdef USE_POSIX_2008_LOCALE - && old_locale != PL_C_locale_obj -# endif + if ( PL_cur_locale_obj != NULL + && PL_cur_locale_obj != LC_GLOBAL_LOCALE + && PL_cur_locale_obj != PL_C_locale_obj ) { - DEBUG_Lv(PerlIO_printf(Perl_debug_log, - "%s:%d: Freeing %p\n", __FILE__, __LINE__, old_locale)); - freelocale(old_locale); + locale_t cur_locale = uselocale((locale_t) 0); + if (cur_locale == PL_cur_locale_obj) { + uselocale(LC_GLOBAL_LOCALE); + } + + freelocale(PL_cur_locale_obj); + PL_cur_locale_obj = NULL; } } diff --git a/sv.c b/sv.c index bc5f95143b2d..99b71c65ab1c 100644 --- a/sv.c +++ b/sv.c @@ -15939,6 +15939,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags, #endif /* !USE_LOCALE_NUMERIC */ #if defined(USE_POSIX_2008_LOCALE) PL_scratch_locale_obj = NULL; + PL_cur_locale_obj = PL_C_locale_obj; #endif #ifdef HAS_MBRLEN