Expand description
Strongly typed pointers with reserved space for storing additional bit patterns within the same memory word.
§Motivation
In low-level concurrent programming (synchronization primitives, lock-free data structures, etc) it is often required to store additional state information (tags) alongside pointers to objects in memory, since most atomic CPU instructions operate on pointer-wide memory words. The marked pointer types provided by this crate encapsulate the logic and pointer arithmetic for composing (creating), decomposing and mutating such pointers and tag values.
§Tag Bits and Type Alignment
The possible space for storing tag bits in a pointer is determined by the
alignment of the pointed-to type, as long as the pointer is well-aligned
(e.g., not packed).
For instance, pointers to types with an alignment of 2 (2^1) bytes (e.g.,
u16
) never use the first of their lower bits (i.e., it is always zero),
pointers to types with an alignment of 8 (2^3) bytes such as u64
never
use their 3 lowest bits and so on.
Great care must be taken at all times to avoid over- or underflows in the
usually highly restricted range of valid tags for common tag sizes when
doing arithmetic operations.
Any operations resulting in tag values outside of their valid range will
invariably corrupt the bits representing the pointer and hence invoke
undefined behavior when dereferencing these pointers.
Constructing a type such as TagPtr<u64, 4>
is hence usually a user error,
since a pointer to a u64
has only 3 unused bits.
The resulting type would consider the first actual bit of the pointer to be
part of its tag and return a potentially corrupted pointer in methods such
as decompose
.
The has_sufficient_alignment
and assert_alignment
functions can be
used to explicitly check for or assert this property.
There is, however, one exception where using an otherwise ill-formed tag
pointer type is valid:
After composing a well-formed tag pointer instance (e.g., TagPtr<u64, 3>
)
it is valid to cast
it to a type with a smaller alignment
and the same number of tag bits such as TagPtr<(), 3>
for the purpose of
type-erasure.
§Example
Storing a boolean status flag alongside the pointer to a mutable u64
:
type TagPtr = tagptr::TagPtr<u64, 3>;
let mut val = 0xCAFE;
let is_ok = true;
let ptr = TagPtr::compose(&mut val, is_ok as usize);
let (reference, tag) = unsafe { ptr.decompose_mut() };
assert_eq!(reference, Some(&mut 0xCAFE));
assert_eq!(tag == 1, true);
Structs§
- A raw pointer type which can be safely shared between threads and which can use up to
N
of its lower bits to store additional information (the tag). - A type representing a
null
pointer with potential tag bits. - A non-nullable tagged raw pointer type similar to
NonNull
which can use up toN
of its lower bits to store additional information (the tag). - A raw, unsafe pointer type like
*mut T
which can use up toN
of its lower bits to store additional information (the tag).
Functions§
- Asserts that the alignment of
U
is large enough so a pointer to an instance may storeN
tag bits. - Returns
true
if the alignment ofT
is large enough so a pointer to an instance may store the given number oftag_bits
.