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

Commit

Permalink
Add new Object::connect_unsafe(), Object::connect_notify_unsafe() and…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
sdroege committed Jan 26, 2019
1 parent f84b84b commit 8afc671
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 65 deletions.
36 changes: 23 additions & 13 deletions src/closure.rs
Expand Up @@ -32,10 +32,16 @@ glib_wrapper! {

impl Closure {
pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
unsafe {
Closure::new_unsafe(callback)
}
}

pub unsafe fn new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self {
unsafe extern "C" fn marshal<F>(_closure: *mut gobject_ffi::GClosure, return_value: *mut gobject_ffi::GValue,
n_param_values: c_uint, param_values: *mut gobject_ffi::GValue, _invocation_hint: *mut c_void,
marshal_data: *mut c_void)
where F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static
where F: Fn(&[Value]) -> Option<Value>
{
let values = slice::from_raw_parts(param_values as *const _, n_param_values as usize);
let callback: Box<F> = Box::from_raw(marshal_data as *mut _);
Expand All @@ -53,23 +59,27 @@ impl Closure {
}

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

unsafe {
let size = 4 + 4 + 3 * mem::size_of::<*mut c_void>() as u32;
let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut());
assert_ne!(closure, ptr::null_mut());
let callback = Box::new(callback);
let ptr: *mut F = Box::into_raw(callback);
let ptr: *mut c_void = ptr as *mut _;
gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
from_glib_none(closure)
}
// Due to bitfields we have to do our own calculations here for the size of the GClosure:
// - 4: 32 bits in guint bitfields at the beginning
// - padding due to alignment needed for the following pointer
// - 3 * size_of<*mut c_void>: 3 pointers
// We don't store any custom data ourselves in the GClosure
let size = u32::max(4, mem::align_of::<*mut c_void>() as u32)
+ 3 * mem::size_of::<*mut c_void>() as u32;
let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut());
assert_ne!(closure, ptr::null_mut());
let callback = Box::new(callback);
let ptr: *mut F = Box::into_raw(callback);
let ptr: *mut c_void = ptr as *mut _;
gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
from_glib_none(closure)
}

pub fn invoke(&self, values: &[&ToValue]) -> Option<Value> {
Expand Down
116 changes: 64 additions & 52 deletions src/object.rs
Expand Up @@ -872,10 +872,13 @@ pub trait ObjectExt: ObjectType {

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

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

Expand Down Expand Up @@ -999,11 +1002,17 @@ impl<T: ObjectType> ObjectExt for T {
}

fn connect_notify<'a, P: Into<Option<&'a str>>, F: Fn(&Self, &::ParamSpec) + Send + Sync + 'static>(&self, name: P, f: F) -> SignalHandlerId {
unsafe {
self.connect_notify_unsafe(name, f)
}
}

unsafe fn connect_notify_unsafe<'a, P: Into<Option<&'a str>>, F: Fn(&Self, &::ParamSpec)>(&self, name: P, f: F) -> SignalHandlerId {
use std::mem::transmute;

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

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

unsafe {
let f: Box<Box<Fn(&Self, &::ParamSpec) + Send + Sync + 'static>> = Box::new(Box::new(f));
::signal::connect(self.as_object_ref().to_glib_none().0, &signal_name,
transmute(notify_trampoline::<Self> as usize), Box::into_raw(f) as *mut _)
}
let f: Box<Box<Fn(&Self, &::ParamSpec)>> = Box::new(Box::new(f));
::signal::connect(self.as_object_ref().to_glib_none().0, &signal_name,
transmute(notify_trampoline::<Self> as usize), Box::into_raw(f) as *mut _)
}

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

fn connect<'a, N, F>(&self, signal_name: N, after: bool, callback: F) -> Result<SignalHandlerId, BoolError>
where N: Into<&'a str>, F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static {
unsafe {
self.connect_unsafe(signal_name, after, callback)
}
}

unsafe fn connect_unsafe<'a, N, F>(&self, signal_name: N, after: bool, callback: F) -> Result<SignalHandlerId, BoolError>
where N: Into<&'a str>, F: Fn(&[Value]) -> Option<Value> {
let signal_name: &str = signal_name.into();

unsafe {
let type_ = self.get_type();
let type_ = self.get_type();

let mut signal_id = 0;
let mut signal_detail = 0;
let mut signal_id = 0;
let mut signal_detail = 0;

let found: bool = from_glib(gobject_ffi::g_signal_parse_name(signal_name.to_glib_none().0,
type_.to_glib(), &mut signal_id,
&mut signal_detail, true.to_glib()));
let found: bool = from_glib(gobject_ffi::g_signal_parse_name(signal_name.to_glib_none().0,
type_.to_glib(), &mut signal_id,
&mut signal_detail, true.to_glib()));

if !found {
return Err(glib_bool_error!("Signal not found"));
}
if !found {
return Err(glib_bool_error!("Signal not found"));
}

let mut details = mem::zeroed();
gobject_ffi::g_signal_query(signal_id, &mut details);
if details.signal_id != signal_id {
return Err(glib_bool_error!("Signal not found"));
}
let mut details = mem::zeroed();
gobject_ffi::g_signal_query(signal_id, &mut details);
if details.signal_id != signal_id {
return Err(glib_bool_error!("Signal not found"));
}

// This is actually G_SIGNAL_TYPE_STATIC_SCOPE
let return_type: Type = from_glib(details.return_type & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT));
let closure = Closure::new(move |values| {
let ret = callback(values);
// This is actually G_SIGNAL_TYPE_STATIC_SCOPE
let return_type: Type = from_glib(details.return_type & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT));
let closure = Closure::new_unsafe(move |values| {
let ret = callback(values);

if return_type == Type::Unit {
if let Some(ret) = ret {
panic!("Signal required no return value but got value of type {}", ret.type_().name());
if return_type == Type::Unit {
if let Some(ret) = ret {
panic!("Signal required no return value but got value of type {}", ret.type_().name());
}
None
} else {
match ret {
Some(ret) => {
let valid_type: bool = from_glib(gobject_ffi::g_type_check_value_holds(
mut_override(ret.to_glib_none().0),
return_type.to_glib()));
if !valid_type {
panic!("Signal required return value of type {} but got {}",
return_type.name(), ret.type_().name());
}
Some(ret)
}
None
} else {
match ret {
Some(ret) => {
let valid_type: bool = from_glib(gobject_ffi::g_type_check_value_holds(
mut_override(ret.to_glib_none().0),
return_type.to_glib()));
if !valid_type {
panic!("Signal required return value of type {} but got {}",
return_type.name(), ret.type_().name());
}
Some(ret)
},
None => {
panic!("Signal required return value of type {} but got None", return_type.name());
},
None => {
panic!("Signal required return value of type {} but got None", return_type.name());
}
}
});
let handler = gobject_ffi::g_signal_connect_closure_by_id(self.as_object_ref().to_glib_none().0, signal_id, signal_detail,
closure.to_glib_none().0, after.to_glib());

if handler == 0 {
Err(glib_bool_error!("Failed to connect to signal"))
} else {
Ok(from_glib(handler))
}
});
let handler = gobject_ffi::g_signal_connect_closure_by_id(self.as_object_ref().to_glib_none().0, signal_id, signal_detail,
closure.to_glib_none().0, after.to_glib());

if handler == 0 {
Err(glib_bool_error!("Failed to connect to signal"))
} else {
Ok(from_glib(handler))
}
}

Expand Down

0 comments on commit 8afc671

Please sign in to comment.