A resposta de Scheff descreve como corrigir seu código. Eu pensei em acrescentar um pouco de informação sobre o que realmente está acontecendo neste caso.
Compilei seu código no godbolt usando o nível de otimização 1 ( -O1
). Sua função compila da seguinte maneira:
func():
cmp BYTE PTR finished[rip], 0
jne .L4
.L5:
jmp .L5
.L4:
mov eax, 0
ret
Então o que está acontecendo aqui? Primeiro, temos uma comparação: cmp BYTE PTR finished[rip], 0
- isso verifica se finished
é falso ou não.
Se é não falsa (aka true) devemos sair do loop na primeira execução. Isto conseguido jne .L4
pelo qual j umps quando n ot e qua a etiqueta .L4
em que o valor de i
( 0
) é armazenada num registo para utilização posterior e a função retorna.
Se for falso, no entanto, passamos para
.L5:
jmp .L5
Este é um salto incondicional, para rotular .L5
que por acaso é o próprio comando de salto.
Em outras palavras, o encadeamento é colocado em um loop ocupado infinito.
Então, por que isso aconteceu?
No que diz respeito ao otimizador, os threads estão fora de seu alcance. Ele assume que outros threads não estão lendo ou gravando variáveis simultaneamente (porque isso seria UB de corrida de dados). Você precisa dizer que ele não pode otimizar acessos ausentes. É aqui que a resposta de Scheff entra. Não vou me incomodar em repeti-lo.
Como o otimizador não é informado de que a finished
variável pode potencialmente mudar durante a execução da função, ela vê que finished
não é modificada pela própria função e assume que é constante.
O código otimizado fornece os dois caminhos de código que resultarão da entrada na função com um valor bool constante; ou ele executa o loop infinitamente, ou o loop nunca é executado.
no -O0
compilador (como esperado) não otimiza o corpo do loop e a comparação:
func():
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], 0
.L148:
movzx eax, BYTE PTR finished[rip]
test al, al
jne .L147
add QWORD PTR [rbp-8], 1
jmp .L148
.L147:
mov rax, QWORD PTR [rbp-8]
pop rbp
ret
portanto, a função, quando não otimizada, funciona, a falta de atomicidade aqui normalmente não é um problema, porque o código e o tipo de dados são simples. Provavelmente, o pior que podemos encontrar aqui é um valor i
que está fora de um para o que deveria ser.
Um sistema mais complexo com estruturas de dados tem muito mais probabilidade de resultar em dados corrompidos ou execução incorreta.