Skip to content
This repository was archived by the owner on Mar 4, 2024. It is now read-only.

Commit f9f17d6

Browse files
committed
glib: Don't store subclass impl/private data in an Option<T>
We don't know the exact memory layout of the Option<T> in relation to T so can't easily go from a *const T to the surrounding *const Option<T>. Since nightly from 2020-02-12 using both pointer types interchangeably causes segmentation faults as their memory layouts are not actually compatible and never were, but due to some compiler changes this actually causes crashes at runtime now. See rust-lang/rust#69102 for details. As an Option<_> was only used for some paranoid runtime checks that are not really needed, simply store T directly as impl/private data of subclasses. This has the side-effect of having zero-sized impl/private data types to actually occupy zero bytes of memory, and in some other cases to save a few bytes of the allocation.
1 parent 4947f85 commit f9f17d6

File tree

2 files changed

+24
-9
lines changed

2 files changed

+24
-9
lines changed

src/subclass/object.rs

+11
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,17 @@ mod test {
564564
assert!(weak.upgrade().is_none());
565565
}
566566

567+
#[test]
568+
fn test_create_child_object() {
569+
let type_ = ChildObject::get_type();
570+
let obj = Object::new(type_, &[]).unwrap();
571+
572+
// ChildObject is a zero-sized type and we map that to the same pointer as the object
573+
// itself. No private/impl data is allocated for zero-sized types.
574+
let imp = ChildObject::from_instance(&obj);
575+
assert_eq!(imp as *const _ as *const (), obj.as_ptr() as *const _);
576+
}
577+
567578
#[test]
568579
fn test_set_properties() {
569580
let obj = Object::new(SimpleObject::get_type(), &[]).unwrap();

src/subclass/types.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ pub unsafe trait InstanceStruct: Sized + 'static {
7272
let private_offset = data.as_ref().private_offset;
7373
let ptr: *const u8 = self as *const _ as *const u8;
7474
let priv_ptr = ptr.offset(private_offset);
75-
let imp = priv_ptr as *const Option<Self::Type>;
75+
let imp = priv_ptr as *const Self::Type;
7676

77-
(*imp).as_ref().expect("No private struct")
77+
&*imp
7878
}
7979
}
8080

@@ -377,7 +377,7 @@ unsafe extern "C" fn class_init<T: ObjectSubclass>(
377377

378378
// We have to update the private struct offset once the class is actually
379379
// being initialized.
380-
{
380+
if mem::size_of::<T>() != 0 {
381381
let mut private_offset = data.as_ref().private_offset as i32;
382382
gobject_sys::g_type_class_adjust_private_offset(klass, &mut private_offset);
383383
(*data.as_mut()).private_offset = private_offset as isize;
@@ -417,13 +417,13 @@ unsafe extern "C" fn instance_init<T: ObjectSubclass>(
417417
let private_offset = (*data.as_mut()).private_offset;
418418
let ptr: *mut u8 = obj as *mut _ as *mut u8;
419419
let priv_ptr = ptr.offset(private_offset);
420-
let imp_storage = priv_ptr as *mut Option<T>;
420+
let imp_storage = priv_ptr as *mut T;
421421

422422
let klass = &*(klass as *const T::Class);
423423

424424
let imp = T::new_with_class(klass);
425425

426-
ptr::write(imp_storage, Some(imp));
426+
ptr::write(imp_storage, imp);
427427
}
428428

429429
unsafe extern "C" fn finalize<T: ObjectSubclass>(obj: *mut gobject_sys::GObject) {
@@ -433,9 +433,9 @@ unsafe extern "C" fn finalize<T: ObjectSubclass>(obj: *mut gobject_sys::GObject)
433433
let private_offset = (*data.as_mut()).private_offset;
434434
let ptr: *mut u8 = obj as *mut _ as *mut u8;
435435
let priv_ptr = ptr.offset(private_offset);
436-
let imp_storage = priv_ptr as *mut Option<T>;
436+
let imp_storage = priv_ptr as *mut T;
437437

438-
let imp = (*imp_storage).take().expect("No private struct");
438+
let imp = ptr::read(imp_storage);
439439
drop(imp);
440440

441441
// Chain up to the parent class' finalize implementation, if any.
@@ -494,8 +494,12 @@ where
494494

495495
let mut data = T::type_data();
496496
(*data.as_mut()).type_ = type_;
497-
let private_offset =
498-
gobject_sys::g_type_add_instance_private(type_.to_glib(), mem::size_of::<Option<T>>());
497+
498+
let private_offset = if mem::size_of::<T>() == 0 {
499+
0
500+
} else {
501+
gobject_sys::g_type_add_instance_private(type_.to_glib(), mem::size_of::<T>())
502+
};
499503
(*data.as_mut()).private_offset = private_offset as isize;
500504

501505
T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));

0 commit comments

Comments
 (0)