diff options
author | Karl Williamson <khw@cpan.org> | 2016-07-20 10:33:40 -0600 |
---|---|---|
committer | Karl Williamson <khw@cpan.org> | 2016-07-29 15:46:46 -0600 |
commit | 6ebbc8624b039b6346d70b097fe51229b3938d1b (patch) | |
tree | 3a3dabd8ff5075596d355c003f470f2357440fa6 /locale.c | |
parent | 8ebda0e9b901456f365e0c5fbdbba0fef14054fe (diff) | |
download | perl-6ebbc8624b039b6346d70b097fe51229b3938d1b.tar.gz |
locale.c: Revamp my_strerror() for thread-safeness
This commit is the first step in making locale handling thread-safe.
[perl #127708] was solved for 5.24 by adding a mutex in this function.
That bug was caused by the code changing the locale even if the calling
program is not consciously using locales.
Posix 2008 introduced thread-safe locale functions. This commit changes
this function to use them if the perl is threaded and the platform has
them available. This means that the mutex is avoided on modern
platforms.
It restructures the function to return a mortal copy of the error
message. This is a step towards making the function completely thread
safe. Right now, as documented, if you do 'use locale', locale handling
isn't thread-safe.
A global C locale object is created and used here if necessary. It is
destroyed at the end of the program.
Note that some platforms have a strerror_r(), which is automatically
used instead of strerror() if available. It differs form straight
strerror() by taking a buffer to place the returned string, so the
return does not point to internal static storage. One could test for
the existence of this and avoid the mortal copy.
Diffstat (limited to 'locale.c')
-rw-r--r-- | locale.c | 95 |
1 files changed, 71 insertions, 24 deletions
@@ -2464,47 +2464,94 @@ Perl__is_in_locale_category(pTHX_ const bool compiling, const int category) } char * -Perl_my_strerror(pTHX_ const int errnum) { +Perl_my_strerror(pTHX_ const int errnum) +{ + /* Returns a mortalized copy of the text of the error message associated + * with 'errnum'. It uses the current locale's text unless the platform + * doesn't have the LC_MESSAGES category or we are not being called from + * within the scope of 'use locale'. In the former case, it uses whatever + * strerror returns; in the latter case it uses the text from the C locale. + * + * The function just calls strerror(), but temporarily switches, if needed, + * to the C locale */ + + char *errstr; + +#ifdef USE_LOCALE_MESSAGES /* If platform doesn't have messages category, we + don't do any switching to the C locale; we just + use whatever strerror() returns */ + const bool within_locale_scope = IN_LC(LC_MESSAGES); + dVAR; - /* Uses C locale for the error text unless within scope of 'use locale' for - * LC_MESSAGES */ +# ifdef USE_THREAD_SAFE_LOCALE + locale_t save_locale; +# else + char * save_locale; + bool locale_is_C; -#ifdef USE_LOCALE_MESSAGES - if (! IN_LC(LC_MESSAGES)) { - char * save_locale; + /* We have a critical section to prevent another thread from changing the + * locale out from under us (or zapping the buffer returned from + * setlocale() ) */ + LOCALE_LOCK; + +# endif + + if (! within_locale_scope) { - /* We have a critical section to prevent another thread from changing - * the locale out from under us (or zapping the buffer returned from - * setlocale() ) */ - LOCALE_LOCK; +# ifdef USE_THREAD_SAFE_LOCALE /* Use the thread-safe locale functions */ + + save_locale = uselocale(PL_C_locale_obj); + +# else /* Not thread-safe build */ save_locale = setlocale(LC_MESSAGES, NULL); - if (! isNAME_C_OR_POSIX(save_locale)) { - char *errstr; + locale_is_C = isNAME_C_OR_POSIX(save_locale); - /* The next setlocale likely will zap this, so create a copy */ - save_locale = savepv(save_locale); + /* Switch to the C locale if not already in it */ + if (! locale_is_C) { + /* The setlocale() just below likely will zap 'save_locale', so + * create a copy. */ + save_locale = savepv(save_locale); setlocale(LC_MESSAGES, "C"); + } - /* This points to the static space in Strerror, with all its - * limitations */ - errstr = Strerror(errnum); +# endif - setlocale(LC_MESSAGES, save_locale); - Safefree(save_locale); + } /* end of ! within_locale_scope */ - LOCALE_UNLOCK; +#endif - return errstr; - } + errstr = Strerror(errnum); + if (errstr) { + errstr = savepv(errstr); + SAVEFREEPV(errstr); + } + +#ifdef USE_LOCALE_MESSAGES + + if (! within_locale_scope) { - LOCALE_UNLOCK; +# ifdef USE_THREAD_SAFE_LOCALE + + uselocale(save_locale); } + +# else + + if (! locale_is_C) { + setlocale(LC_MESSAGES, save_locale); + Safefree(save_locale); + } + } + + LOCALE_UNLOCK; + +# endif #endif - return Strerror(errnum); + return errstr; } /* |