diff mbox series

[18/26] rust: qom: add possibility of overriding unparent

Message ID 20241209123717.99077-19-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
Add a blanket definition of ClassInitImpl<ObjectClass> that thunks
ObjectImpl::UNPARENT and overrides it in ObjectClass if it is not
None.

ClassInitImpl<DeviceClass> can now call its superclass's ClassInitImpl,
so that the C and Rust hierarchies match more closely.

This is mostly done as an example of implementing the metaclass
hierarchy under ClassInitImpl.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/qemu-api/src/definitions.rs  | 44 ++++++++++++++++++++++++++++---
 rust/qemu-api/src/device_class.rs |  6 +++--
 2 files changed, 45 insertions(+), 5 deletions(-)

Comments

Zhao Liu Dec. 12, 2024, 9:40 a.m. UTC | #1
On Mon, Dec 09, 2024 at 01:37:09PM +0100, Paolo Bonzini wrote:
> Date: Mon,  9 Dec 2024 13:37:09 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 18/26] rust: qom: add possibility of overriding unparent
> X-Mailer: git-send-email 2.47.1
> 
> Add a blanket definition of ClassInitImpl<ObjectClass> that thunks
> ObjectImpl::UNPARENT and overrides it in ObjectClass if it is not
> None.
>
> ClassInitImpl<DeviceClass> can now call its superclass's ClassInitImpl,
> so that the C and Rust hierarchies match more closely.
> 
> This is mostly done as an example of implementing the metaclass
> hierarchy under ClassInitImpl.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/qemu-api/src/definitions.rs  | 44 ++++++++++++++++++++++++++++---
>  rust/qemu-api/src/device_class.rs |  6 +++--
>  2 files changed, 45 insertions(+), 5 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>

(with an additional comment below...)

> diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
> index 2429b9f53f0..958ea34decc 100644
> --- a/rust/qemu-api/src/definitions.rs
> +++ b/rust/qemu-api/src/definitions.rs
> @@ -6,7 +6,7 @@
>  
>  use std::{ffi::CStr, os::raw::c_void};
>  
> -use crate::bindings::{Object, ObjectClass, TypeInfo};
> +use crate::bindings::{self, Object, ObjectClass, TypeInfo};
>  
>  unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
>      // SAFETY: obj is an instance of T, since rust_instance_init<T>
> @@ -115,6 +115,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
>          class_data: core::ptr::null_mut(),
>          interfaces: core::ptr::null_mut(),
>      };
> +
> +    // methods on ObjectClass
> +    const UNPARENT: Option<fn(&mut Self)> = None;
>  }

Will we change `&mut Self` to an immutable reference in the future?

IIUC, the parent on the C side also has a pointer to current object.
Paolo Bonzini Dec. 12, 2024, 11:15 a.m. UTC | #2
On 12/12/24 10:40, Zhao Liu wrote:
> On Mon, Dec 09, 2024 at 01:37:09PM +0100, Paolo Bonzini wrote:
>> Date: Mon,  9 Dec 2024 13:37:09 +0100
>> From: Paolo Bonzini <pbonzini@redhat.com>
>> Subject: [PATCH 18/26] rust: qom: add possibility of overriding unparent
>> X-Mailer: git-send-email 2.47.1
>>
>> Add a blanket definition of ClassInitImpl<ObjectClass> that thunks
>> ObjectImpl::UNPARENT and overrides it in ObjectClass if it is not
>> None.
>>
>> ClassInitImpl<DeviceClass> can now call its superclass's ClassInitImpl,
>> so that the C and Rust hierarchies match more closely.
>>
>> This is mostly done as an example of implementing the metaclass
>> hierarchy under ClassInitImpl.
>>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> ---
>>   rust/qemu-api/src/definitions.rs  | 44 ++++++++++++++++++++++++++++---
>>   rust/qemu-api/src/device_class.rs |  6 +++--
>>   2 files changed, 45 insertions(+), 5 deletions(-)
> 
> Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
> 
> (with an additional comment below...)
> 
>> diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
>> index 2429b9f53f0..958ea34decc 100644
>> --- a/rust/qemu-api/src/definitions.rs
>> +++ b/rust/qemu-api/src/definitions.rs
>> @@ -6,7 +6,7 @@
>>   
>>   use std::{ffi::CStr, os::raw::c_void};
>>   
>> -use crate::bindings::{Object, ObjectClass, TypeInfo};
>> +use crate::bindings::{self, Object, ObjectClass, TypeInfo};
>>   
>>   unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
>>       // SAFETY: obj is an instance of T, since rust_instance_init<T>
>> @@ -115,6 +115,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
>>           class_data: core::ptr::null_mut(),
>>           interfaces: core::ptr::null_mut(),
>>       };
>> +
>> +    // methods on ObjectClass
>> +    const UNPARENT: Option<fn(&mut Self)> = None;
>>   }
> 
> Will we change `&mut Self` to an immutable reference in the future?

