(x | y) - y por que não pode ser simplesmente x ou mesmo `x | 0`


47

Eu estava lendo um código do kernel e, em um lugar, vi uma expressão dentro de uma ifinstrução como

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

onde SPINLOCK_SHARED = 0x80000000é uma constante predefinida.

Gostaria de saber por que precisamos (SPINLOCK_SHARED | 1) - 1- para fins de conversão de tipo? o resultado da expressão seria 80000000-- o mesmo que 0x80000000, não é? ainda, por que ORing 1 e Subtração 1 são importantes?

Tenho a sensação de que estou sentindo falta de algo ..


3
#define SPINLOCK_SHARED 0x80000000
RaGa__M

11
Eu suspeito que não há razão. Possivelmente uma coisa de copiar e colar. Você poderia adicionar exatamente onde você encontrou isso (qual versão de qual kernel, qual arquivo etc.).
Sander De Dycker


2
O mesmo arquivo de código-fonte também contém if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1)).
Eric Postpischil

2
Então acho que precisamos perguntar ao autor por que isso foi alterado.
funnydman

Respostas:


1

Foi assim feito para maior clareza, só isso. Isso ocorre porque atomic_fetchadd_int () (por exemplo, sys / spinlock2.h) retorna o valor ANTES da adição / subtração, e esse valor é passado para _spin_lock_contested ()

Observe que o compilador C pré-calcula completamente todas as expressões constantes. De fato, o compilador pode até otimizar o código embutido com base em condicionais que usam argumentos de procedimentos passados ​​quando os procedimentos são passados ​​constantes nesses argumentos. É por isso que o lockmgr () inline em sys / lock.h possui uma instrução de caso .... porque toda a instrução de caso será otimizada e evoluirá para uma chamada direta para a função apropriada.

Além disso, em todas essas funções de bloqueio, a sobrecarga das operações atômicas diminui todos os outros cálculos em duas ou três ordens de magnitude.

-Matt


Esta resposta é do autor !!!
RaGa__M 29/01

31

O código é encontrado em _spin_lock_contested, que é chamado _spin_lock_quickquando outra pessoa está tentando obter o bloqueio:

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

Se não houver concurso, counto valor anterior deve ser 0, mas não é. Este countvalor é passado como parâmetro para _spin_lock_contestedcomo o valueparâmetro. Isso valueé verificado com o ifdo 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;
}

Tendo em mente que valueé o valor anterior de spin->counta, e o último já foi incrementado em 1, esperamos spin->countaser iguais value + 1(a menos que algo tenha mudado nesse meio tempo).

Portanto, verificar se spin->counta == SPINLOCK_SHARED | 1(a pré-condição de atomic_cmpset_int) corresponde a verificar se value + 1 == SPINLOCK_SHARED | 1, que pode ser reescrito como value == (SPINLOCK_SHARED | 1) - 1(novamente, se nada mudou nesse meio tempo).

Embora value == (SPINLOCK_SHARED | 1) - 1possa ser reescrito como value == SPINLOCK_SHARED, é deixado como está para esclarecer a intenção da comparação (ou seja, comparar o valor anterior incrementado com o valor do teste).

Ou uau. a resposta parece ser: para maior clareza e consistência do código.


Obrigado pela sua resposta, tudo, exceto (SPINLOCK_SHARED | 1) - 1parte é compreensível e value == SPINLOCK_SHAREDé o meu pensamento também, porque estamos verificando-bandeira compartilhada se o valor anterior set.if sim, ativar o bloqueio em exclusivo .........
RaGa__M

11
@RaGa__M: o objetivo da ifverificação é verificar se value + 1(que deve ter o mesmo valor como spin->countase nada tivesse mudado nesse meio tempo) é igual SPINLOCK_SHARED | 1. Se você escrever a ifverificação como value == SPINLOCK_SHARED, essa intenção não é clara e seria muito mais difícil descobrir o que a verificação significa. Manter ambos SPINLOCK_SHARED | 1e - 1explicitamente sob ifcontrole é intencional.
Sander De Dycker

Mas na verdade está causando confusão.
RaGa__M

Por que não if (value + 1 == (SPINLOCK_SHARED | 1) )?
Pablo H

Bem .... simplesmente poderia ser o value & SPINLOCK_SHAREDque é mais legível.
RaGa__M

10

