No artigo com o mesmo título que o desta pergunta, os autores descrevem como construir uma operação CAS linearizável de múltiplas palavras sem bloqueio usando apenas um CAS de uma única palavra. Eles primeiro introduzem a operação de dupla comparação, troca única - RDCSS, da seguinte maneira:
word_t RDCSS(RDCSSDescriptor_t *d) {
do {
r = CAS1(d->a2, d->o2, d);
if (IsDescriptor(r)) Complete(r);
} while (IsDescriptor(r));
if (r == d->o2) Complete(d); // !!
return r;
}
void Complete(RDCSSDescriptor_t *d) {
v = *(d->a1);
if (v == d->o1) CAS1(d->a2, d, d->n2);
else CAS1(d->a2, d, d->o2);
}
onde RDCSSDescriptor_t
é uma estrutura com os seguintes campos:
a1
- endereço da primeira condiçãoo1
- valor esperado no primeiro endereçoa2
- endereço da segunda condiçãoo2
- valor esperado no segundo endereçon2
- o novo valor a ser escrito no segundo endereço
Esse descritor é criado e inicializado uma vez em um encadeamento que inicia a operação RDCSS - nenhum outro encadeamento faz referência a ele até que o primeiro CAS1 da função seja RDCSS
bem-sucedido, tornando o descritor acessível (ou ativo na terminologia do artigo).
A idéia por trás do algoritmo é a seguinte - substitua o segundo local da memória por um descritor dizendo o que você deseja fazer. Em seguida, como o descritor está presente, verifique o primeiro local da memória para ver se seu valor foi alterado. Caso contrário, substitua o descritor no segundo local da memória pelo novo valor. Caso contrário, defina o segundo local da memória novamente para o valor antigo.
Os autores não explicam por que a linha com o !!
comentário é necessária no artigo. Parece-me que as CAS1
instruções na Complete
função sempre falham após essa verificação, desde que não haja modificação simultânea. E se houve uma modificação simultânea entre a verificação e a entrada do CAS Complete
, o segmento que está realizando a verificação ainda deve falhar com o seu CAS Complete
, pois a modificação simultânea não deve usar o mesmo descritor d
.
A minha pergunta é: Pode o check-in a função RDCSSS
, if (r == d->o2)...
ser omitido, com RDCSS ainda mantendo a semântica de um comparar única instrução dupla, troca que é linearizable e livre-lock ? (linha com !!
comentário)
Caso contrário, você pode descrever o cenário em que essa linha é realmente necessária para garantir a correção?
Obrigado.
fonte
Respostas:
Em um ambiente de tempo de execução simultâneo, coisas simples podem parecer estranhas ... espero que isso ajude.
Temos um CAS1 ATÔMICO INCORPORADO com esta semântica:
Precisamos definir uma função ATOMIC RDCSS usando CAS1 e ter a seguinte semântica:
Intuitivamente: precisamos alterar o valor em addr2 CONCURRENTE apenas se * addr1 == oldval1 ... se outro segmento estiver mudando, podemos ajudar o outro segmento a concluir a operação e tentar novamente.
A função RDCSS será usada (consulte o artigo) para definir o CASN. Agora, definimos um descritor RDCSS da seguinte maneira:
Em seguida, implementamos o RDCSS da seguinte maneira:
RESPOSTA À SUA PERGUNTA
Se omitirmos o STEP4, a parte if (... && * addr1 == oldval1) * addr2 = newval2 da semântica do RDCSS nunca será executada (... ou melhor: ela pode ser executada de maneira imprevisível por outros threads, ajudando o atual).
Conforme indicado por você no seu comentário, a condição se (res == d1-> oldval2) no PASSO 4 é desnecessária: mesmo se o omitirmos, o CAS1 em Complete () falhará porque * (d-> addr2)! = D . Seu único objetivo é evitar uma chamada de função.
Exemplo T1 = segmento1, T2 = segmento2:
fonte
addr2
contémd2->newval2
. Mas, parece-me que o CAS1 noComplete
falhará, porque espera que o valor antigo seja o descritord1
- nada será escrito por T1. Direita?