This is the mail archive of the
mailing list for the libstdc++ project.
Re: "Interesting" behavior in std::locale
- From: James Benze <benzejaa at gmail dot com>
- To: Jonathan Wakely <jwakely at redhat dot com>
- Cc: libstdc++ at gcc dot gnu dot org
- Date: Fri, 31 Oct 2014 14:56:09 -0400
- Subject: Re: "Interesting" behavior in std::locale
- Authentication-results: sourceware.org; auth=none
- References: <CAFCb1eYNzFi3_1sCfirFWRFTezJv4VWURsOFPHrh-R0QLJ+pbQ at mail dot gmail dot com> <20141031183557 dot GK3033 at redhat dot com>
On Fri, Oct 31, 2014 at 2:35 PM, Jonathan Wakely <firstname.lastname@example.org> wrote:
> On 31/10/14 12:07 -0400, James Benze wrote:
>> Hey all:
>> I found some code in libstdc++ that, while I understand what it does,
>> I'm not understand why it's programmed the way it is, and I was hoping
>> someone could shed some light on the behavior...I was hoping someone
>> would be bored at work today and wouldn't mind spending the time to
>> satisfy my curiosity.
>> The function is std::locale::classic() (in locale_init.cc:256). It
>> seems to me to be a singleton pattern, but implemented in a strange
>> way. First it initializes _S_classic, using a normal singleton
>> initialization pattern. No big deal there. Then, it uses placement
>> new to create the new c_locale object and then return it. Every time.
>> So my question is, why wasn't this programmed with an if check to see
>> if the singleton is already initialized? I made a quick test program:
>> std::locale::_Impl* _S_classic;
>> typedef char fake_locale[sizeof(std::locale)]
>> __attribute__ ((aligned(__alignof__(std::locale))));
>> fake_locale c_locale;
>> std::locale * global = NULL;
>> int main()
>> *(new (&c_locale) std::locale(_S_classic));
>> return 0;
> I don't know the reason, but you'd need more than just an 'if' as
> presumably you'd also want to set 'global' to be non-null after the
> first call, which would need to be synchronized to avoid data races.
You're right about setting global, I accidentally omitted that in my
test. My bad. Regardless of that, it's simply more that happens
inside the if block, which only happens once anyway.
I was thinking that you were right about a data race, but I'm curious
as to why we don't have the same problem then with _S_global (in
local_init.cc). It's set in locale::_S_initialize_once() and doesn't
seem to have any include guards around it.
This was actually partially why I was so confused with
locale::classic()...it sets _S_classic in the more conventional
singleton pattern, and then does this weird thing for setting
c_locale. The multiple placement new pattern wouldn't work for
_S_classic obviously, since it seems to contain a reference count and
"will" change at points, unless the c_locale object, but since the
library developer did something different for locale::classic, I was
wondering if there was a purpose.
Regardless though, I would think that either _S_classic is also a
source for a possible data-race, or neither of them are.
I'm also pretty sure you could get around the data race side effects with
global = new (&c_locale) locale(_S_classic);
Concurrent writes on global is moot since every write is the same
value...the address of the c_locale array. A concurrent read and
write could be problematic, but the read would at least pick up
non-null, so we avoid the problem by not referencing it again, and
simply using the address of the c_locale array. Interleaved reads
before global is set gives us the same multiple placement new issue we
have now which is already a problem.
I pretty much hate this though, and would much rather use some sort of
atomic, were I actually implementing a change to the library. :-)