patrón de bloqueo doblemente verificado

In C ++ y los peligros del bloqueo de doble verificación, hay un persudocódigo para implementar el patrón correctamente, sugerido por los autores. Vea abajo

Singleton* Singleton::instance () {
    Singleton* tmp = pInstance;
    ... // insert memory barrier (1)
    if (tmp == 0) {
        Lock lock;
        tmp = pInstance;
        if (tmp == 0) {
            tmp = new Singleton;
            ... // insert memory barrier (2)
            pInstance = tmp;
        }
    }
    return tmp;
}

Me pregunto si la primera barrera de memoria se puede mover justo encima de la declaración de devolución.

EDITA: Otra pregunta: en el artículo vinculado, como vidstige citado

Técnicamente, no necesita barreras bidireccionales completas. La primera barrera debe evitar la migración hacia abajo de la construcción de Singleton (por otro hilo); la segunda barrera debe evitar la migración hacia arriba de la inicialización de pInstance. Estas se llaman operaciones "adquirir" y "liberar", y pueden producir un mejor rendimiento que las barreras completas en el hardware (como Itainum) que hace la distinción.

Dice que la segunda barrera no tiene que ser bidireccional, entonces, ¿cómo puede evitar que la asignación a la instancia se mueva antes de esa barrera? Aunque la primera barrera puede evitar la migración ascendente, otro hilo aún puede tener la oportunidad de ver los miembros no inicializados.

EDITA: Creo que casi entiendo el propósito de la primera barrera. Como sonicoder notado, la predicción de rama puede hacer que tmp sea NULL cuando el if devuelve verdadero. Para evitar ese problema, debe haber una barrera de adquisición para evitar la lectura de tmp a cambio antes de la lectura en if.

La primera barrera se combina con la segunda barrera para lograr sincronizar-con relación, por lo que se puede mover hacia abajo.

EDITA: Para aquellos que estén interesados en esta pregunta, les recomiendo leer memory-barreras.txt.

Respuestas a la pregunta(2)

Su respuesta a la pregunta