Skip to content

Commit f49f492

Browse files
committed
Switch libc per-interpreter data when tTHX changes
As noted in the previous commit, some library functions now keep per-thread state. So far the only ones we care about are libc locale-changing ones. When perl changes threads by swapping out tTHX, those library functions need to be informed about the new value so that they remain in sync with what perl thinks the locale should be. This commit creates a function to do this, and changes the thread-changing macros to also call this as part of the change. For POSIX 2008, the function just calls uselocale() using the per-interpreter object introduced previously. For Windows, this commit adds a per-interpreter string of the current LC_ALL, and the function calls setlocale on that. We keep the same string for POSIX 2008 implementations that lack querylocale(), so this commit just enables that variable on Windows as well. The code is already in place to free the memory the string occupies when done. The commit also creates a mechanism to skip this during thread destruction. A thread in its death throes doesn't need to have accurate locale information, and the information needed to map from thread to what libc needs to know gets destroyed as part of those throes, while relics of the thread remain. I couldn't find a way to accurately know if we are dealing with a relic or not, so the solution I adopted was to just not switch during destruction. This commit completes fixing #20155.
1 parent 82f6bac commit f49f492

File tree

9 files changed

+111
-3
lines changed

9 files changed

+111
-3
lines changed

dist/threads/threads.xs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,20 @@ S_ithread_clear(pTHX_ ithread *thread)
241241
S_block_most_signals(&origmask);
242242
#endif
243243

244+
int save_veto = PL_veto_switch_non_tTHX_context;
245+
244246
interp = thread->interp;
245247
if (interp) {
246248
dTHXa(interp);
247249

250+
/* We will pretend to be a thread that we are not by switching tTHX,
251+
* which doesn't work with things that don't rely on tTHX during
252+
* tear-down, as they will tend to rely on a mapping from the tTHX
253+
* structure, and that structure is being destroyed. */
254+
PL_veto_switch_non_tTHX_context = true;
255+
248256
PERL_SET_CONTEXT(interp);
257+
249258
S_ithread_set(aTHX_ thread);
250259

251260
SvREFCNT_dec(thread->params);
@@ -262,6 +271,8 @@ S_ithread_clear(pTHX_ ithread *thread)
262271
}
263272

264273
PERL_SET_CONTEXT(aTHX);
274+
PL_veto_switch_non_tTHX_context = save_veto;
275+
265276
#ifdef THREAD_SIGNAL_BLOCKING
266277
S_set_sigmask(&origmask);
267278
#endif

embed.fnc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,6 +1665,9 @@ Apd |void |switch_to_global_locale
16651665
Apd |bool |sync_locale
16661666
Apx |void |thread_locale_init
16671667
Apx |void |thread_locale_term
1668+
#ifdef USE_PERL_SWITCH_LOCALE_CONTEXT
1669+
CopT |void |switch_locale_context
1670+
#endif
16681671
ApdO |void |require_pv |NN const char* pv
16691672
Apd |void |packlist |NN SV *cat|NN const char *pat|NN const char *patend|NN SV **beglist|NN SV **endlist
16701673
#if defined(PERL_USES_PL_PIDSTATUS) && defined(PERL_IN_UTIL_C)

locale.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2592,7 +2592,6 @@ S_win32_setlocale(pTHX_ int category, const char* locale)
25922592
* use the particular category's variable if set; otherwise to use the LANG
25932593
* variable. */
25942594

2595-
25962595
if (locale == NULL) {
25972596
return wrap_wsetlocale(category, NULL);
25982597
}
@@ -2606,6 +2605,20 @@ S_win32_setlocale(pTHX_ int category, const char* locale)
26062605
const char * result = wrap_wsetlocale(category, locale);
26072606
DEBUG_L(PerlIO_printf(Perl_debug_log, "%s\n",
26082607
setlocale_debug_string_r(category, locale, result)));
2608+
2609+
# ifdef USE_PL_CUR_LC_ALL
2610+
2611+
/* If we need to keep track of LC_ALL, update it to the new value. */
2612+
Safefree(PL_cur_LC_ALL);
2613+
if (category == LC_ALL) {
2614+
PL_cur_LC_ALL = savepv(result);
2615+
}
2616+
else {
2617+
PL_cur_LC_ALL = savepv(setlocale(LC_ALL, NULL));
2618+
}
2619+
2620+
# endif
2621+
26092622
return result;
26102623
}
26112624

