Skip to content
This repository was archived by the owner on Jun 8, 2021. It is now read-only.

Commit 8afc671

Browse files
committed
Add new Object::connect_unsafe(), Object::connect_notify_unsafe() and Closure::new_unsafe()
These allow to use non-Send/non-Sync/non-'static closures and it's the job of the caller to ensure that the closures are only used in a safe way. Also clean up the calculation of the size of the allocated GClosure object, and as a side-effect allocate 4 bytes less on 32 bit architectures.
1 parent f84b84b commit 8afc671

File tree

2 files changed

+87
-65
lines changed

2 files changed

+87
-65
lines changed

src/closure.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,16 @@ glib_wrapper! {
3232

3333
impl Closure {
3434
pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
35+
unsafe {
36+
Closure::new_unsafe(callback)
37+
}
38+
}
39+
40+
pub unsafe fn new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self {
3541
unsafe extern "C" fn marshal<F>(_closure: *mut gobject_ffi::GClosure, return_value: *mut gobject_ffi::GValue,
3642
n_param_values: c_uint, param_values: *mut gobject_ffi::GValue, _invocation_hint: *mut c_void,
3743
marshal_data: *mut c_void)
38-
where F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static
44+
where F: Fn(&[Value]) -> Option<Value>
3945
{
4046
let values = slice::from_raw_parts(param_values as *const _, n_param_values as usize);
4147
let callback: Box<F> = Box::from_raw(marshal_data as *mut _);
@@ -53,23 +59,27 @@ impl Closure {
5359
}
5460

5561
unsafe extern "C" fn finalize<F>(notify_data: *mut c_void, _closure: *mut gobject_ffi::GClosure)
56-
where F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static
62+
where F: Fn(&[Value]) -> Option<Value>
5763
{
5864
let _callback: Box<F> = Box::from_raw(notify_data as *mut _);
5965
// callback is dropped here.
6066
}
6167

62-
unsafe {
63-
let size = 4 + 4 + 3 * mem::size_of::<*mut c_void>() as u32;
64-
let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut());
65-
assert_ne!(closure, ptr::null_mut());
66-
let callback = Box::new(callback);
67-
let ptr: *mut F = Box::into_raw(callback);
68-
let ptr: *mut c_void = ptr as *mut _;
69-
gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
70-
gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
71-
from_glib_none(closure)
72-
}
68+
// Due to bitfields we have to do our own calculations here for the size of the GClosure:
69+
// - 4: 32 bits in guint bitfields at the beginning
70+
// - padding due to alignment needed for the following pointer
71+
// - 3 * size_of<*mut c_void>: 3 pointers
72+
// We don't store any custom data ourselves in the GClosure
73+
let size = u32::max(4, mem::align_of::<*mut c_void>() as u32)
74+
+ 3 * mem::size_of::<*mut c_void>() as u32;
75+
let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut());
76+
assert_ne!(closure, ptr::null_mut());
77+
let callback = Box::new(callback);
78+
let ptr: *mut F = Box::into_raw(callback);
79+
let ptr: *mut c_void = ptr as *mut _;
80+
gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
81+
gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
82+
from_glib_none(closure)
7383
}
7484

