void a() { ... }
void b() { ... }
struct X
{
X() { b(); }
};
void f()
{
a();
static X x;
...
}
Supongamos que se llama varias veces desde varios subprocesos (potencialmente disputados) después de la entrada de main. (Y por supuesto que las únicas llamadas a a y b son los que se ve arriba)G ++ 4.6 -std = gnu ++ 0x: Constructor de variables locales estáticas Segmentación de llamadas y seguridad de subprocesos
Cuando el código anterior se compila con gcc g ++ 4,6 en -std = gnu ++ 0x modo:
Q1 . ¿Se garantiza que se llamará a() al menos una vez y se devolverá antes de llamar a b()? Es decir, en la primera llamada a f(), ¿es el constructor de x llamado al mismo tiempo una variable local de duración automática (no estática) (y no en el tiempo de inicialización estática global, por ejemplo)?
Q2. ¿Se garantiza que b() se llamará exactamente una vez? Incluso si dos hilos ejecutan f por primera vez al mismo tiempo en diferentes núcleos? En caso afirmativo, ¿mediante qué mecanismo específico el código generado por GCC proporciona sincronización? Editar: Además, ¿podría uno de los subprocesos llamar a f() obtener acceso a x antes de que el constructor de X regrese?
Actualización: Estoy tratando de compilar y descompilar un ejemplo para investigar el mecanismo ...
test.cpp:
struct X;
void ext1(int x);
void ext2(X& x);
void a() { ext1(1); }
void b() { ext1(2); }
struct X
{
X() { b(); }
};
void f()
{
a();
static X x;
ext2(x);
}
continuación:
$ g++ -std=gnu++0x -c -o test.o ./test.cpp
$ objdump -d test.o -M intel > test.dump
test.dump :
test.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_Z1av>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: bf 01 00 00 00 mov edi,0x1
9: e8 00 00 00 00 call e <_Z1av+0xe>
e: 5d pop rbp
f: c3 ret
0000000000000010 <_Z1bv>:
10: 55 push rbp
11: 48 89 e5 mov rbp,rsp
14: bf 02 00 00 00 mov edi,0x2
19: e8 00 00 00 00 call 1e <_Z1bv+0xe>
1e: 5d pop rbp
1f: c3 ret
0000000000000020 <_Z1fv>:
20: 55 push rbp
21: 48 89 e5 mov rbp,rsp
24: 41 54 push r12
26: 53 push rbx
27: e8 00 00 00 00 call 2c <_Z1fv+0xc>
2c: b8 00 00 00 00 mov eax,0x0
31: 0f b6 00 movzx eax,BYTE PTR [rax]
34: 84 c0 test al,al
36: 75 2d jne 65 <_Z1fv+0x45>
38: bf 00 00 00 00 mov edi,0x0
3d: e8 00 00 00 00 call 42 <_Z1fv+0x22>
42: 85 c0 test eax,eax
44: 0f 95 c0 setne al
47: 84 c0 test al,al
49: 74 1a je 65 <_Z1fv+0x45>
4b: 41 bc 00 00 00 00 mov r12d,0x0
51: bf 00 00 00 00 mov edi,0x0
56: e8 00 00 00 00 call 5b <_Z1fv+0x3b>
5b: bf 00 00 00 00 mov edi,0x0
60: e8 00 00 00 00 call 65 <_Z1fv+0x45>
65: bf 00 00 00 00 mov edi,0x0
6a: e8 00 00 00 00 call 6f <_Z1fv+0x4f>
6f: 5b pop rbx
70: 41 5c pop r12
72: 5d pop rbp
73: c3 ret
74: 48 89 c3 mov rbx,rax
77: 45 84 e4 test r12b,r12b
7a: 75 0a jne 86 <_Z1fv+0x66>
7c: bf 00 00 00 00 mov edi,0x0
81: e8 00 00 00 00 call 86 <_Z1fv+0x66>
86: 48 89 d8 mov rax,rbx
89: 48 89 c7 mov rdi,rax
8c: e8 00 00 00 00 call 91 <_Z1fv+0x71>
Disassembly of section .text._ZN1XC2Ev:
0000000000000000 <_ZN1XC1Ev>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 83 ec 10 sub rsp,0x10
8: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
c: e8 00 00 00 00 call 11 <_ZN1XC1Ev+0x11>
11: c9 leave
12: c3 ret
¿No veo el mecanismo de sincronización? ¿O se agrega en el tiempo de enlace?
Update2: Ok cuando me ligo lo puedo ver ...
400973: 84 c0 test %al,%al
400975: 75 2d jne 4009a4 <_Z1fv+0x45>
400977: bf 98 20 40 00 mov $0x402098,%edi
40097c: e8 1f fe ff ff callq 4007a0 <[email protected]>
400981: 85 c0 test %eax,%eax
400983: 0f 95 c0 setne %al
400986: 84 c0 test %al,%al
400988: 74 1a je 4009a4 <_Z1fv+0x45>
40098a: 41 bc 00 00 00 00 mov $0x0,%r12d
400990: bf a0 20 40 00 mov $0x4020a0,%edi
400995: e8 a6 00 00 00 callq 400a40 <_ZN1XC1Ev>
40099a: bf 98 20 40 00 mov $0x402098,%edi
40099f: e8 0c fe ff ff callq 4007b0 <[email protected]>
4009a4: bf a0 20 40 00 mov $0x4020a0,%edi
4009a9: e8 72 ff ff ff callq 400920 <_Z4ext2R1X>
4009ae: 5b pop %rbx
4009af: 41 5c pop %r12
4009b1: 5d pop %rbp
Rodea con __cxa_guard_acquire y __cxa_guard_release, hagan lo que hagan.
Q2: garantizado exactamente una vez. C++ 11 conoce los hilos y eso fue especificado en el estándar. En cuanto al mecanismo, ninguna idea, pero no puede ser muy diferente de un mutex;). –
Q2: garantizado (como otros ya se indica). P1: No apostaría por eso. Declarar una variable estática casi no forma parte del flujo del programa y los optimizadores son notorios descodificadores de código. De todos modos, odiaría mantener un código que dependa de sutilezas como esta. – stefaanv
@stefaanv: Incorrecto. El orden está garantizado tanto por el estándar como por la implementación. –