@@ -6718,6 +6731,46 @@ S_my_setlocale_debug_string_i(pTHX_
67186731
retval_quote, retval, retval_quote);
67196732
}
67206733

6734+
#endif
6735+
#ifdef USE_PERL_SWITCH_LOCALE_CONTEXT
6736+
6737+
void
6738+
Perl_switch_locale_context()
6739+
{
6740+
/* libc keeps per-thread locale status information in some configurations.
6741+
* So, we can't just switch out aTHX to switch to a new thread. libc has
6742+
* to follow along. This routine does that based on per-interpreter
6743+
* variables we keep just for this purpose */
6744+
6745+
/* Can't use pTHX, because we may be called from a place where that
6746+
* isn't available */
6747+
dTHX;
6748+
6749+
if (UNLIKELY( aTHX == NULL
6750+
|| PL_veto_switch_non_tTHX_context
6751+
|| PL_phase == PERL_PHASE_CONSTRUCT))
6752+
{
6753+
return;
6754+
}
6755+
6756+
# ifdef USE_POSIX_2008_LOCALE
6757+
6758+
if (! uselocale(PL_cur_locale_obj)) {
6759+
locale_panic_(Perl_form(aTHX_
6760+
"Can't uselocale(%p), LC_ALL supposed to be '%s",
6761+
PL_cur_locale_obj, get_LC_ALL_display()));
6762+
}
6763+
6764+
# elif defined(WIN32)
6765+
6766+
if (! bool_setlocale_c(LC_ALL, PL_cur_LC_ALL)) {
6767+
locale_panic_(Perl_form(aTHX_ "Can't setlocale(%s)", PL_cur_LC_ALL));
6768+
}
6769+
6770+
# endif
6771+
6772+
}
6773+
67216774
#endif
67226775

67236776
void

makedef.pl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ BEGIN
177177

