En el documento con el mismo título que el de esta pregunta, los autores describen cómo construir una operación CAS de varias palabras linealizable sin bloqueo utilizando solo un CAS de una sola palabra. Primero presentan la operación de doble comparación, intercambio único, RDCSS, de la siguiente manera:
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);
}
donde el RDCSSDescriptor_t
es una estructura con los siguientes campos:
a1
- dirección de la primera condicióno1
- valor esperado en la primera direccióna2
- dirección de la segunda condicióno2
- valor esperado en la segunda direcciónn2
- el nuevo valor que se escribirá en la segunda dirección
Este descriptor se crea e inicializa una vez en un subproceso que inicia la operación RDCSS: ningún otro subproceso tiene una referencia hasta que el primer CAS1 en la función RDCSS
tiene éxito, haciendo que el descriptor sea accesible (o activo en la terminología del documento).
La idea detrás del algoritmo es la siguiente: reemplace la segunda ubicación de memoria con un descriptor que diga lo que desea hacer. Luego, dado que el descriptor está presente, verifique la primera ubicación de la memoria para ver si su valor cambió. Si no es así, reemplace el descriptor en la segunda ubicación de memoria con el nuevo valor. De lo contrario, vuelva a establecer la segunda ubicación de memoria en el valor anterior.
Los autores no explican por qué la línea con el !!
comentario es necesaria dentro del artículo. Me parece que las CAS1
instrucciones en la Complete
función siempre fallarán después de esta verificación, siempre que no haya una modificación concurrente. Y si hubo una modificación concurrente entre la verificación y el CAS Complete
, entonces el hilo que realiza la verificación aún debería fallar con su CAS Complete
, ya que la modificación concurrente no debería usar el mismo descriptor d
.
Mi pregunta es: ¿se puede omitir la verificación en la función RDCSSS
, if (r == d->o2)...
con RDCSS aún manteniendo la semántica de una instrucción de doble comparación, intercambio único que es linealizable y sin bloqueo ? (línea con !!
comentario)
Si no, ¿puede describir el escenario en el que esta línea es realmente necesaria para garantizar la corrección?
Gracias.
fuente
Respuestas:
En un entorno de tiempo de ejecución concurrente, las cosas simples pueden parecer extrañas ... espero que esto pueda ayudar.
Tenemos un CAS1 ATÓMICO INCORPORADO que tiene esta semántica:
Necesitamos definir una función RDCSS ATÓMICA usando CAS1 y que tenga la siguiente semántica:
Intuitivamente: necesitamos cambiar CONCURRENTEMENTE el valor en addr2 solo si * addr1 == oldval1 ... si otro hilo lo está cambiando, podemos ayudar al otro hilo a completar la operación, entonces podemos volver a intentarlo.
Se utilizará la función RDCSS (ver artículo) para definir CASN. Ahora, definimos un Descriptor RDCSS de la siguiente manera:
Luego implementamos RDCSS de la siguiente manera:
RESPUESTA A SU PREGUNTA
Si omitimos STEP4, entonces la parte if (... && * addr1 == oldval1) * addr2 = newval2 de la semántica RDCSS nunca se ejecutará (... o mejor: puede ser ejecutada de manera impredecible por otros hilos que ayudan el actual).
Como señaló en su comentario, la condición si (res == d1-> oldval2) en STEP4 es innecesaria: incluso si lo omitimos, ambos CAS1 en Complete () fallarán porque * (d-> addr2)! = D . Su único propósito es evitar una llamada a la función.
Ejemplo T1 = hilo1, T2 = hilo2:
fuente
addr2
contiened2->newval2
. Pero, me parece que el CAS1Complete
simplemente fallará, porque espera que el valor anterior sea el descriptord1
: T1 no escribirá nada. ¿Derecho?