I was reading a kernel code, and in one place I've seen an expression inside if
statement like
if (value == (SPINLOCK_SHARED | 1) - 1) {
............
}
where SPINLOCK_SHARED = 0x80000000
is a predefined constant.
I wonder why do we need (SPINLOCK_SHARED | 1) - 1
- for type conversion purpose? the result of the expression would be 80000000-- same as 0x80000000, is it not? yet, why ORing 1 and Subtracting 1 matters?
Have a feeling like I am missing to get something..
The code is found in _spin_lock_contested
, which is called from _spin_lock_quick
when someone else is attempting to obtain the lock :
count = atomic_fetchadd_int(&spin->counta, 1);
if (__predict_false(count != 0)) {
_spin_lock_contested(spin, ident, count);
}
If there's no contest, then count
(the previous value) should be 0
, but it isn't. This count
value is passed as parameter to _spin_lock_contested
as the value
parameter. This value
is then checked with the if
from the 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;
}
Keeping in mind that value
is the previous value of spin->counta
, and the latter has already been incremented by 1, we expect spin->counta
to equal value + 1
(unless something has changed in the meantime).
So, checking if spin->counta == SPINLOCK_SHARED | 1
spin->counta == SPINLOCK_SHARED | 1
(the precondition of the atomic_cmpset_int
) corresponds to checking if value + 1 == SPINLOCK_SHARED | 1
value + 1 == SPINLOCK_SHARED | 1
, which can be rewritten as value == (SPINLOCK_SHARED | 1) - 1
(again, if nothing has changed in the meantime).
While value == (SPINLOCK_SHARED | 1) - 1
could be rewritten as value == SPINLOCK_SHARED
, it's left as is, to clarify the intent of the comparison (ie. to compare the incremented previous value with the test value).
Or iow. the answer appears to be : for clarity and code consistency.
I think the goal is probably to ignore the lowest significant bit:
would have been perhaps clearer to use a bit mask expression ?
The effect of
(SPINLOCK_SHARED | 1) - 1
is to ensure that the low-order bit of the result is cleared prior to the comparison with value
. I agree that it seems rather pointless but apparently the low-order bit has a particular usage or meaning which is not apparent in this code, and I think we have to assume that the devs had a good reason for doing this. An interesting question would be - is this same pattern ( | 1) -1
) used throughout the codebase you're looking at?
It's an obfuscated way of writing a bit mask. Readable version: value == (SPINLOCK_SHARED & ~1u)
.
It was just done that way for clarity, that's all. It's because atomic_fetchadd_int() (in eg sys/spinlock2.h) returns the value PRIOR to the addition/subtraction, and that value is passed to _spin_lock_contested()
Note that the C compiler completely pre-calculates all constant expressions. In fact, the compiler can even optimize-out inlined code based on conditionals that use passed-in procedure arguments when the procedures are passed constants in those arguments. This is why the lockmgr() inline in sys/lock.h has a case statement.... because that entire case statement will be optimized out and devolve into a direct call to the appropriate function.
Also, in all of these locking functions the overhead of the atomic ops dwarf all other calculations by two or three orders of magnitude.
-Matt
Most like this is done to handle several additional cases. For example, in this case, we say that SPINLOCK_SHARED
can not be 1:
int SPINLOCK_SHARED = 0x01
int res = (SPINLOCK_SHARED | 1) - 1 // 0
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.