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]

"Interesting" behavior in std::locale


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;
}

//no-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;

int main()
{
   *(new (&c_locale) std::locale(_S_classic));
   return 0;
}

Note that I had to hack a constructor of std::locale to be public for
the test.  This gives me the following (relevant) assembly output:

;with-if.s
main:
.LFB1000:
    .cfi_startproc
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movq    global(%rip), %rax
    testq    %rax, %rax
    jne    .L4
    movl    $c_locale, %eax
    movq    %rax, %rsi
    movl    $8, %edi
    call    _ZnwmPv
    testq    %rax, %rax
    je    .L4
    movq    _S_classic(%rip), %rdx
    movq    %rdx, %rsi
    movq    %rax, %rdi
    call    _ZNSt6localeC1EPNS_5_ImplE
.L4:
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

;no-if.s

main:
.LFB1000:
    .cfi_startproc
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $c_locale, %eax
    movq    %rax, %rsi
    movl    $8, %edi
    call    _ZnwmPv
    testq    %rax, %rax
    je    .L5
    movq    _S_classic(%rip), %rdx
    movq    %rdx, %rsi
    movq    %rax, %rdi
    call    _ZNSt6localeC1EPNS_5_ImplE
.L5:
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

Which seems like a pretty significant reduction to me when the jump
occurs, especially considering that the call to _ZnwmPv (not pasted
here for fear of making this email even longer) is 6 instructions long
as well.

I found this code because helgrind (valgrind's data-race finder) lit
up like a lightbulb with all these placement-new calls to the same
array.  It's obviously safe...the locale returned by
std::locale::classic is guaranteed never to change so it's essentially
just writing the same chunk of memory over and over.

So it seems that, if we had a normal if-check and pointer return we
would save on instructions, have code with a more obvious intent, and,
while the would still cause false positives in data-race detectors, it
would cause them with less frequency...only when originally setting
the pointer.

So I can't figured out specifically why it was coded this way, except
maybe "it works and no one has had any reason to change it".  Is that
it, or am I missing something else?


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