unsafe fn increase_refcount<T: ArcWake>(data: *const ()) {
// Retain Arc, but don't touch refcount by wrapping in ManuallyDrop
let arc = mem::ManuallyDrop::new(Arc::<T>::from_raw(data.cast::<T>()));
// Now increase refcount, but don't drop new refcount either
let _arc_clone: mem::ManuallyDrop<_> = arc.clone();
}
I am a bit baffled by what is going on here. The current best approximation is:
normally, when arc and _arc_clone gets dropped, the ref count goes -1
the first ManuallyDrop prevents arc drop handler from being called
the scond ManuallyDrop prevents _arc_clone drop handler from being called
the arc.clone() line is where the count is actually incremented
Is this what is going on? If so, does this look a bit convoluted? I feel like there might be a more straight forward way to express this.
// cast added 1.38, from_raw needs 1.17
let arc = Arc::from_raw(data as *const T));
let arc_clone = arc.clone();
std::mem::forget(arc_clone); // could just make into one liner without added variable
std::mem::forget(arc); // still have the arc from prior into_raw stored in data
I actually find this impl clearer. I think the thing that threw me off was the name ManuallyDrop. Perhaps if it was named IgnoreDrop or DontAutoDrop, it would be clearer.
I get that ManuallyDrop signifies: it's your job to manually call drop on it if you want to call drop; but still adjusting to the naming.
The problem with explicit forget is that it's not unwind-safe. This might not actually be a problem in this particular piece of code, but in general, in the presence of unsafe, calling too many destructors because forget() wasn't invoked due to a panic is a real problem.
let resource = get_resource();
let managed_ptr = resource.get();
unsafe {
// if this panics, `resource.drop()` is called: potential double free
free_managed_ptr(managed_ptr);
}
mem::forget(resource);
then the created Arc would be dropped when the ArcWake::wake_by_ref call panics, which could easily cause use-after-free errors or similar down the line.