summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorKarl Williamson <khw@cpan.org>2018-03-09 12:53:13 -0700
committerKarl Williamson <khw@cpan.org>2018-03-12 10:22:01 -0600
commit69c5e0dbc1d307bce522321a107ca1670ec89f2c (patch)
treeb35d1255f5a3464b9a6efdaae219ec001a34dc31 /ext
parent6470dfd24226b0307246dd4530b5acd43c2ec134 (diff)
downloadperl-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.pm9
-rw-r--r--ext/POSIX/POSIX.xs31
-rw-r--r--ext/POSIX/lib/POSIX.pod4
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.