2011-06-24 9 views
5

En nuestro producto tenemos una implementación de mutex en línea, utilizando una variedad de métodos específicos de compilación y plataforma para las piezas específicas del hardware. Una de nuestras "reglas" para un código sobre-optimizado que intenta "engañar", es que si se accede a una variable fuera del mutex y dentro de ella, entonces esa variable debe declararse como volátil. Pensé que esto también se aplicaba a las implementaciones mutex opacas (como pthread_mutex_lock/unlock), y esto condujo a un debate interesante.barrera volátil frente a compilador con montaje en línea gcc

Ha sido afirmado por una persona que esto es una indicación de un error del compilador (especialmente cuando la implementación de mutex está en línea y "no opaca" para el compilador). Me dio el siguiente ejemplo para disputar este

int v = pSharedMem->myVariable ; 

__asm__ __volatile__(("isync" : : :"memory")) 

v = pSharedMem->myVariable ; 

En este fragmento de código LinuxPPC gcc, el compilador no tiene ningún conocimiento de los efectos del tiempo de ejecución isync, aparte de lo que podemos decir que a través de la restricción de la memoria . Encontrará una instrucción tal sincronizada en el extremo posterior de una adquisición mutex para evitar la ejecución de las instrucciones que siguen a la adquisición exitosa del mutex antes de que realmente se mantuviera el mutex (de modo que si una carga se hubiera ejecutado antes de la sincronización lo haría tiene que ser descartado).

En este fragmento de código, que tienen la barrera compilador que impide una reescritura del código como si fueron los siguientes

int v = pSharedMem->myVariable ; 
v = pSharedMem->myVariable ; 

__asm__ __volatile__(("isync" : : :"memory")) 

o

__asm__ __volatile__(("isync" : : :"memory")) 

int v = pSharedMem->myVariable ; 
v = pSharedMem->myVariable ; 

(es decir: ambos compilador re -los pedidos deben ser inhibidos por el volátil atributo)

También tenemos la propia sincronización que impide la primera reordenación de un t tiempo de ejecución (pero no creo que previene el segundo, que no es tan interesante).

Sin embargo, mi pregunta es que si es myVariable no declarado volátil, sea o no la restricción de "memoria" es suficiente que gcc necesariamente volver a la carga "v" después de la isync? Todavía me inclinaría a exigir volatilidad para ese patrón ya que este tipo de código es demasiado delicado con todos los compiladores del compilador específicos de la plataforma. Dicho esto, si reducimos la discusión a solo GCC y este fragmento de código, ¿esta restricción de memoria asm es suficiente para tener un código que se genera con un par de cargas en lugar de solo uno?

Respuesta

2

El __asm__ __volatile__ con "memory" clobber es necesario y actuará como barrera de reordenación completa. volatile en la variable es innecesario. De hecho, si nos fijamos en la definición del núcleo de Linux de atomic_t, no utiliza ningún modificador volatile y se basa completamente en las declaraciones __asm__ __volatile__ con restricciones apropiadas.

Por otro lado, creo que volatile por sí solo no prohíbe el reordenamiento en absoluto, solo almacena en caché y optimiza el valor por completo, por lo que no tiene valor para la sincronización.

+0

Sí, pero no estoy hablando de volatilidad en la palabra atómica o de bloqueo, sino en los datos protegidos por el mutex. Tampoco estoy implicando que el volátil tiene ningún valor de sincronización (es decir: la sincronización es proporcionada por el mutex y me pregunto acerca de los datos que están protegidos por el mutex). Si también se accede a los datos fuera del mutex, es necesario volátil para obligar a volver a cargar el valor una vez que se adquiere el mutex. –

+0

Aquí hay un contexto adicional (la discusión original que generó esta pregunta) http://peeterjoot.wordpress.com/2011/06/21/an-unusual-voile-required-example/ –

+0

@Peeter: Estoy casi seguro de que la muestra está equivocada. Cualquier llamada de función no forzada siempre obliga a recuperar datos no locales de la memoria, porque el compilador no puede estar seguro de que la función no tenga un simple puntero a esos datos y simplemente los escriba, lo que puede ocurrir en un caso de subproceso único y ahí se define la semántica. Por otro lado, ese código es incorrecto con o sin volátil, porque las lecturas no son atómicas en algunas plataformas. Debe usar operaciones atómicas sincronizadas especiales cuando acceda a variables compartidas sin bloqueo. –

Cuestiones relacionadas