G++ 4.6 -std=gnu++0x: Static Local Variable Constructor Call Timing and Thread Safety

后端 未结 2 1238
暖寄归人
暖寄归人 2021-01-12 18:46
void a() { ... }
void b() { ... }

struct X
{
    X() { b(); }
};

void f()
{
    a();
    static X x;
    ...
}

Assume f is called multiple times

相关标签:
2条回答
  • 2021-01-12 19:20

    As far as I know it is guaranteed that b is only called once. However, it is not guaranteed that the initialisation is performed thread safe, which means another thread could potentially work with a half/not initialized x. (That's kind of funny because static mutexes are basicly useless this way.)

    0 讨论(0)
  • 2021-01-12 19:24

    Q1. Yes. According to C++11, 6.7/4:

    such a variable is initialized the first time control passes through its declaration

    so it will be initialised after the first call to a().

    Q2. Under GCC, and any compiler that supports the C++11 thread model: yes, initialisation of local static variables is thread safe. Other compilers might not give that guarantee. The exact mechanism is an implementation detail. I believe GCC uses an atomic flag to indicate whether it's initialised, and a mutex to protect initialisation when the flag is not set, but I could be wrong. Certainly, this thread implies that it was originally implemented like that.

    UPDATE: your code does indeed contain the initialisation code. You can see it more clearly if you link it, and then disassemble the program, so that you can see which functions are being called. I also used objdump -SC to interleave the source and demangle C++ names. It uses internal locking functions __cxa_guard_acquire and __cxa_guard_release, to make sure only one thread executes the initialisation code.

      #void f()
      #{
      400724: push   rbp
      400725: mov    rbp,rsp
      400728: push   r13
      40072a: push   r12
      40072c: push   rbx
      40072d: sub    rsp,0x8
    
      # a();
      400731: call   400704 <a()>
    
      # static X x;
      # if (!guard) {
      400736: mov    eax,0x601050
      40073b: movzx  eax,BYTE PTR [rax]
      40073e: test   al,al
      400740: jne    400792 <f()+0x6e>
    
      #     if (__cxa_guard_acquire(&guard)) {
      400742: mov    edi,0x601050
      400747: call   4005c0 <__cxa_guard_acquire@plt>  
      40074c: test   eax,eax
      40074e: setne  al
      400751: test   al,al
      400753: je     400792 <f()+0x6e>
    
      #         // initialise x
      400755: mov    ebx,0x0
      40075a: mov    edi,0x601058
      40075f: call   4007b2 <X::X()>
    
      #         __cxa_guard_release(&guard);
      400764: mov    edi,0x601050
      400769: call   4005e0 <__cxa_guard_release@plt>
    
      #     } else {
      40076e: jmp    400792 <f()+0x6e>
    
      #         // already initialised
      400770: mov    r12d,edx
      400773: mov    r13,rax
      400776: test   bl,bl
      400778: jne    400784 <f()+0x60>
      40077a: mov    edi,0x601050
      40077f: call   4005f0 <__cxa_guard_abort@plt>
      400784: mov    rax,r13
      400787: movsxd rdx,r12d
      40078a: mov    rdi,rax
      40078d: 400610 <_Unwind_Resume@plt>
    
      #     }
      # }
      # ext2(x);
      400792: mov    edi,0x601058
      400797: call   4007d1 <_Z4ext2R1X>
      #}
    
    0 讨论(0)
提交回复
热议问题