Home Dashboard Directory Help
Search

std::locale constructor modifies global locale via "setlocale()" by Andreas Neubacher


Status: 

Closed
 as Fixed Help for as Fixed


2
0
Sign in
to vote
Type: Bug
ID: 492128
Opened: 9/25/2009 6:45:37 AM
Access Restriction: Public
1
Workaround(s)
view
1
User(s) can reproduce this bug

Description

The std::locale constructor

    explicit locale(const char* _Locname, category _Cat = all);

will temporarily set the global locale to the locale being constructed.

This occurs in "C:\Program Files\Microsoft Visual Studio 9.0\VC\crt\src\locale.cpp" line 186, method "_Locinfo::_Locinfo_Addcats(_Locinfo *pLocinfo, int cat, const char *locname)" (called from method "_Locinfo::_Locinfo_ctor()"):

    else if (cat == _M_ALL)
        oldlocname = setlocale(LC_ALL, locname);

This is set back to the previous value in file "C:\Program Files\Microsoft Visual Studio 9.0\VC\crt\src\locale0.cpp" line 262, method "_Locinfo::_Locinfo_dtor(_Locinfo *pLocinfo)"

    {    // destroy a _Locinfo object, revert locale
    if (0 < pLocinfo->_Oldlocname.size())
        setlocale(LC_ALL, pLocinfo->_Oldlocname.c_str());
    }

This is problematic because any code accessing the global locale in another thread has a chance to temporarily use the wrong locale, namely the one being constructed on the other thread.

We have experienced this problem in the field.

I could not find any specification of the C++ standard that would allow the std::locale constructor to touch the global locale.

Note: I tested this only with the multi-threaded runtime DLL because that's what our products need to link with.
Details
Sign in to post a comment.
Posted by Microsoft on 7/27/2010 at 3:08 PM
Hi,

Thanks again for reporting this bug. We've fixed it by making std::locale use the same lock as setlocale(), and the fix will appear in VC11.

As always, if you have any further questions, feel free to E-mail me at stl@microsoft.com .

Stephan T. Lavavej
Visual C++ Libraries Developer
Posted by Eyal Farago on 5/12/2010 at 11:02 PM
we encountered this bug in visual studio 2005.
we had to construct a locale for code page 500 (".500") in runtime, once constructed operations like atol started failing all over the place.
we investigated into it (especially after seeing this web page) and it seems that the issue isn't so much the threading issue but the very specific locale for code page 500. it seems that _Locinfo::_Locinfo_dtor fails to restore the locale back to the original locale. the suggested workaround did fix the issue, it even enabled functions like atol to keep working in the same thread.
what bothers me is the fact that _Locinfo_dtor failed to restore the previous locale, my guess is that it also fails with the workaround enabled, but when returning to the 'non threaded global locale' the thread simply picks up the real global locale that was used by other threads. is it possible that the 'setlocale' function uses some locale aware functions to parse the locale name? if true, is it possible that an 'exotic' code page like 500 affects this parsing?

p.s. why not use this workaround in std::locale's constructor? seems like a very simple solution for the issue.

eyal.
Posted by Microsoft on 10/12/2009 at 6:58 PM
Hi,

Thanks for reporting this issue. We've resolved it as Won't Fix for VC10, for several reasons. The primary reason is that VC10 is very close to shipping, so changes are being subjected to a high level of scrutiny (as so little time remains for us to notice regressions). Longstanding issues with limited impact that require invasive changes to fix don't meet the bar at this time. Unfortunately, this issue is all of the above. Our C++ Standard Library implementation (licensed from Dinkumware) has worked like this for over 15 years, so this issue isn't a regression from previous releases. It's specific to std::locale interacting with setlocale(). And making std::locale not call setlocale() would require extensive rewriting, if it was possible in the first place.

The underlying problem is that std::locale protects its call to setlocale() with a lock, which is used by the rest of the C++ Standard Library implementation. However, our C Standard Library implementation doesn't use this lock, so direct calls to setlocale() bypass it. This is similar to an issue fixed earlier during VC10's development, where printf() and std::cout didn't use the same locks, and hence couldn't be used simultaneously by multiple threads. I've marked this issue for investigation during VC11's development, as we may be able to apply the same kind of fix.

Possible workarounds include using _configthreadlocale() to enable per-thread locales, calling std::locale::global() instead of setlocale(), or protecting your calls to std::locale's constructor and setlocale() with the same lock. I believe that std::locale::global() would be the best workaround, as it uses the STL's lock.

If you have any further questions, feel free to E-mail me at stl@microsoft.com .

Stephan T. Lavavej
Visual C++ Libraries Developer
Posted by Microsoft on 9/28/2009 at 10:53 PM
Thanks for your feedback.

We are rerouting this issue to the appropriate group within the Visual Studio Product Team for triage and resolution. These specialized experts will follow-up with your issue.

Thank you
Sign in to post a workaround.
Posted by Andreas Neubacher on 9/25/2009 at 7:14 AM
The constructor can be wrapped as follows:

    int iPreviousFlag = ::_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
    std::locale newLoc("German_germany");
    if (iPreviousFlag > 0 )
        ::_configthreadlocale(iPreviousFlag);

This is fairly ugly and inefficient, but should keep other threads from seeing the temporary change to the global locale.