let layout = Layout::from_size_align_unchecked(1024 * 8 , align);
println!("Size {}, Align {}", layout.size(), layout.align());
let ptr = alloc(layout);
Now, I have converted this ptr to Vector using from_raw_parts().
After that I am passing this vector using ffi after forgetting at vector using forget(vec).
After that I receive ptr, len and capacity from ffi and than I wanted to dealloc that memory.
dealloc(vv.as_mut_ptr(), layout);
Is using delloc method for this use case is right?
How can I verify that the memory is actually dellocated? Is there any other way to do this deallocation of memory.
Do not put the pointer into a Vec if the layout does not match what a Vec requires! Either just store the raw pointer directly, or wrap it in a custom struct.
To verify whether the deallocation actually happened, you can use valgrind.
It's good enough if you never giveaway the ownership or expose borrowing (&mut). But I still recommend you wrap the Vec<u8> in ManuallyDrop to ensure no automatic dropping is happening.
Edit: Actually just pass the pointer, do not construct Vec<u8>, construct &[u8] instead.
Its important for my business logic to convert it into Vec. Is there no safe way to do that? @zirconium-n @alice
Why won't forgetting it forget(vv); , and than calling dealloc() after I use is complete will not work?
Could you elaborate why does it require to be a Vec<u8>? Are you calling third party API requires Vec<u8>? &[u8] should be suffice for most if not all circumstance.
Why won't forgetting it forget(vv); , and than calling dealloc() after I use is complete will not work?
See my original comment: In practice it will work if you never giveaway &mut Vec<u8> or Vec<u8>, i.e. only using &Vec<u8>. But it's no different from &[u8] then.
...and don't grow or shrink it yourself, of course. And if vector isn't going to be grown or shrunk, &mut [u8] will probably cover every possible case, except when the API asks for more then it needs.
It works as long as you never modify or drop the Vec. People (including me) are just advising against doing it, since it's not a sound abstraction and easy to volatile the constraint.
So... If you have to do it, go ahead, and write a descriptive comment about it, then carefully code review any further modification related to these code.
The reason I do not recommend it is that it is fragile. If you ever call a method that causes it to reallocate, or hit some unexpected code path that causes its destructor to run (e.g. are you resistant to panics?), then you get undefined behavior.
As others have said, the Vec::from_raw_parts() constructor adds a bunch of safety invariants in order to avoid UB. However, if we're just trying to allocate some memory so it can be passed across the FFI boundary, then why not just pass the pointer across as-is?
use std::alloc::Layout;
extern "C" {
/// A FFI call that will initialize the buffer with something useful.
fn call_to_c(buffer: *mut u8, len: usize);
}
fn main() {
unsafe {
// Allocate a chunk of memory
let layout = Layout::from_size_align(1024 * 8, 512).unwrap();
let ptr: *mut u8 = std::alloc::alloc(layout);
assert!(!ptr.is_null());
// Now, we pass the buffer across the FFI boundary so it can be initialized
call_to_c(ptr, layout.size());
// Safety: the C code initialized our buffer, so now it's safe to use
let buffer: &[u8] = std::slice::from_raw_parts(ptr, layout.size());
println!("{:?}", &buffer[..5]);
// Make sure the buffer is freed at the end
std::alloc::dealloc(ptr, layout);
}
}
There's CVec that allows you to have a Vec-like container with arbitrary allocation managed by you:
But you absolutely can't do that with Vec. No way. Vec pretty much by definition is not allowed to behave the way you want it to, and fudging it is unsafe and unsound. Even if it may happen to work now, it's a time bomb that may corrupt your program in a different Rust version.
If you have an existing API that takes a Vec, then it's not compatible, and that API doesn't support higher memory alignment. You have to change that API (e.g. make it take impl AsRef<[u8]>).
A Vec that can't be resized or dropped is just a slice. You can use std::slice::from_raw_parts() safely with higher alignment.