Good point, let's do it now since anyway UNPARENT is unused.

Paolo
diff mbox series

Patch

diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 2429b9f53f0..958ea34decc 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -6,7 +6,7 @@ 
 
 use std::{ffi::CStr, os::raw::c_void};
 
-use crate::bindings::{Object, ObjectClass, TypeInfo};
+use crate::bindings::{self, Object, ObjectClass, TypeInfo};
 
 unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
     // SAFETY: obj is an instance of T, since rust_instance_init<T>
@@ -115,6 +115,9 @@  pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
         class_data: core::ptr::null_mut(),
         interfaces: core::ptr::null_mut(),
     };
+
+    // methods on ObjectClass
+    const UNPARENT: Option<fn(&mut Self)> = None;
 }
 
 /// Internal trait used to automatically fill in a class struct.
@@ -128,7 +131,8 @@  pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
 ///
 /// Each struct will implement this trait with `T` equal to each
 /// superclass.  For example, a device should implement at least
-/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>`.
+/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>` and
+/// `ClassInitImpl<`[`ObjectClass`](crate::bindings::ObjectClass)`>`.
 ///
 /// Fortunately, this is almost never necessary.  Instead, the Rust
 /// implementation of methods will usually come from a trait like
@@ -139,9 +143,13 @@  pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
 /// ```ignore
 /// impl<T> ClassInitImpl<DeviceClass> for T
 /// where
-///     T: DeviceImpl,
+///     T: ClassInitImpl<ObjectClass> + DeviceImpl,
 /// ```
 ///
+/// The bound on `ClassInitImpl<ObjectClass>` is needed so that,
+/// after initializing the `DeviceClass` part of the class struct,
+/// the parent [`ObjectClass`] is initialized as well.
+///
 /// The only case in which a manual implementation of the trait is needed
 /// is for interfaces (note that there is no Rust example yet for using
 /// interfaces).  In this case, unlike the C case, the Rust class _has_
@@ -203,3 +211,33 @@  extern "C" fn ctor_fn() {
         }
     };
 }
+
+/// # Safety
+///
+/// We expect the FFI user of this function to pass a valid pointer that
+/// can be downcasted to type `T`. We also expect the device is
+/// readable/writeable from one thread at any time.
+unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut Object) {
+    unsafe {
+        assert!(!dev.is_null());
+        let mut state = core::ptr::NonNull::new_unchecked(dev.cast::<T>());
+        T::UNPARENT.unwrap()(state.as_mut());
+    }
+}
+
+impl<T> ClassInitImpl<ObjectClass> for T
+where
+    T: ObjectImpl,
+{
+    fn class_init(oc: &mut ObjectClass) {
+        if <T as ObjectImpl>::UNPARENT.is_some() {
+            oc.unparent = Some(rust_unparent_fn::<T>);
+        }
+    }
+}
+
+unsafe impl ObjectType for Object {
+    type Class = ObjectClass;
+    const TYPE_NAME: &'static CStr =
+        unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) };
+}
diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs
index ee9ae7eeb74..285dfe582c7 100644
--- a/rust/qemu-api/src/device_class.rs
+++ b/rust/qemu-api/src/device_class.rs
@@ -5,7 +5,7 @@ 
 use std::ffi::CStr;
 
 use crate::{
-    bindings::{self, DeviceClass, DeviceState, Error, Property, VMStateDescription},
+    bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
     definitions::ClassInitImpl,
     prelude::*,
     zeroable::Zeroable,
@@ -69,7 +69,7 @@  fn vmsd() -> Option<&'static VMStateDescription> {
 
 impl<T> ClassInitImpl<DeviceClass> for T
 where
-    T: DeviceImpl,
+    T: ClassInitImpl<ObjectClass> + DeviceImpl,
 {
     fn class_init(dc: &mut DeviceClass) {
         if <T as DeviceImpl>::REALIZE.is_some() {
@@ -86,6 +86,8 @@  fn class_init(dc: &mut DeviceClass) {
         unsafe {
             bindings::device_class_set_props(dc, <T as DeviceImpl>::properties().as_ptr());
         }
+
+        <T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class);
     }
 }