diff mbox series

[13/26] rust: qom: automatically use Drop trait to implement instance_finalize

Message ID 20241209123717.99077-14-pbonzini@redhat.com (mailing list archive)
State New
Headers show
Series rust: bundle of prerequisites for HPET implementation | expand

Commit Message

Paolo Bonzini Dec. 9, 2024, 12:37 p.m. UTC
Replace the customizable INSTANCE_FINALIZE with a generic function
that drops the Rust object.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/qemu-api/src/definitions.rs | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

Comments

Zhao Liu Dec. 10, 2024, 4:16 p.m. UTC | #1
On Mon, Dec 09, 2024 at 01:37:04PM +0100, Paolo Bonzini wrote:
> Date: Mon,  9 Dec 2024 13:37:04 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 13/26] rust: qom: automatically use Drop trait to implement
>  instance_finalize
> X-Mailer: git-send-email 2.47.1
> 
> Replace the customizable INSTANCE_FINALIZE with a generic function
> that drops the Rust object.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/qemu-api/src/definitions.rs | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
> 

Great idea. It nicely balances the differences between Rust and C QOM
conventions.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Paolo Bonzini Dec. 11, 2024, 12:42 p.m. UTC | #2
On Tue, Dec 10, 2024 at 4:58 PM Zhao Liu <zhao1.liu@intel.com> wrote:
> Great idea. It nicely balances the differences between Rust and C QOM
> conventions.

Except it does not work. :(  Suppose you have

       pub struct MySuperclass {
           parent: DeviceState,
           field: Box<MyData>,
           ...
       }

       impl Drop for MySuperclass {
           ...
       }

       pub struct MySubclass {
           parent: MySuperclass,
           ...
       }

When instance_finalize is called for MySubclass, it will walk the
struct's list of fields and call the drop method for MySuperclass.
Then, object_deinit recurses to the superclass and calls the same drop
method again.  This will cause double-freeing of the Box<MyData>, or
more in general double-dropping.

What's happening here is that QOM wants to control the drop order of
MySuperclass and MySubclass's fields.  To do so, the parent field must
be marked ManuallyDrop<>, which is quite ugly.  Perhaps we can add a
wrapper type ParentField<> that is specific to QOM.  This hides the
implementation detail of *what* is special about the ParentField, and
it will also be easy to check for in the #[derive(Object)] macro.
Maybe in the future it will even make sense to have special functions
implemented on ParentField, I don't know...

Paolo
Zhao Liu Dec. 11, 2024, 3:37 p.m. UTC | #3
On Wed, Dec 11, 2024 at 01:42:32PM +0100, Paolo Bonzini wrote:
> Date: Wed, 11 Dec 2024 13:42:32 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH 13/26] rust: qom: automatically use Drop trait to
>  implement instance_finalize
> 
> On Tue, Dec 10, 2024 at 4:58 PM Zhao Liu <zhao1.liu@intel.com> wrote:
> > Great idea. It nicely balances the differences between Rust and C QOM
> > conventions.
> 
> Except it does not work. :(  Suppose you have
> 
>        pub struct MySuperclass {
>            parent: DeviceState,
>            field: Box<MyData>,
>            ...
>        }
> 
>        impl Drop for MySuperclass {
>            ...
>        }
> 
>        pub struct MySubclass {
>            parent: MySuperclass,
>            ...
>        }
> 
> When instance_finalize is called for MySubclass, it will walk the
> struct's list of fields and call the drop method for MySuperclass.
> Then, object_deinit recurses to the superclass and calls the same drop
> method again.  This will cause double-freeing of the Box<MyData>, or
> more in general double-dropping.

Good catch! Yes, there is indeed such an issue... The above example
could become a test case :-), which I supposed could trigger a double-
dropping error when compiling.

> What's happening here is that QOM wants to control the drop order of
> MySuperclass and MySubclass's fields.  To do so, the parent field must
> be marked ManuallyDrop<>, which is quite ugly.  Perhaps we can add a
> wrapper type ParentField<> that is specific to QOM.  This hides the
> implementation detail of *what* is special about the ParentField, and
> it will also be easy to check for in the #[derive(Object)] macro.
> Maybe in the future it will even make sense to have special functions
> implemented on ParentField, I don't know...

I also looked into the implementation of ManuallyDrop, and I agree with
a new ParentField (or simply ObjectParent). This wrapper is simple
enough but also useful for QOM. I will pay more attention to the
recursed relationships in QOM in review as well...

Thanks,
Zhao
diff mbox series

Patch

diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 0467e6290e0..d64a581a5cc 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -8,6 +8,13 @@ 
 
 use crate::bindings::{Object, ObjectClass, TypeInfo};
 
+unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) {
+    // SAFETY: obj is an instance of T, since drop_object<T>
+    // is called from QOM core as the instance_finalize function
+    // for class T
+    unsafe { std::ptr::drop_in_place(obj.cast::<T>()) }
+}
+
 /// Trait a type must implement to be registered with QEMU.
 pub trait ObjectImpl: ClassInitImpl + Sized {
     type Class;
@@ -16,7 +23,6 @@  pub trait ObjectImpl: ClassInitImpl + Sized {
     const ABSTRACT: bool = false;
     const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
     const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
-    const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
 
     const TYPE_INFO: TypeInfo = TypeInfo {
         name: Self::TYPE_NAME.as_ptr(),
@@ -29,7 +35,7 @@  pub trait ObjectImpl: ClassInitImpl + Sized {
         instance_align: core::mem::align_of::<Self>(),
         instance_init: Self::INSTANCE_INIT,
         instance_post_init: Self::INSTANCE_POST_INIT,
-        instance_finalize: Self::INSTANCE_FINALIZE,
+        instance_finalize: Some(drop_object::<Self>),
         abstract_: Self::ABSTRACT,
         class_size: core::mem::size_of::<Self::Class>(),
         class_init: <Self as ClassInitImpl>::CLASS_INIT,