Skip to content

Commit 2d2f44f

Browse files
authored
Merge pull request #563 from jf2048/closure-watch-weak-refs
glib-macros: Pass a pointer for watched objects into closure macro
2 parents 251143b + d8eb7be commit 2d2f44f

File tree

3 files changed

+99
-10
lines changed

3 files changed

+99
-10
lines changed

glib-macros/src/closure.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ impl Capture {
3232
let alias = self.alias();
3333
let name = &self.name;
3434
match self.kind {
35-
CaptureKind::Watch | CaptureKind::WeakAllowNone => quote! {
35+
CaptureKind::Watch => quote! {
36+
let #alias = #crate_ident::object::Watchable::watched_object(&#name);
37+
},
38+
CaptureKind::WeakAllowNone => quote! {
3639
let #alias = #crate_ident::clone::Downgrade::downgrade(&#name);
3740
},
3841
CaptureKind::Strong => quote! {
@@ -45,7 +48,7 @@ impl Capture {
4548
let name = &self.name;
4649
match self.kind {
4750
CaptureKind::Watch => quote! {
48-
#crate_ident::object::Object::watch_closure(&#name, &#closure_ident);
51+
#crate_ident::object::Watchable::watch_closure(&#name, &#closure_ident);
4952
},
5053
_ => Default::default(),
5154
}
@@ -55,12 +58,9 @@ impl Capture {
5558
let alias = self.alias();
5659
match self.kind {
5760
CaptureKind::Watch => {
58-
let err_msg = format!("failed to upgrade `{}`", alias);
5961
quote! {
60-
let #alias = match #crate_ident::clone::Upgrade::upgrade(&#alias) {
61-
Some(val) => val,
62-
None => panic!(#err_msg)
63-
};
62+
let #alias = unsafe { #alias.borrow() };
63+
let #alias = ::core::convert::AsRef::as_ref(&#alias);
6464
}
6565
}
6666
CaptureKind::WeakAllowNone => quote! {

glib-macros/tests/test.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ fn closure() {
359359
assert_eq!(obj.ref_count(), 1);
360360
let weak_test = glib::closure_local!(@watch obj => move || obj.ref_count());
361361
assert_eq!(obj.ref_count(), 1);
362-
assert_eq!(weak_test.invoke::<u32>(&[]), 3);
362+
assert_eq!(weak_test.invoke::<u32>(&[]), 2);
363363
assert_eq!(obj.ref_count(), 1);
364364

365365
weak_test
@@ -379,7 +379,7 @@ fn closure() {
379379
}
380380

381381
let obj = glib::Object::new::<glib::Object>(&[]).unwrap();
382-
assert_eq!(obj.ref_count_in_closure(), 3);
382+
assert_eq!(obj.ref_count_in_closure(), 2);
383383
}
384384

385385
{
@@ -398,7 +398,7 @@ fn closure() {
398398
let a = A {
399399
obj: glib::Object::new::<glib::Object>(&[]).unwrap(),
400400
};
401-
assert_eq!(a.ref_count_in_closure(), 3);
401+
assert_eq!(a.ref_count_in_closure(), 2);
402402
}
403403

404404
let strong_test = {
@@ -450,4 +450,46 @@ fn closure() {
450450
});
451451
assert_eq!(struct_test.invoke::<u32>(&[]), 2);
452452
}
453+
454+
{
455+
use glib::prelude::*;
456+
use glib::subclass::prelude::*;
457+
458+
#[derive(Default)]
459+
pub struct FooPrivate {}
460+
461+
#[glib::object_subclass]
462+
impl ObjectSubclass for FooPrivate {
463+
const NAME: &'static str = "MyFoo2";
464+
type Type = Foo;
465+
}
466+
467+
impl ObjectImpl for FooPrivate {}
468+
469+
glib::wrapper! {
470+
pub struct Foo(ObjectSubclass<FooPrivate>);
471+
}
472+
473+
impl Foo {
474+
fn my_ref_count(&self) -> u32 {
475+
self.ref_count()
476+
}
477+
}
478+
479+
let cast_test = {
480+
let foo = glib::Object::new::<Foo>(&[]).unwrap();
481+
482+
assert_eq!(foo.my_ref_count(), 1);
483+
let cast_test = glib::closure_local!(@watch foo => move || foo.my_ref_count());
484+
assert_eq!(foo.my_ref_count(), 1);
485+
assert_eq!(cast_test.invoke::<u32>(&[]), 2);
486+
assert_eq!(foo.my_ref_count(), 1);
487+
488+
let foo_ref = &foo;
489+
let _ = glib::closure_local!(@watch foo_ref => move || foo_ref.my_ref_count());
490+
491+
cast_test
492+
};
493+
cast_test.invoke::<()>(&[]);
494+
}
453495
}

glib/src/object.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3168,6 +3168,53 @@ impl<T: ObjectType> ObjectExt for T {
31683168
}
31693169
}
31703170

3171+
// Helper struct to avoid creating an extra ref on objects inside closure watches. This is safe
3172+
// because `watch_closure` ensures the object has a ref when the closure is called.
3173+
#[doc(hidden)]
3174+
pub struct WatchedObject<T: ObjectType>(ptr::NonNull<T::GlibType>);
3175+
3176+
#[doc(hidden)]
3177+
unsafe impl<T: ObjectType + Send + Sync> Send for WatchedObject<T> {}
3178+
3179+
#[doc(hidden)]
3180+
impl<T: ObjectType> WatchedObject<T> {
3181+
pub fn new(obj: &T) -> Self {
3182+
Self(unsafe { ptr::NonNull::new_unchecked(obj.as_ptr()) })
3183+
}
3184+
pub unsafe fn borrow(&self) -> Borrowed<T>
3185+
where
3186+
T: FromGlibPtrBorrow<*mut <T as ObjectType>::GlibType>,
3187+
{
3188+
from_glib_borrow(self.0.as_ptr())
3189+
}
3190+
}
3191+
3192+
#[doc(hidden)]
3193+
pub trait Watchable<T: ObjectType> {
3194+
fn watched_object(&self) -> WatchedObject<T>;
3195+
fn watch_closure(&self, closure: &impl AsRef<Closure>);
3196+
}
3197+
3198+
#[doc(hidden)]
3199+
impl<T: ObjectType> Watchable<T> for T {
3200+
fn watched_object(&self) -> WatchedObject<T> {
3201+
WatchedObject::new(self)
3202+
}
3203+
fn watch_closure(&self, closure: &impl AsRef<Closure>) {
3204+
ObjectExt::watch_closure(self, closure)
3205+
}
3206+
}
3207+
3208+
#[doc(hidden)]
3209+
impl<T: ObjectType> Watchable<T> for &T {
3210+
fn watched_object(&self) -> WatchedObject<T> {
3211+
WatchedObject::new(*self)
3212+
}
3213+
fn watch_closure(&self, closure: &impl AsRef<Closure>) {
3214+
ObjectExt::watch_closure(*self, closure)
3215+
}
3216+
}
3217+
31713218
// Validate that the given property value has an acceptable type for the given property pspec
31723219
// and if necessary update the value
31733220
fn validate_property_type(

0 commit comments

Comments
 (0)