Examinez le code condensé suivant:Pourquoi __sync_add_and_fetch fonctionne-t-il pour une variable 64 bits sur un système 32 bits?
/* Compile: gcc -pthread -m32 -ansi x.c */
#include <stdio.h>
#include <inttypes.h>
#include <pthread.h>
static volatile uint64_t v = 0;
void *func (void *x) {
__sync_add_and_fetch (&v, 1);
return x;
}
int main (void) {
pthread_t t;
pthread_create (&t, NULL, func, NULL);
pthread_join (t, NULL);
printf ("v = %"PRIu64"\n", v);
return 0;
}
J'ai une variable uint64_t
que je veux incrémenter atomiquement, car la variable est un compteur dans un programme multi-thread. Pour atteindre l'atomicité, j'utilise les codes atomic builtins de GCC.
Si je compile pour un système amd64 (-m64), le code assembleur produit est facile à comprendre. En utilisant un lock addq
, le processeur garantit que l'incrément sera atomique.
400660: f0 48 83 05 d7 09 20 lock addq $0x1,0x2009d7(%rip)
Mais le même code C produit un code ASM très compliqué sur un système ia32 (-m32):
804855a: a1 28 a0 04 08 mov 0x804a028,%eax
804855f: 8b 15 2c a0 04 08 mov 0x804a02c,%edx
8048565: 89 c1 mov %eax,%ecx
8048567: 89 d3 mov %edx,%ebx
8048569: 83 c1 01 add $0x1,%ecx
804856c: 83 d3 00 adc $0x0,%ebx
804856f: 89 ce mov %ecx,%esi
8048571: 89 d9 mov %ebx,%ecx
8048573: 89 f3 mov %esi,%ebx
8048575: f0 0f c7 0d 28 a0 04 lock cmpxchg8b 0x804a028
804857c: 08
804857d: 75 e6 jne 8048565 <func+0x15>
Voici ce que je ne comprends pas:
lock cmpxchg8b
garantit que la variable modifiée n'est écrite que si la valeur attendue réside toujours dans l'adresse cible. Le compare-and-swap est garanti pour arriver atomiquement.- Mais ce qui garantit que la lecture de la variable dans 0x804855a et 0x804855f être atomique?
Probablement qu'il n'a pas d'importance s'il y avait une « lecture sale », mais quelqu'un pourrait s'il vous plaît décrire une courte preuve qu'il n'y a pas de problème? En outre: Pourquoi le code généré revient-il à 0x8048565 et non 0x804855a? Je suis certain que cela n'est correct que si d'autres auteurs, eux aussi, incrémentent la variable. Est-ce une exigence implicite pour la fonction __sync_add_and_fetch
?