En répondant à cette question, une autre question au sujet de l'OP situation que je n'étais pas sûr au sujet: c'est surtout une architecture de processeur question, mais avec un effet d'entraînement sur la question sur le C++ 11 mémoire de ce modèle.
Fondamentalement, les OP du code était de boucler à l'infini à plus l'optimisation des niveaux de raison de le code suivant (légèrement modifié pour des raisons de simplicité):
while (true) {
uint8_t ov = bits_; // bits_ is some "uint8_t" non-local variable
if (ov & MASK) {
continue;
}
if (ov == __sync_val_compare_and_swap(&bits_, ov, ov | MASK)) {
break;
}
}
où __sync_val_compare_and_swap()
est GCC atomique de CAS intégré. GCC (raisonnablement) optimisé ce dans une boucle infinie dans le cas d' bits_ & mask
a été détecté à être true
avant d'entrer dans la boucle, en sautant, en CAS d'opération entièrement, j'ai donc proposé la modification suivante (qui fonctionne):
while (true) {
uint8_t ov = bits_; // bits_ is some "uint8_t" non-local variable
if (ov & MASK) {
__sync_synchronize();
continue;
}
if (ov == __sync_val_compare_and_swap(&bits_, ov, ov | MASK)) {
break;
}
}
Après j'ai répondu, OP noter que le changement de bits_
de volatile uint8_t
semble fonctionner aussi bien. Je suggère de ne pas aller dans cette voie, depuis volatile
ne devrait normalement pas être utilisé pour la synchronisation, et il ne semble pas être beaucoup inconvénient de l'utilisation d'une clôture ici de toute façon.
Cependant, j'y pensais plus, et dans ce cas, la sémantique est telle qu'il n'a pas vraiment d'importance si l' ov & MASK
contrôle est basé sur un état de la valeur, tant qu'il ne repose pas sur une indéfiniment rassis un (c'est à dire tant que la boucle est cassé par la suite), car la réelle tentative de mise à jour bits_
est synchronisé. Donc, est - volatile
assez de place pour garantir que cette boucle s'arrête finalement si bits_
est mise à jour par un autre thread, tels que bits_ & MASK == false
, pour toute inexistants processeur? En d'autres termes, en l'absence d'une mémoire explicite de la clôture, est-il possible pour ne lit pas optimisé par le compilateur pour être optimisé par le processeur au lieu de cela, indéfiniment? (EDIT: Pour être clair, je pose la question ici à propos de ce matériel moderne pourrait effectivement le faire compte tenu de l'hypothèse que les lectures sont émis dans une boucle par le compilateur, il n'est donc pas techniquement une langue en question, bien que l'exprimant en termes de C++ sémantique est commode.)
C'est le matériel de l'angle, mais de le mettre à jour légèrement et de le rendre aussi un responsable de la question sur le C++11 modèle de mémoire ainsi, considérer la variation suivante pour le code ci-dessus:
// bits_ is "std::atomic<unsigned char>"
unsigned char ov = bits_.load(std::memory_order_relaxed);
while (true) {
if (ov & MASK) {
ov = bits_.load(std::memory_order_relaxed);
continue;
}
// compare_exchange_weak also updates ov if the exchange fails
if (bits_.compare_exchange_weak(ov, ov | MASK, std::memory_order_acq_rel)) {
break;
}
}
cppreference prétend qu' std::memory_order_relaxed
implique "pas de contraintes sur la réorganisation de l'accès à la mémoire autour de la variable atomique", de manière indépendante de ce matériel sera ou ne sera pas, implique que bits_.load(std::memory_order_relaxed)
pourrait techniquement jamais lire une valeur mise à jour après l' bits_
est mis à jour sur un autre thread dans une mise en œuvre conforme?
EDIT: j'ai trouvé ça dans la norme (de 29,4 p13):
Implémentations devraient faire atomique magasins visible atomique des charges à l'intérieur d'un laps de temps raisonnable.
Donc, apparemment en attente "infiniment long" pour une mise à jour de la valeur est (surtout?) hors de question, mais il n'y a aucune garantie de quelque intervalle de temps spécifique de fraîcheur, d'autres que c'est doit être "raisonnable"; encore, la question sur le matériel réel le comportement des peuplements.