The Linux manual page for futex(2) describes the interpretation of the fourth argument as follows:
For several blocking operations, the timeout argument is a pointer to a timespec structure that specifies a timeout for the operation. However, notwithstanding the prototype shown above, for some operations, the least significant four bytes of this argument are instead used as an integer whose meaning is determined by the operation. For these operations, the kernel casts the timeout value first to unsigned long, then to uint32_t, and in the remainder of this page, this argument is referred to as val2 when interpreted in this fashion.
I assume the purpose of the two casts is just to get the bottom 32 bits of the pointer, but why does that need to be done with two casts rather than one?
What is the difference between this
uint32_t x = (uint32_t) (unsigned long) p;
and this?
uint32_t x = (uint32_t) p;
(uint32_t) p
risks undefined behavior (UB).
Consider from the C spec:
Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result is not required to be in the range of values of any integer type. C23dr § 6.3.2.3 6
Since uint32_t
may be narrower than a pointer, to avoid the UB of "If the result cannot be represented in the integer type, the behavior is undefined.", it makes sense to first cast to a wide type. unsigned long
seems a weak choice compared to unsigned long long
, uintmax_t
, or even (u)intptr_t
when it exists.
In any case, it appears the first cast is to avoid UB. The uint32_t
is simply to toss upper bits and only retain the least significant 32.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With