I am currently writing a lock-free allocator in Rust, which first allocates from the system (mmap
and VirtualAlloc
). This system memory is zero-initialized. In this case, is it valid to consider AtomicPtr
well-initialized to null
and AtomicUsize
well-initialized to 0
?
I just had a peek at the source code for AtomicPtr::default()
and it looks like defaulting to null
has been stable since 1.0
.
Using ATOMIC_USIZE_INIT
should always give you a 0
-initialized AtomicUsize
as well.
I am afraid these require writing to memory. My question is about zero initialized memory allocation. If the memory is zero initialized, do I have to ptr.write(AtomicPtr::new(null_mut()))
or is it implied by the fact that memory is zero initialized? What about AtomicUsize
?
Mmmmm.
So I guess the question is, "can an &AtomicPtr
pointing to zero-initialized memory be safely assumed to point to AtomicPtr::null()
without explicit initialization in Rust code?"
So long as the optimizer can't tell anything about the mmapped memory (e.g. a *mut u8
to it was returned by an extern fn
), it seems fine to me. The docs for AtomicPtr explicitly stated that the representation is identical to *mut T
.
If the question is instead, "can I use uninitialized()
on data that I know whose memory is zero-initialized," I wouldn't be so sure. If the optimizer sees you trying to read data that it thinks is uninitialized, it may do nasty things.
I don't think the optimizer can tell anything about being uninitialized()
or zeroed()
. The pointer comes from a FFI function from which the compiler optimizer cannot prove anything. So, the possible problem is not with the optimizer. The problem is with language guarantees.
Yes, indeed, the docs says the memory representation of atomics is the same as the underlying types. This solves my question about AtomicUsize
. However, I am not sure for null
because I don't know if Rust guarantees null
to be represented in memory with 0
.
I'm not sure if Rust formally guarantees that null
is 0
, but you can see that conflated here, at least:
*mut T
but non-zero and covariant.
Also, ptr::null()
is implemented by just casting 0
, but you could argue that this is only because it works for all currently supported platforms -- maybe still not a guarantee. (And technically, the cast could convert 0
to something else for the pointer in raw memory.)
https://doc.rust-lang.org/stable/src/core/ptr.rs.html#77
Yes, I am afraid new supported platforms could have a different implementation for ptr::null()
unless some guarantee is given. But even if this (0 as *const T
) is the implementation for all platforms, it could require some conversion. I say it because C standard requires that the integer 0
when cast to a pointer needs to be converted to a null pointer, even if the memory representation of NULL
is not zeroed.
Summary: We need Rust to take position about this guarantee.
Well, I realized even if null is not zeroed in memory, I can still assume that the AtomicPtr
is initialized to whatever the pointer represented by 0
is. This means I can safely share the memory and if necessary make a store refr.store(null_mut(), Release)
rather than the non-thread-safe ptr.write(AtomicPtr::new(null_mut()))
.
But still would be nice a position on null
's memory representation.