Zero initialize `AtomicPtr` and `AtomicUsize`

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.

1 Like

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.