(x | y) - y por qué no puede ser simplemente x o incluso `x | 0`

47

Estaba leyendo un código del núcleo, y en un lugar he visto una expresión dentro de una ifdeclaración como

if (value == (SPINLOCK_SHARED | 1) - 1) {
         ............
}

donde SPINLOCK_SHARED = 0x80000000es una constante predefinida

Me pregunto por qué necesitamos (SPINLOCK_SHARED | 1) - 1, para fines de conversión de tipos. el resultado de la expresión sería 80000000-- igual que 0x80000000, ¿no es así? sin embargo, ¿por qué importa ORing 1 y Resta 1?

Tengo la sensación de que me falta conseguir algo ...

RaGa__M
fuente
3
#define SPINLOCK_SHARED 0x80000000
RaGa__M
1
Sospecho que no hay razón. Posiblemente una cosa de copiar y pegar. ¿Podría agregar dónde encontró exactamente esto (qué versión de qué núcleo, qué archivo, etc.).
Sander De Dycker
2
El mismo archivo de código fuente también contiene if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1)).
Eric Postpischil
2
Entonces creo que debemos preguntarle al autor por qué se cambió.
funnydman

Respuestas:

1

Simplemente se hizo así para mayor claridad, eso es todo. Es porque atomic_fetchadd_int () (por ejemplo, en sys / spinlock2.h) devuelve el valor ANTES de la suma / resta, y ese valor se pasa a _spin_lock_contested ()

Tenga en cuenta que el compilador de C calcula previamente todas las expresiones constantes. De hecho, el compilador puede incluso optimizar el código en línea basado en condicionales que usan argumentos de procedimiento pasados ​​cuando los procedimientos son constantes pasados ​​en esos argumentos. Es por eso que lockmgr () en línea en sys / lock.h tiene una declaración de caso ... porque toda la declaración de caso se optimizará y se convertirá en una llamada directa a la función apropiada.

Además, en todas estas funciones de bloqueo, la sobrecarga de las operaciones atómicas eclipsa todos los demás cálculos en dos o tres órdenes de magnitud.

-Mate

Matthew Dillon
fuente
Esta respuesta es del autor !!!
RaGa__M
31

El código se encuentra en _spin_lock_contested, que se llama _spin_lock_quickcuando alguien más intenta obtener el bloqueo:

count = atomic_fetchadd_int(&spin->counta, 1);
if (__predict_false(count != 0)) {
    _spin_lock_contested(spin, ident, count);
}

Si no hay concurso, entonces count(el valor anterior) debería serlo 0, pero no lo es. Este countvalor se pasa como parámetro a _spin_lock_contestedcomo valueparámetro. Esto valueluego se verifica con ifel OP:

/*
 * WARNING! Caller has already incremented the lock.  We must
 *      increment the count value (from the inline's fetch-add)
 *      to match.
 *
 * Handle the degenerate case where the spinlock is flagged SHARED
 * with only our reference.  We can convert it to EXCLUSIVE.
 */
if (value == (SPINLOCK_SHARED | 1) - 1) {
    if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED | 1, 1))
        return;
}

Teniendo en cuenta que ese valuees el valor anterior de spin->counta, y este último ya se ha incrementado en 1, esperamos spin->countaque sea igual value + 1(a menos que algo haya cambiado mientras tanto).

Por lo tanto, verificar si spin->counta == SPINLOCK_SHARED | 1(la condición previa de atomic_cmpset_int) corresponde a verificar si value + 1 == SPINLOCK_SHARED | 1, que puede reescribirse como value == (SPINLOCK_SHARED | 1) - 1(nuevamente, si nada ha cambiado mientras tanto).

Si bien value == (SPINLOCK_SHARED | 1) - 1podría reescribirse como value == SPINLOCK_SHARED, se deja como está, para aclarar la intención de la comparación (es decir, comparar el valor anterior incrementado con el valor de la prueba).

O iow. la respuesta parece ser: por claridad y consistencia del código.

Sander De Dycker
fuente
Gracias por su respuesta, todo, excepto la (SPINLOCK_SHARED | 1) - 1parte, es comprensible y también value == SPINLOCK_SHAREDlo creo, porque estamos verificando si el valor anterior tiene un conjunto de marcas compartidas. Si es así, convierta el bloqueo en exclusivo .........
RaGa__M
1
@RaGa__M: la intención de la ifverificación es verificar si value + 1(que debería ser el mismo valor que spin->countasi nada ha cambiado mientras tanto) es igual SPINLOCK_SHARED | 1. Si escribe el ifcheque como value == SPINLOCK_SHARED, esta intención no está clara, y sería mucho más difícil averiguar qué significa el cheque. Mantener ambos SPINLOCK_SHARED | 1y - 1explícitamente en el ifcheque es intencional.
Sander De Dycker
Pero en realidad está causando confusión.
RaGa__M
¿Por qué no if (value + 1 == (SPINLOCK_SHARED | 1) )?
Pablo H
Bueno ... simplemente podría ser lo value & SPINLOCK_SHAREDque es más legible.
RaGa__M
10

