This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: "Interesting" behavior in std::locale


On Fri, Oct 31, 2014 at 2:35 PM, Jonathan Wakely <jwakely@redhat.com> 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:
>>
>> //with-if.cpp
>>
>> #include<locale>
>>
>> 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()
>> {
>>   if(!global)
>>      *(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

if(!global)
   global = new (&c_locale) locale(_S_classic);
   return global;
return *(locale*)(&c_locale);

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. :-)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]