178178
if ($ARGS{PLATFORM} eq 'win32' && $define{USE_THREAD_SAFE_LOCALE})
179179
{
180+
$define{USE_PL_CUR_LC_ALL} = 1;
181+
180182
if ($cctype < 140) {
181183
$define{TS_W32_BROKEN_LOCALECONV} = 1;
182184
}
@@ -400,6 +402,7 @@ sub readvar {
400402
PL_stashpad
401403
PL_stashpadix
402404
PL_stashpadmax
405+
PL_veto_switch_non_tTHX_context
403406
Perl_alloccopstash
404407
Perl_allocfilegv
405408
Perl_clone_params_del

perl.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,7 @@ violations are fatal.
12391239
# if defined(USE_LOCALE_THREADS) && ! defined(NO_THREAD_SAFE_LOCALE)
12401240
# if defined(USE_POSIX_2008_LOCALE) || (defined(WIN32) && defined(_MSC_VER))
12411241
# define USE_THREAD_SAFE_LOCALE
1242+
# define USE_PERL_SWITCH_LOCALE_CONTEXT
12421243
# endif
12431244
# endif
12441245

@@ -1272,6 +1273,11 @@ violations are fatal.
12721273

12731274
# if defined(WIN32) && defined(USE_THREAD_SAFE_LOCALE)
12741275

1276+
/* We need to be able to map the current value of what the tTHX context
1277+
* thinks LC_ALL is so as to inform the Windows libc when switching
1278+
* contexts. */
1279+
# define USE_PL_CUR_LC_ALL
1280+
12751281
/* Microsoft documentation reads in the change log for VS 2015: "The
12761282
* localeconv function declared in locale.h now works correctly when
12771283
* per-thread locale is enabled. In previous versions of the library, this
@@ -4046,7 +4052,10 @@ out there, Solaris being the most prominent.
40464052

40474053
/* the traditional thread-unsafe notion of "current interpreter". */
40484054
#ifndef PERL_SET_INTERP
4049-
# define PERL_SET_INTERP(i) (PL_curinterp = (PerlInterpreter*)(i))
4055+
# define PERL_SET_INTERP(i) \
4056+
STMT_START { PL_curinterp = (PerlInterpreter*)(i); \
4057+
PERL_SET_NON_tTHX_CONTEXT(i); \
4058+
} STMT_END
40504059
#endif
40514060

40524061
#ifndef PERL_GET_INTERP
@@ -6273,6 +6282,24 @@ EXTCONST U8 PL_deBruijn_bitpos_tab64[];
62736282
# define PERL_SET_CONTEXT(i) PERL_SET_INTERP(i)
62746283
#endif
62756284

6285+
#ifdef USE_PERL_SWITCH_LOCALE_CONTEXT
6286+
# define PERL_SET_LOCALE_CONTEXT(i) \
6287+
STMT_START { \
6288+
if (UNLIKELY(PL_veto_switch_non_tTHX_context)) \
6289+
Perl_switch_locale_context(); \
6290+
} STMT_END
6291+
#else
6292+
# define PERL_SET_LOCALE_CONTEXT(i) NOOP
6293+
#endif
6294+
6295+
/* In some Configurations there may be per-thread information that is carried
6296+
* in a library instead of perl's tTHX structure. This macro is to be used to
6297+
* handle those when tTHX is changed. Only locale handling is currently known
6298+
* to be affected. */
6299+
#define PERL_SET_NON_tTHX_CONTEXT(i) \
6300+
STMT_START { PERL_SET_LOCALE_CONTEXT(i); } STMT_END
6301+
6302+
62766303
#ifndef PERL_GET_CONTEXT
62776304
# define PERL_GET_CONTEXT PERL_GET_INTERP
62786305
#endif
@@ -7883,7 +7910,9 @@ C<strtoul>.
78837910
* "DynaLoader::_guts" XS_VERSION
78847911
* XXX in the current implementation, this string is ignored.
78857912
* 2. Declare a typedef named my_cxt_t that is a structure that contains
7886-
* all the data that needs to be interpreter-local.
7913+
* all the data that needs to be interpreter-local that perl controls. This
7914+
* doesn't include things that libc controls, such as the uselocale object
7915+
* in Configurations that use it.
78877916
* 3. Use the START_MY_CXT macro after the declaration of my_cxt_t.
78887917
* 4. Use the MY_CXT_INIT macro such that it is called exactly once
78897918
* (typically put in the BOOT: section).

perlvars.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ PERLVAR(G, check_mutex, perl_mutex) /* Mutex for PL_check */
169169
#ifdef MULTIPLICITY
170170
# ifdef USE_ITHREADS
171171
PERLVAR(G, my_ctx_mutex, perl_mutex)
172+
PERLVARI(G, veto_switch_non_tTHX_context, int, FALSE)
172173
# endif
173174
PERLVARI(G, my_cxt_index, int, 0)
174175
#endif

proto.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7661,6 +7661,10 @@ PERL_CALLCONV SSize_t Perl_PerlIO_write(pTHX_ PerlIO *f, const void *vbuf, Size_
76617661
#define PERL_ARGS_ASSERT_PERLIO_WRITE \
76627662
assert(vbuf)
76637663
#endif
7664+
#if defined(USE_PERL_SWITCH_LOCALE_CONTEXT)
7665+
PERL_CALLCONV void Perl_switch_locale_context(void);
7666+
#define PERL_ARGS_ASSERT_SWITCH_LOCALE_CONTEXT
7667+
#endif
76647668
#if defined(USE_QUADMATH)
76657669
PERL_CALLCONV bool Perl_quadmath_format_needed(const char* format)
76667670
__attribute__visibility__("hidden");

thread.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ extern PERL_THREAD_LOCAL void *PL_current_context;
404404
PL_current_context = (void *)(t)))) \
405405
Perl_croak_nocontext("panic: pthread_setspecific (%d) [%s:%d]", \
406406
_eC_, __FILE__, __LINE__); \
407+
PERL_SET_NON_tTHX_CONTEXT(t); \
407408
} STMT_END
408409

409410
#else

util.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3740,6 +3740,9 @@ Perl_set_context(void *t)
37403740
Perl_croak_nocontext("panic: pthread_setspecific, error=%d", error);
37413741
}
37423742
# endif
3743+
3744+
PERL_SET_NON_tTHX_CONTEXT(t);
3745+
37433746
#else
37443747
PERL_UNUSED_ARG(t);
37453748
#endif

0 commit comments

Comments
 (0)