Skip to content

Commit 1094750

Browse files
committed
Some locale operations need to be done in proper thread
This is a step in solving #20155 The POSIX 2008 locale API introduces per-thread locales. But the previous global locale system is retained, probably for backward compatibility. The POSIX 2008 interface causes memory to be malloc'd that needs to be freed. In order to do this, the caller must first stop using that memory, by switching to another locale. perl accomplishes this during termination by switching to the global locale, which is always available and doesn't need to be freed. Perl has long assumed that all that was needed to switch threads was to change out tTHX. That's because that structure was intended to hold all the information for a given thread. But it turns out that this doesn't work when some library independently holds information about the thread's state. And there are now some libraries that do that. What was happening in this case was that perl thought that it was sufficient to switch tTHX to change to a different thread in order to do the freeing of memory, and then used the POSIX 2008 function to change to the global locale so that the memory could be safely freed. But the POSIX 2008 function doesn't care about tTHX, and actually was typically operating on a different thread, and so changed that thread to the global locale instead of the intended thread. Often that was the top-level thread, thread 0. That caused whatever thread it was to no longer be in the expected locale, and to no longer be thread-safe with regards to localess, This commit causes locale_term(), which has always been called from the actual terminating thread that POSIX 2008 knows about, to change to the global thread and free the memory. It also creates a new per-interpreter variable that effectively maps the tTHX thread to the associated POSIX 2008 memory. During perl_destruct(), it frees the memory this variable points to, instead of blindly assuming the memory to free is the current tTHX thread's. This fixes the symptoms associtated with #20155, but doesn't solve the whole problem. In general, a library that has independent thread status needs to be updated to the new thread when Perl changes threads using tTHX. Future commits will do this.
1 parent 7af2d20 commit 1094750

File tree

6 files changed

+52
-29
lines changed

6 files changed

+52
-29
lines changed

embedvar.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

intrpvar.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,9 @@ PERLVAR(I, constpadix, PADOFFSET) /* lowest unused for constants */
738738

739739
PERLVAR(I, padix_floor, PADOFFSET) /* how low may inner block reset padix */
740740

741+
#if defined(USE_POSIX_2008_LOCALE) && defined(MULTIPLICITY)
742+
PERLVARI(I, cur_locale_obj, locale_t, NULL)
743+
#endif
741744
#ifdef USE_PL_CURLOCALES
742745

743746
/* This is the most number of categories we've encountered so far on any

locale.c

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,10 @@ S_emulate_setlocale_i(pTHX_
13091309
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
13101310
"(%" LINE_Tf "): emulate_setlocale_i now using %p\n", line, new_obj));
13111311

1312+
#ifdef MULTIPLICITY
1313+
PL_cur_locale_obj = new_obj;
1314+
#endif
1315+
13121316
/* We are done, except for updating our records (if the system doesn't keep
13131317
* them) and in the case of locale "", we don't actually know what the
13141318
* locale that got switched to is, as it came from the environment. So
@@ -6723,31 +6727,28 @@ S_my_setlocale_debug_string_i(pTHX_
67236727
void
67246728
Perl_thread_locale_init(pTHX)
67256729
{
6726-
/* Called from a thread on startup*/
67276730

67286731
#ifdef USE_THREAD_SAFE_LOCALE
6732+
# ifdef USE_POSIX_2008_LOCALE
6733+
6734+
/* Called from a thread on startup.
6735+
*
6736+
* The operations here have to be done from within the calling thread, as
6737+
* they affect libc's knowledge of the thread; libc has no knowledge of
6738+
* aTHX */
67296739

67306740
DEBUG_L(PerlIO_printf(Perl_debug_log,
67316741
"new thread, initial locale is %s;"
67326742
" calling setlocale(LC_ALL, \"C\")\n",
67336743
get_LC_ALL_display()));
6734-
# ifdef WIN32
67356744

6736-
/* On Windows, make sure new thread has per-thread locales enabled */
6737-
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
6738-
6739-
# endif
6740-
# if defined(LC_ALL)
6745+
uselocale(PL_C_locale_obj);
67416746

6742-
/* This thread starts off in the C locale. Use the full Perl_setlocale()
6743-
* to make sure no ill-advised shortcuts get taken on this new thread, */
6744-
Perl_setlocale(LC_ALL, "C");
6747+
# elif defined(WIN32)
67456748