Creo que el objetivo es probablemente ignorar el bit significativo más bajo:

  • Si SPINLOCK_SHARED expresado en binario es xxx0 -> el resultado es xxx0
  • Si SPINLOCK_SHARED = xxx1 -> el resultado también es xxx0

habría sido quizás más claro usar una expresión de máscara de bits?

Guillaume Petitjean
fuente
8
Eso es lo que hace el código, pero la pregunta es ¿por qué haría eso para una constante definida que no tiene el conjunto de bits menos significativo?
Sander De Dycker
44
@SanderDeDycker ¿Porque el kernel de Linux?
Lundin
99
@Lundin El kernel de Linux no está exento de prácticas de codificación comprensibles. Todo lo contrario.
Qix - MONICA FUE MAL
2
@Qix Si tú lo dices. Era un gran admirador de Linux hasta que miré el código y leí el documento de estilo de codificación del núcleo. Hoy en día mantengo una distancia de seguridad de 10 metros para las computadoras Linux.
Lundin
2
@ Qix Nah, más bien lo estoy juzgando en función de su código fuente ...
Lundin
4

El efecto de

(SPINLOCK_SHARED | 1) - 1

es garantizar que el bit de orden inferior del resultado se borre antes de la comparación con value. Estoy de acuerdo en que parece bastante inútil, pero aparentemente el bit de bajo orden tiene un uso o significado particular que no es aparente en este código, y creo que debemos suponer que los desarrolladores tenían una buena razón para hacerlo. Una pregunta interesante sería: ¿se | 1) -1usa este mismo patrón ( ) en toda la base de código que está viendo?

Bob Jarvis - Restablece a Monica
fuente
2

Es una forma ofuscada de escribir una máscara de bits. Versión legible: value == (SPINLOCK_SHARED & ~1u).

Lundin
fuente
55
Sí, pero por qué . El OP pregunta por qué este sería el caso si SPINLOCK_SHAREDes una constante conocida. Si simplemente están probando SPINLOCK_SHAREDestar presentes en una máscara, ¿por qué no if (value & SPINLOCK_SHARED)?
Qix - MONICA FUE MAL
44
value == (SPINLOCK_SHARED & ~1u)no es equivalente porque value == (SPINLOCK_SHARED | 1) - 1funciona incluso si el tipo de SPINLOCK_SHAREDes más ancho que unsigned.
Eric Postpischil
44
Honestamente, no estoy seguro de que eso & ~1usea ​​más claro. Pensé en sugerir & 0xFFFFFFFEen mi respuesta, pero me di cuenta de que eso tampoco está realmente claro. Sin embargo, su sugerencia tiene la ventaja de la brevedad. :-)
Bob Jarvis - Restablece a Mónica el
66
@Lundin: No sabemos si lo será 0x80000000. OP ha declarado que está definido con #define SPINLOCK_SHARED 0x80000000, pero eso podría estar dentro #if…#endify se usa una definición diferente en otras circunstancias, o el autor de este código podría haber pensado que funcionara incluso si la definición se edita o el código se compila con otros encabezados que definirlo de manera diferente. En cualquier caso, las dos piezas de código no son equivalentes por sí mismas.
Eric Postpischil
2
@ BobJarvis-ReinstateMonica Es mucho más claro para las personas que trabajan con operadores bit a bit todos los días. Mezclar bit a bit con aritmética regular es confuso.
Lundin
0

La mayoría de esto se hace para manejar varios casos adicionales. Por ejemplo, en este caso, decimos que SPINLOCK_SHAREDno puede ser 1:

int SPINLOCK_SHARED = 0x01

int res = (SPINLOCK_SHARED | 1) - 1 // 0
divertido
fuente
2
Tendría sentido, pero, aunque en realidad no está claro a partir de la pregunta, parece que SPINLOCK_SHAREDes una constante definida y la variable probada lo es value. En este caso el misterio permanece.
Roberto Caboni
Gracias por su respuesta, pero creo que en el caso original SPINLOCK_SHARED no es 0x01, mantuvo la | 1) - 1parte aunque, cuando SPINLOCK_SHARED sostuvo 0x80000000¿cuál sería el impacto | 1) - 1?
RaGa__M
La única razón por la que se me ocurre es que querían evitar SPINLOCK_SHAREDser cambiados en el futuro. Pero no está nada claro. Escribiría en los desarrolladores del núcleo y pediría que se hiciera un comentario de aclaración o que la expresión se reorganice para que se documente.
Qix - MONICA FUE MAL