Volatile Wrapping Structs

Having read a lot of the previous threads on volatile reads and writes using the provided functions on pointers. I came to think if it was possible to have VolatileCellRw<T>, VolatileCellRo<T>, and VolatileCellWo<T>. Where they were respectively implement Deref and DerefMut which could be used with the provided functions (or even better not need then at all).

Would this be enough to guarantee no spurious reads or writes?

  • Deref can produce &T, which doesn’t have volatile reads.

  • DerefMut implies Deref so VolatileCellWo is impossible.

1 Like

So it would only work if they had their own methods on them which isn’t much better than using pointers directly is it?

Note that std::cell::Cell doesn’t impl Deref for the reason @ExpHP mentioned. Imo VolatileCell* families should follow the interface of std::cell::Cell

1 Like

So, as a point of reference, would something like this work?

I tried to model the Cell interface as much as possible

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e8b787d07bf75da9aa694e8a0d74421c

Would that work if T was a pointer or would that have to be a different struct?

I don’t know what you are trying to say.

Do you mean is VolatileCell<*const Foo> valid? I don’t see why not.

This exists as:

https://github.com/japaric/vcell < A cell where all operations are volatile

https://github.com/japaric/volatile-register < Volatile registers with read-write, read-only and write-only actions

Because everything is volatile, they expect T: Copy on the inner type and allow no borrows.

3 Likes

Unfortunately, “volatile cell” does not work because you are still using references. A &VolatileCell<T> is still marked as dereferencable and thus the compiler is allowed to introduce spurious reads. As such, the vcell crate is unfortunately not solving this problem.

To solve the problem with volatile references, we need either a wrapper around a raw pointer – something like VolatileRef<'a, T> which is basically a newtype around NonNull<T>; or we need a language change to no longer allow spurious dereferences of all references. Note that this does not just affect volatile accesses, we also have a case in libstd that is in conflict with this.

Also see this UCG discussion.

6 Likes

So this happens even though VolatileCell<T> is wrapping an UnsafeCell<T>? In other words, UnsafeCell<T> prevents the aliasing analysis of a &UnsafeCell<T> to cross the wrapper boundary and be applied to the wrappee, but it does not prevent the dereferenceable attribute from crossing the wrapper boundaries, is that it?

In that case, a new #[lang] wrapper seems to be needed, to act as a barrier on the dereferenceable attribute, is it not?

I mean, I don’t think that VolatileRef[Mut]<'a, T> can suffice for all use cases, although this one can indeed be already implemented in Rust (comments on the code are welcome), even if it is very hard to get a value in Rust that has not been a reference before (need for the &raw operator, here).

For instance, how valid is the example currently provided by the documentation?

Correct.

If we want to use references for this, yes. The alternative is to make UnsafeCell that wrapper, i.e. to also make it remove the dereferencable attribute.

That example is valid as in there is no UB, but it allows spurious reads. x is not an MMIO location so that’s not a problem.

3 Likes

Does MaybeUninit remove the dereferencable attribute? If so, we can use that.

1 Like

No it does not. It does not have any effect on aliasing either – &MaybeUninit<i32> is just as much a read-only reference as &i32.

1 Like

I was thinking UnsafeCell<MaybeUninit<T>>, but since that won’t work either, I guess we do need either &raw or a lang item.