Eu acho que o objetivo é provavelmente ignorar o bit mais baixo e significativo:

  • Se SPINLOCK_SHARED expresso em binário for xxx0 -> result is xxx0
  • Se SPINLOCK_SHARED = xxx1 -> o resultado também for xxx0

teria sido talvez mais claro usar uma expressão de máscara de bits?


8
É isso que o código faz, mas a pergunta é por que você faria isso para uma constante definida que não possui o conjunto de bits menos significativo?
Sander De Dycker

4
@SanderDeDycker Porque o kernel do Linux?
Lundin

9
@Lundin O kernel do linux não está isento de práticas de codificação compreensíveis. Muito pelo contrário.
Qix - MONICA FOI ERRADA

2
@ Qix Se você diz. Eu era um grande fã do Linux até espiar o código e ler o documento sobre o estilo de codificação do kernel. Atualmente, mantenho uma distância de segurança de 10 metros dos computadores Linux.
Lundin

2
@Qix Nah Estou em vez de julgá-lo com base em seu código-fonte ...
Lundin

4

O efeito de

(SPINLOCK_SHARED | 1) - 1

é garantir que o bit de baixa ordem do resultado seja limpo antes da comparação com value. Concordo que parece inútil, mas aparentemente o bit de ordem inferior tem um uso ou significado específico que não é aparente neste código, e acho que devemos assumir que os desenvolvedores tiveram uma boa razão para fazer isso. Uma pergunta interessante seria - esse mesmo padrão ( | 1) -1) é usado em toda a base de código que você está vendo?


2

É uma maneira ofuscada de escrever um pouco de máscara. Versão legível: value == (SPINLOCK_SHARED & ~1u).


5
Sim, mas por que . O OP está perguntando por que esse seria o caso se SPINLOCK_SHAREDfor uma constante conhecida. Se eles estão simplesmente testando a SPINLOCK_SHAREDpresença de uma máscara, por que não if (value & SPINLOCK_SHARED)?
Qix - MONICA FOI ERRADA

4
value == (SPINLOCK_SHARED & ~1u)não é equivalente porque value == (SPINLOCK_SHARED | 1) - 1funciona mesmo se o tipo de SPINLOCK_SHAREDfor maior que unsigned.
Eric Postpischil

4
Honestamente, não tenho certeza se isso & ~1ué mais claro. Pensei em sugerir & 0xFFFFFFFEminha resposta, mas percebi que isso também não é muito claro. Sua sugestão tem a vantagem da brevidade, no entanto. :-)
Bob Jarvis - Restabelece Monica

6
@Lundin: Nós não sabemos o que será 0x80000000. O OP declarou que está definido com #define SPINLOCK_SHARED 0x80000000, mas que poderia estar dentro #if…#endife uma definição diferente é usada em outras circunstâncias, ou o autor deste código poderia ter pretendido que funcionasse mesmo se a definição fosse editada ou o código compilado com outros cabeçalhos que defina-o de maneira diferente. Independentemente disso, os dois pedaços de código não são equivalentes por si mesmos.
Eric Postpischil

2
@ BobJarvis-ReinstateMonica É muito mais claro para as pessoas que trabalham com operadores bit a bit todos os dias. Misturar bit a bit com aritmética regular é confuso.
Lundin

0

A maioria é feita para lidar com vários casos adicionais. Por exemplo, neste caso, dizemos que SPINLOCK_SHAREDnão pode ser 1:

int SPINLOCK_SHARED = 0x01

int res = (SPINLOCK_SHARED | 1) - 1 // 0

2
Isso faria sentido, mas, embora não esteja claro na pergunta, parece que SPINLOCK_SHAREDé uma constante definida e a variável testada value. Nesse caso, o mistério permanece.
Roberto Caboni

Obrigado pela sua resposta, mas acho que no caso original SPINLOCK_SHARED não é 0x01, você manteve a | 1) - 1parte, quando SPINLOCK_SHARED segurando 0x80000000qual seria o impacto | 1) - 1?
RaGa__M

A única razão pela qual eu conseguia pensar é que eles queriam se proteger contra SPINLOCK_SHARED mudanças no futuro. Mas não está nada claro. Eu escrevia nos desenvolvedores do kernel e solicitava um comentário de esclarecimento ou a expressão a ser reorganizada para que ela se autodocumentasse.
Qix - MONICA FOI ERRADA
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.