-
Notifications
You must be signed in to change notification settings - Fork 96
Revamp object identifier ownership #139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@aldanor It would be nice if you had a look at this proposal. This is the opposite of keeping the registry, but seems to be easier to reason about regarding ownership of objects. |
8bba25d
to
9c2b7c8
Compare
This will need to be rebased as #140 is merged. @mulimoen I've read through this, and I guess my main question is: "why does it work?" :) Especially pre-1.10, for 1.8.x. (specifically, in multi-threaded environment) Also, for the crate user who doesn't want to dabble in source code of this crate, what would be the major usage differences? (in regards to strong close etc) |
It is simply having a single owner for every ID, taking special care to incref if we share the ID, and never decref more than necessary (special care is taken in tests to avoid this). The previous code was buggy as it always decreffed all open handles on file close. There are no multi-threaded concerns as we always operate on the ID using a mutex (serial execution). For users this means never use strong close (unless dead certain no objects are open), don't decref ( |
Seems |
6b66419 changed the inner type of |
src/hl/plist/dataset_create.rs
Outdated
let mut out: mem::MaybeUninit<T> = mem::MaybeUninit::uninit(); | ||
let buf = value.get_buf(); | ||
ptr::copy_nonoverlapping(buf.as_ptr(), (&mut out as *mut T).cast(), buf.len()); | ||
mem::forget(value); | ||
out | ||
ptr::copy_nonoverlapping(buf.as_ptr(), out.as_mut_ptr().cast(), buf.len()); | ||
// Drop the Box<[u8]>, but not subfields | ||
value.drop_nonrecursive(); | ||
// We now have exclusive access to all subfields | ||
out.assume_init() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since drop_nonrecursive()
is only used here, I wonder if it could be made pub(crate)
and this whole block could be pulled out into a method on OwnedDynValue
like this:
#[doc(hidden)]
pub unsafe fn cast<T: H5Type>(mut self) -> Result<T> {
...
}
where it could also check that T::type_descriptor() == self.tp
(or it could return -> T
without Result
and just panic on mismatch).
And then here it just becomes
let dtype = Datatype::from_type::<T>()?;
self.get_fill_value(&dtype.to_descriptor()?)?.map(|v| unsafe { v.cast() }).transpose()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new code pushes all the unsafety on hdf5-types
, making the code a lot clearer. As zero-sized fixed arrays are not possible to create in hdf5
, I've added a small expection to dyn_drop
of DynArray
to support this code path.
41efb66
to
8bf60ff
Compare
f503665
to
97f3cb8
Compare
c040a92
to
003e561
Compare
d251a27
to
915596d
Compare
31efca6
to
2e5f897
Compare
f5eca62
to
8fc6995
Compare
8fc6995
to
095aa37
Compare
Posted the last batch of minor comments ^ I was wondering, do we even really want |
I suppose advanced users of |
I think the point is, if you use the high-level interface (i.e. If you really need to dig into some advanced territory (i.e. using I'm just trying to think of cases and I can't really find none. You could think of Well, given that this update is already quite big, let's leave it be, but perhaps remove it in a later update (I'll add a ticket) and maybe remove |
One more thing - could you get rid of a manual incref in |
I find |
Well, yea, it's basically our |
Ok well, let's try to merge this (hope it doesn't shoot us in the foot, in some multi-threaded cases or some other weird stuff like that) :) |
This is a new way of handling object identifiers given by us from the
hdf5-c
library.The old approach was to store the handles in a registry, a hashmap that kept track of all previous identfiers. Each object addtionally kept track of whether it is invalid or not through an
RwLock
. This works very well for our tests, but fails when opening and closing many objects (#76, #137).This PR instead relies on
hdf5-c
to keep track of the identifiers and validity. TheHandle
is now a type which owns the underlying object directly, and ownership is a bit stricter.In practical terms this does not change much. Opened file objects will not be automatically closed when a file is dropped, the user must set the strong close degree on the file to get this behaviour. Setting the strong close degree is dangerous (in the sense it closes the wrong items), but not unsafe, and a warning has been added to the corresponding function.
The idea is to strengthen the relationship between an object handle on the
rust
side and thehdf5
side. A handle owns the object, and is cloned by incrementing the reference count and handing out a new object. The old approach was to try and close all related handles, acting as a halfFileCloseDegree::Strong
. The new approach requires the user to explicitely opt into this behaviour, with all the pitfalls this entails...Soundness difficulties (but not memory safety concern) leading to double free:
decref
without increfShould we remove or change some items from our API to remove some of these? We could add a counterpart of
from_id
(from_borrowed_id
), makedecref
hidden, and useunsafe
to annotate these functions.Fixes #76
Fixes #137