6746-
# else
6747-
6748-
for (unsigned i = 0; i < NOMINAL_LC_ALL_INDEX; i++) {
6749-
Perl_setlocale(categories[i], "C");
6750-
}
6749+
/* On Windows, make sure new thread has per-thread locales enabled */
6750+
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
6751+
void_setlocale_c(LC_ALL, "C");
67516752

67526753
# endif
67536754
#endif
@@ -6757,20 +6758,34 @@ Perl_thread_locale_init(pTHX)
67576758
void
67586759
Perl_thread_locale_term(pTHX)
67596760
{
6760-
/* Called from a thread as it gets ready to terminate */
6761+
/* Called from a thread as it gets ready to terminate.
6762+
*
6763+
* The operations here have to be done from within the calling thread, as
6764+
* they affect libc's knowledge of the thread; libc has no knowledge of
6765+
* aTHX */
67616766

67626767
#ifdef USE_POSIX_2008_LOCALE
67636768

67646769
/* C starts the new thread in the global C locale. If we are thread-safe,
67656770
* we want to not be in the global locale */
67666771

6767-
{ /* Free up */
6768-
locale_t cur_obj = uselocale(LC_GLOBAL_LOCALE);
6769-
if (cur_obj != LC_GLOBAL_LOCALE && cur_obj != PL_C_locale_obj) {
6770-
freelocale(cur_obj);
6771-
}
6772+
/* Free up */
6773+
locale_t actual_obj = uselocale(LC_GLOBAL_LOCALE);
6774+
if (actual_obj != LC_GLOBAL_LOCALE && actual_obj != PL_C_locale_obj) {
6775+
freelocale(actual_obj);
67726776
}
67736777

6778+
/* Prevent leaks even if something has gone wrong */
6779+
locale_t expected_obj = PL_cur_locale_obj;
6780+
if (UNLIKELY( expected_obj != actual_obj
6781+
&& expected_obj != LC_GLOBAL_LOCALE
6782+
&& expected_obj != PL_C_locale_obj))
6783+
{
6784+
freelocale(expected_obj);
6785+
}
6786+
6787+
PL_cur_locale_obj = LC_GLOBAL_LOCALE;
6788+
67746789
#endif
67756790

67766791
}

makedef.pl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ sub readvar {
386386
++$skip{$_} foreach qw(
387387
PL_keyword_plugin_mutex
388388
PL_check_mutex
389+
PL_cur_locale_obj
389390
PL_op_mutex
390391
PL_regex_pad
391392
PL_regex_padav

perl.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,15 +1129,17 @@ perl_destruct(pTHXx)
11291129
{
11301130
/* This also makes sure we aren't using a locale object that gets freed
11311131
* below */
1132-
const locale_t old_locale = uselocale(LC_GLOBAL_LOCALE);
1133-
if ( old_locale != LC_GLOBAL_LOCALE
1134-
# ifdef USE_POSIX_2008_LOCALE
1135-
&& old_locale != PL_C_locale_obj
1136-
# endif
1132+
if ( PL_cur_locale_obj != NULL
1133+
&& PL_cur_locale_obj != LC_GLOBAL_LOCALE
1134+
&& PL_cur_locale_obj != PL_C_locale_obj
11371135
) {
1138-
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
1139-
"%s:%d: Freeing %p\n", __FILE__, __LINE__, old_locale));
1140-
freelocale(old_locale);
1136+
locale_t cur_locale = uselocale((locale_t) 0);
1137+
if (cur_locale == PL_cur_locale_obj) {
1138+
uselocale(LC_GLOBAL_LOCALE);
1139+
}
1140+
1141+
freelocale(PL_cur_locale_obj);
1142+
PL_cur_locale_obj = NULL;
11411143
}
11421144
}
11431145

sv.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15939,6 +15939,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
1593915939
#endif /* !USE_LOCALE_NUMERIC */
1594015940
#if defined(USE_POSIX_2008_LOCALE)
1594115941
PL_scratch_locale_obj = NULL;
15942+
PL_cur_locale_obj = PL_C_locale_obj;
1594215943
#endif
1594315944

1594415945
#ifdef HAS_MBRLEN

0 commit comments

Comments
 (0)