Dans l' article portant le même titre que celui de cette question, les auteurs décrivent comment construire une opération CAS multi-mots linéarisable non bloquante en utilisant uniquement un CAS en un seul mot. Ils introduisent d'abord l'opération de double comparaison-échange simple - RDCSS, comme suit:
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);
}
où RDCSSDescriptor_t
est une structure avec les champs suivants:
a1
- adresse de la première conditiono1
- valeur attendue à la première adressea2
- adresse de la deuxième conditiono2
- valeur attendue à la deuxième adressen2
- la nouvelle valeur à écrire à la deuxième adresse
Ce descripteur est créé et initialisé une fois dans un thread qui initie l'opération RDCSS - aucun autre thread n'y fait référence jusqu'à ce que le premier CAS1 de la fonction RDCSS
réussisse, ce qui rend le descripteur accessible (ou actif dans la terminologie du papier).
L'idée derrière l'algorithme est la suivante - remplacez le deuxième emplacement de mémoire par un descripteur indiquant ce que vous voulez faire. Puis, étant donné que le descripteur est présent, vérifiez le premier emplacement de mémoire pour voir si sa valeur a changé. Si ce n'est pas le cas, remplacez le descripteur au deuxième emplacement mémoire par la nouvelle valeur. Sinon, redéfinissez le deuxième emplacement de mémoire sur l'ancienne valeur.
Les auteurs n'expliquent pas pourquoi la ligne avec le !!
commentaire est nécessaire dans l'article. Il me semble que les CAS1
instructions de la Complete
fonction échoueront toujours après cette vérification, à condition qu'il n'y ait pas de modification simultanée. Et s'il y avait une modification simultanée entre la vérification et le CAS dans Complete
, alors le thread effectuant la vérification devrait toujours échouer avec son CAS dans Complete
, car la modification simultanée ne devrait pas utiliser le même descripteur d
.
Ma question est la suivante: la vérification de la fonction peut RDCSSS
-elle if (r == d->o2)...
être omise, RDCSS conservant toujours la sémantique d'une instruction de comparaison double et de permutation simple qui est linéarisable et sans verrouillage ? (ligne avec !!
commentaire)
Sinon, pouvez-vous décrire le scénario où cette ligne est réellement nécessaire pour garantir l'exactitude?
Je vous remercie.
la source
Réponses:
Dans un environnement d'exécution simultanée, des choses simples peuvent sembler étranges ... j'espère que cela peut vous aider.
Nous avons un CAS1 ATOMIQUE INTÉGRÉ ayant cette sémantique:
Nous devons définir une fonction ATOMIC RDCSS en utilisant CAS1 et ayant la sémantique suivante:
Intuitivement: nous devons CHANGER ENCORE LA valeur à addr2 seulement si * addr1 == oldval1 ... si un autre thread le change nous pouvons aider l'autre thread à terminer l'opération, alors nous pouvons réessayer.
La fonction RDCSS sera utilisée (voir article) pour définir CASN. Maintenant, nous définissons un descripteur RDCSS de la manière suivante:
Ensuite, nous implémentons RDCSS de la manière suivante:
RÉPONSE À VOTRE QUESTION
Si nous omettons STEP4, la partie if (... && * addr1 == oldval1) * addr2 = newval2 de la sémantique RDCSS ne sera jamais exécutée (... ou mieux: elle peut être exécutée de manière imprévisible par d'autres threads aidant l'actuel).
Comme vous l'avez souligné dans votre commentaire, la condition si (res == d1-> oldval2) à STEP4 n'est pas nécessaire: même si nous l'omettons, les deux CAS1 dans Complete () échoueront car * (d-> addr2)! = D . Son seul but est d'éviter un appel de fonction.
Exemple T1 = thread1, T2 = thread2:
la source
addr2
contientd2->newval2
. Mais, il me semble que le CAS1 dans l'Complete
échec échouera, car il s'attend à ce que l'ancienne valeur soit le descripteurd1
- rien ne sera écrit par T1. Droite?