diff options
author | Karl Williamson <khw@cpan.org> | 2018-03-09 12:53:13 -0700 |
---|---|---|
committer | Karl Williamson <khw@cpan.org> | 2018-03-12 10:22:01 -0600 |
commit | 69c5e0dbc1d307bce522321a107ca1670ec89f2c (patch) | |
tree | b35d1255f5a3464b9a6efdaae219ec001a34dc31 /ext | |
parent | 6470dfd24226b0307246dd4530b5acd43c2ec134 (diff) | |
download | perl-69c5e0dbc1d307bce522321a107ca1670ec89f2c.tar.gz |
Work around Microsoft threaded locale bug for localeconv()
Prior to Visual Studio 2015, the localeconv() function only looks at the
global locale, not the per-thread one it should. This works around this
by creating critical sections, switching to the global locale to call
localeconv(), then switching back. For the most common usage, it avoids
the switch by parsing a string it generates that should contain the
desired substring. This leaves the switch required for retrieving the
floating point grouping separator and the currency string, plus
POSIX::localeconv(). The first two could be avoided by extra code as
detailed in the pod for switch_to_global_locale(); patches welcome!
Diffstat (limited to 'ext')
-rw-r--r-- | ext/I18N-Langinfo/Langinfo.pm | 9 | ||||
-rw-r--r-- | ext/POSIX/POSIX.xs | 31 | ||||
-rw-r--r-- | ext/POSIX/lib/POSIX.pod | 4 |
3 files changed, 42 insertions, 2 deletions
diff --git a/ext/I18N-Langinfo/Langinfo.pm b/ext/I18N-Langinfo/Langinfo.pm index e9e84d28c2..14d3587ca5 100644 --- a/ext/I18N-Langinfo/Langinfo.pm +++ b/ext/I18N-Langinfo/Langinfo.pm @@ -241,6 +241,15 @@ By default only the C<langinfo()> function is exported. Before Perl 5.28, the returned values are unreliable for the C<RADIXCHAR> and C<THOUSEP> locale constants. +Starting in 5.28, changing locales on threaded builds is supported on systems +that offer thread-safe locale functions. These include POSIX 2008 systems and +Windows starting with Visual Studio 2005, and this module will work properly +in such situations. However, on threaded builds on Windows prior to Visual +Studio 2015, retrieving the items C<CRNCYSTR> and C<THOUSEP> can result in a +race with a thread that has converted to use the global locale. It is quite +uncommon for a thread to have done this. It would be possible to construct a +workaround for this; patches welcome: see L<perlapi/switch_to_global_locale>. + =head1 SEE ALSO L<perllocale>, L<POSIX/localeconv>, L<POSIX/setlocale>, L<nl_langinfo(3)>. diff --git a/ext/POSIX/POSIX.xs b/ext/POSIX/POSIX.xs index 764600c899..cf744c7ecf 100644 --- a/ext/POSIX/POSIX.xs +++ b/ext/POSIX/POSIX.xs @@ -2129,6 +2129,9 @@ localeconv() && defined(HAS_LOCALECONV_L) /* Prefer this thread-safe version */ bool do_free = FALSE; locale_t cur; +# elif defined(TS_W32_BROKEN_LOCALECONV) + const char * save_global; + const char * save_thread; # endif DECLARATION_FOR_LC_NUMERIC_MANIPULATION; @@ -2161,9 +2164,23 @@ localeconv() lcbuf = localeconv_l(cur); # else - LOCALE_LOCK; /* Prevent interference with other threads using + LOCALE_LOCK_V; /* Prevent interference with other threads using localeconv() */ +# ifdef TS_W32_BROKEN_LOCALECONV + /* This is a workaround for a Windows bug prior to VS 15, in which + * localeconv only looks at the global locale. We toggle to the global + * locale; populate the return; then toggle back. We have to use + * LC_ALL instead of the individual ones because of another bug in + * Windows */ + save_thread = savepv(Perl_setlocale(LC_NUMERIC, NULL)); + + _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); + + save_global = savepv(Perl_setlocale(LC_ALL, NULL)); + + Perl_setlocale(LC_ALL, save_thread); +# endif lcbuf = localeconv(); # endif if (lcbuf) { @@ -2223,7 +2240,17 @@ localeconv() freelocale(cur); } # else - LOCALE_UNLOCK; +# ifdef TS_W32_BROKEN_LOCALECONV + Perl_setlocale(LC_ALL, save_global); + + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + + Perl_setlocale(LC_ALL, save_thread); + + Safefree(save_global); + Safefree(save_thread); +# endif + LOCALE_UNLOCK_V; # endif RESTORE_LC_NUMERIC(); #endif /* HAS_LOCALECONV */ diff --git a/ext/POSIX/lib/POSIX.pod b/ext/POSIX/lib/POSIX.pod index c12aaefa63..a4989f77af 100644 --- a/ext/POSIX/lib/POSIX.pod +++ b/ext/POSIX/lib/POSIX.pod @@ -943,6 +943,10 @@ Prior to Perl 5.28, or when operating in a non thread-safe environment, It should not be used in a threaded application unless it's certain that the underlying locale is C or POSIX. This is because it otherwise changes the locale, which globally affects all threads simultaneously. +Windows platforms starting with Visual Studio 2005 are mostly +thread-safe, but use of this function in those prior to Visual Studio +2015 can interefere with a thread that has called +L<perlapi/switch_to_global_locale>. Here is how to query the database for the B<de> (Deutsch or German) locale. |