N f32's <-> Rc pointers <-> *mut f32 for blas/C

This is a followup to Interior mutability on N f32's

I need an object with the following property:

  1. at allocation time, gets enough memory to store N f32’s
  2. I can ahve many Rc pointers at the object
  3. When there are no more Rc pointers, the object gets freed
  4. Even when I have more than one Rc pointer, I can get a *mut f32 (so I can pass it to blas / other C functions)

I’m basically looking for the quiv of:

let ans = (float*) malloc(sizeof(float) * N);

Thanks!

I think part of the problem I am running into is as follows:

Rust compiler says: You can’t have mut ref to something that already has immutable refs

Me: well, they’re a bunch of f32’s; they don’t have constructors/drops / …:

Because Cell<T> has the exact same memory representation as T, and because Rust intentionally doesn’t have any type-based aliasing rules like those found in C, it’s permissible for unsafe code to simply cast *const Cell<T> to *mut T. Note that it will then be your responsibility to ensure you don’t make racing accesses from multiple threads (whereas with Cell the compiler will prevent you from sharing across threads).

So I recommend using Vec<Cell<f32>>, and doing something like my_vec.as_ptr() as *mut f32.

One more dumb question:

After I do my_vec.as_ptr() as *mut f32 and pass it to blas, and blas updates the contents

How I do I tell rustc, or how does rustc know that any previously cached values of Cell (say, in a x86 register or something) is now invalid?

Basically, the compiler is assuming that I only modify Cell via .set right? But now that I’m modifying it behind the compiler’s back, how do I tell the compiler that any cached values in register or elsewhere are now invalid?

I think this (edit: “this” meaning what @comex suggested above) would also need Vec::with_capacity to ensure that the vec has enough space at allocation time:
https://doc.rust-lang.org/std/vec/struct.Vec.html#method.with_capacity

Sure, the capacity issue does not concern me. I am still concerned about rustc/caching – i.e. the compiler assuming that I’m only changing Cell via .set, and I’m clearly changing it behind it’s back.

If you take an immutable reference to an object, you can’t take a mutable reference to said object while the immutable object still exists and use both. This is part of the rust memory safety rules.

The use of Cell already makes the compiler assume that any stray write or call to an external function could mutate the data, unless it can specifically prove that it doesn’t. For example, given the function

fn foo(x: &Cell<i32>) {
    let a = x.get();
    some_other_function();
    let b = x.get();
}

the compiler will not assume that a == b (unless it knows the contents of some_other_function and can prove that it doesn’t make any potentially aliasing writes… or if it knows x came from a stack variable earlier in the call stack and the address never escaped… things like that). However, it could make that assumption if x had type &i32 or &mut i32.

[edit: changed from *x to x.get(), although the original version is also correct, since you’d end up with two Cell<i32> local variables which can be compared for equality ;p]

2 Likes

By the way, for a more detailed explanation of how this works, you can check out the documentation for UnsafeCell. Cell<T> is just a thin wrapper around UnsafeCell<T> that provides a safe API; UnsafeCell<T> is where the magic happens, as it’s a wrapper around T which is special-cased by the compiler to turn off the normal aliasing assumptions.

I see. Put another way, can I always assume, unless the compiler can prove that nothign ahs changed, that a Cell.get() always reads directly from memory?

Yes.

@comex , @mmmmib : Question resolved. Thanks for all the detailed explainations!

1 Like