7585
pub fn invoke(&self, values: &[&ToValue]) -> Option<Value> {

src/object.rs

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -872,10 +872,13 @@ pub trait ObjectExt: ObjectType {
872872

873873
fn connect<'a, N, F>(&self, signal_name: N, after: bool, callback: F) -> Result<SignalHandlerId, BoolError>
874874
where N: Into<&'a str>, F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static;
875+
unsafe fn connect_unsafe<'a, N, F>(&self, signal_name: N, after: bool, callback: F) -> Result<SignalHandlerId, BoolError>
876+
where N: Into<&'a str>, F: Fn(&[Value]) -> Option<Value>;
875877
fn emit<'a, N: Into<&'a str>>(&self, signal_name: N, args: &[&ToValue]) -> Result<Option<Value>, BoolError>;
876878
fn disconnect(&self, handler_id: SignalHandlerId);
877879

878880
fn connect_notify<'a, P: Into<Option<&'a str>>, F: Fn(&Self, &::ParamSpec) + Send + Sync + 'static>(&self, name: P, f: F) -> SignalHandlerId;
881+
unsafe fn connect_notify_unsafe<'a, P: Into<Option<&'a str>>, F: Fn(&Self, &::ParamSpec)>(&self, name: P, f: F) -> SignalHandlerId;
879882
fn notify<'a, N: Into<&'a str>>(&self, property_name: N);
880883
fn notify_by_pspec(&self, pspec: &::ParamSpec);
881884

@@ -999,11 +1002,17 @@ impl<T: ObjectType> ObjectExt for T {
9991002
}
10001003

10011004
fn connect_notify<'a, P: Into<Option<&'a str>>, F: Fn(&Self, &::ParamSpec) + Send + Sync + 'static>(&self, name: P, f: F) -> SignalHandlerId {
1005+
unsafe {
1006+
self.connect_notify_unsafe(name, f)
1007+
}
1008+
}
1009+
1010+
unsafe fn connect_notify_unsafe<'a, P: Into<Option<&'a str>>, F: Fn(&Self, &::ParamSpec)>(&self, name: P, f: F) -> SignalHandlerId {
10021011
use std::mem::transmute;
10031012

10041013
unsafe extern "C" fn notify_trampoline<P>(this: *mut gobject_ffi::GObject, param_spec: *mut gobject_ffi::GParamSpec, f: glib_ffi::gpointer)
10051014
where P: ObjectType {
1006-
let f: &&(Fn(&P, &::ParamSpec) + Send + Sync + 'static) = transmute(f);
1015+
let f: &&(Fn(&P, &::ParamSpec) + 'static) = transmute(f);
10071016
f(&Object::from_glib_borrow(this).unsafe_cast(), &from_glib_borrow(param_spec))
10081017
}
10091018

@@ -1014,11 +1023,9 @@ impl<T: ObjectType> ObjectExt for T {
10141023
"notify".into()
10151024
};
10161025

1017-
unsafe {
1018-
let f: Box<Box<Fn(&Self, &::ParamSpec) + Send + Sync + 'static>> = Box::new(Box::new(f));
1019-
::signal::connect(self.as_object_ref().to_glib_none().0, &signal_name,
1020-
transmute(notify_trampoline::<Self> as usize), Box::into_raw(f) as *mut _)
1021-
}
1026+
let f: Box<Box<Fn(&Self, &::ParamSpec)>> = Box::new(Box::new(f));
1027+
::signal::connect(self.as_object_ref().to_glib_none().0, &signal_name,
1028+
transmute(notify_trampoline::<Self> as usize), Box::into_raw(f) as *mut _)
10221029
}
10231030

10241031
fn notify<'a, N: Into<&'a str>>(&self, property_name: N) {
@@ -1053,64 +1060,69 @@ impl<T: ObjectType> ObjectExt for T {
10531060

10541061
fn connect<'a, N, F>(&self, signal_name: N, after: bool, callback: F) -> Result<SignalHandlerId, BoolError>
10551062
where N: Into<&'a str>, F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static {
1063+
unsafe {
1064+
self.connect_unsafe(signal_name, after, callback)
1065+
}
1066+
}
1067+
1068+
unsafe fn connect_unsafe<'a, N, F>(&self, signal_name: N, after: bool, callback: F) -> Result<SignalHandlerId, BoolError>
1069+
where N: Into<&'a str>, F: Fn(&[Value]) -> Option<Value> {
10561070
let signal_name: &str = signal_name.into();
10571071

1058-
unsafe {
1059-
let type_ = self.get_type();
1072+
let type_ = self.get_type();
10601073

1061-
let mut signal_id = 0;
1062-
let mut signal_detail = 0;
1074+
let mut signal_id = 0;
1075+
let mut signal_detail = 0;
10631076

1064-
let found: bool = from_glib(gobject_ffi::g_signal_parse_name(signal_name.to_glib_none().0,
1065-
type_.to_glib(), &mut signal_id,
1066-
&mut signal_detail, true.to_glib()));
1077+
let found: bool = from_glib(gobject_ffi::g_signal_parse_name(signal_name.to_glib_none().0,
1078+
type_.to_glib(), &mut signal_id,
1079+
&mut signal_detail, true.to_glib()));
10671080

1068-
if !found {
1069-
return Err(glib_bool_error!("Signal not found"));
1070-
}
1081+
if !found {
1082+
return Err(glib_bool_error!("Signal not found"));
1083+
}
10711084

1072-
let mut details = mem::zeroed();
1073-
gobject_ffi::g_signal_query(signal_id, &mut details);
1074-
if details.signal_id != signal_id {
1075-
return Err(glib_bool_error!("Signal not found"));
1076-
}
1085+
let mut details = mem::zeroed();
1086+
gobject_ffi::g_signal_query(signal_id, &mut details);
1087+
if details.signal_id != signal_id {
1088+
return Err(glib_bool_error!("Signal not found"));
1089+
}
10771090

1078-
// This is actually G_SIGNAL_TYPE_STATIC_SCOPE
1079-
let return_type: Type = from_glib(details.return_type & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT));
1080-
let closure = Closure::new(move |values| {
1081-
let ret = callback(values);
1091+
// This is actually G_SIGNAL_TYPE_STATIC_SCOPE
1092+
let return_type: Type = from_glib(details.return_type & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT));
1093+
let closure = Closure::new_unsafe(move |values| {
1094+
let ret = callback(values);
10821095

1083-
if return_type == Type::Unit {
1084-
if let Some(ret) = ret {
1085-
panic!("Signal required no return value but got value of type {}", ret.type_().name());
1096+
if return_type == Type::Unit {
1097+
if let Some(ret) = ret {
1098+
panic!("Signal required no return value but got value of type {}", ret.type_().name());
1099+
}
1100+
None
1101+
} else {
1102+
match ret {
1103+
Some(ret) => {
1104+
let valid_type: bool = from_glib(gobject_ffi::g_type_check_value_holds(
1105+
mut_override(ret.to_glib_none().0),
1106+
return_type.to_glib()));
1107+
if !valid_type {
1108+
panic!("Signal required return value of type {} but got {}",
1109+
return_type.name(), ret.type_().name());
1110+
}
1111+
Some(ret)
10861112
}
1087-
None
1088-
} else {
1089-
match ret {
1090-
Some(ret) => {
1091-
let valid_type: bool = from_glib(gobject_ffi::g_type_check_value_holds(
1092-
mut_override(ret.to_glib_none().0),
1093-
return_type.to_glib()));
1094-
if !valid_type {
1095-
panic!("Signal required return value of type {} but got {}",
1096-
return_type.name(), ret.type_().name());
1097-
}
1098-
Some(ret)
1099-
},
1100-
None => {
1101-
panic!("Signal required return value of type {} but got None", return_type.name());
1102-
},
1113+
None => {
1114+
panic!("Signal required return value of type {} but got None", return_type.name());
11031115
}
11041116
}
1105-
});
1106-
let handler = gobject_ffi::g_signal_connect_closure_by_id(self.as_object_ref().to_glib_none().0, signal_id, signal_detail,
1107-
closure.to_glib_none().0, after.to_glib());
1108-
1109-
if handler == 0 {
1110-
Err(glib_bool_error!("Failed to connect to signal"))
1111-
} else {
1112-
Ok(from_glib(handler))
11131117
}
1118+
});
1119+
let handler = gobject_ffi::g_signal_connect_closure_by_id(self.as_object_ref().to_glib_none().0, signal_id, signal_detail,
1120+
closure.to_glib_none().0, after.to_glib());
1121+
1122+
if handler == 0 {
1123+
Err(glib_bool_error!("Failed to connect to signal"))
1124+
} else {
1125+
Ok(from_glib(handler))
11141126
}
11151127
}
11161128

0 commit comments

Comments
 (0)