diff options
author | Marc-André Lureau <marcandre.lureau@gmail.com> | 2012-06-22 11:57:36 +0200 |
---|---|---|
committer | Marc-André Lureau <marcandre.lureau@gmail.com> | 2012-07-23 16:27:32 +0200 |
commit | c6c335799dacb04e5992d997cd577782c4a2c9e0 (patch) | |
tree | a427288c25d261dc94e5a46cba6c7ed2941635cf /src/virt-viewer-util.c | |
parent | 4648dc16e84a7c69e203b1cadec33028e99ce0b5 (diff) | |
download | virt-viewer-c6c335799dacb04e5992d997cd577782c4a2c9e0.tar.gz virt-viewer-c6c335799dacb04e5992d997cd577782c4a2c9e0.tar.xz virt-viewer-c6c335799dacb04e5992d997cd577782c4a2c9e0.zip |
spice: disconnect signal handlers when either object is destroyed
Use virt_viewer_signal_connect_object(), a copy of telepathy
utility function tp_g_signal_connect_object(). This function
will take care of removing signal handler if any of emitter or
attached object are destroyed.
The following patches will have this condition met, since there is no
longer 1-1 relation between channel and display. The channels can
continue to be around when some of the display are removed.
Diffstat (limited to 'src/virt-viewer-util.c')
-rw-r--r-- | src/virt-viewer-util.c | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/src/virt-viewer-util.c b/src/virt-viewer-util.c index c1182af..9a163fa 100644 --- a/src/virt-viewer-util.c +++ b/src/virt-viewer-util.c @@ -136,6 +136,123 @@ virt_viewer_util_extract_host(const char *uristr, return 0; } +typedef struct { + GObject *instance; + GObject *observer; + GClosure *closure; + gulong handler_id; +} WeakHandlerCtx; + +static WeakHandlerCtx * +whc_new(GObject *instance, + GObject *observer) +{ + WeakHandlerCtx *ctx = g_slice_new0(WeakHandlerCtx); + + ctx->instance = instance; + ctx->observer = observer; + + return ctx; +} + +static void +whc_free(WeakHandlerCtx *ctx) +{ + g_slice_free(WeakHandlerCtx, ctx); +} + +static void observer_destroyed_cb(gpointer, GObject *); +static void closure_invalidated_cb(gpointer, GClosure *); + +/* + * If signal handlers are removed before the object is destroyed, this + * callback will never get triggered. + */ +static void +instance_destroyed_cb(gpointer ctx_, + GObject *where_the_instance_was G_GNUC_UNUSED) +{ + WeakHandlerCtx *ctx = ctx_; + + /* No need to disconnect the signal here, the instance has gone away. */ + g_object_weak_unref(ctx->observer, observer_destroyed_cb, ctx); + g_closure_remove_invalidate_notifier(ctx->closure, ctx, + closure_invalidated_cb); + whc_free(ctx); +} + +/* Triggered when the observer is destroyed. */ +static void +observer_destroyed_cb(gpointer ctx_, + GObject *where_the_observer_was G_GNUC_UNUSED) +{ + WeakHandlerCtx *ctx = ctx_; + + g_closure_remove_invalidate_notifier(ctx->closure, ctx, + closure_invalidated_cb); + g_signal_handler_disconnect(ctx->instance, ctx->handler_id); + g_object_weak_unref(ctx->instance, instance_destroyed_cb, ctx); + whc_free(ctx); +} + +/* Triggered when either object is destroyed or the handler is disconnected. */ +static void +closure_invalidated_cb(gpointer ctx_, + GClosure *where_the_closure_was G_GNUC_UNUSED) +{ + WeakHandlerCtx *ctx = ctx_; + + g_object_weak_unref(ctx->instance, instance_destroyed_cb, ctx); + g_object_weak_unref(ctx->observer, observer_destroyed_cb, ctx); + whc_free(ctx); +} + +/* Copied from tp_g_signal_connect_object. */ +/** + * virt_viewer_signal_connect_object: (skip) + * @instance: the instance to connect to. + * @detailed_signal: a string of the form "signal-name::detail". + * @c_handler: the #GCallback to connect. + * @gobject: the object to pass as data to @c_handler. + * @connect_flags: a combination of #GConnectFlags. + * + * Similar to g_signal_connect_object() but will delete connection + * when any of the objects is destroyed. + * + * Returns: the handler id. + */ +gulong virt_viewer_signal_connect_object(gpointer instance, + const gchar *detailed_signal, + GCallback c_handler, + gpointer gobject, + GConnectFlags connect_flags) +{ + GObject *instance_obj = G_OBJECT(instance); + WeakHandlerCtx *ctx = whc_new(instance_obj, gobject); + + g_return_val_if_fail(G_TYPE_CHECK_INSTANCE (instance), 0); + g_return_val_if_fail(detailed_signal != NULL, 0); + g_return_val_if_fail(c_handler != NULL, 0); + g_return_val_if_fail(G_IS_OBJECT (gobject), 0); + g_return_val_if_fail((connect_flags & ~(G_CONNECT_AFTER|G_CONNECT_SWAPPED)) == 0, 0); + + if (connect_flags & G_CONNECT_SWAPPED) + ctx->closure = g_cclosure_new_object_swap(c_handler, gobject); + else + ctx->closure = g_cclosure_new_object(c_handler, gobject); + + ctx->handler_id = g_signal_connect_closure(instance, detailed_signal, + ctx->closure, (connect_flags & G_CONNECT_AFTER) ? TRUE : FALSE); + + g_object_weak_ref(instance_obj, instance_destroyed_cb, ctx); + g_object_weak_ref(gobject, observer_destroyed_cb, ctx); + g_closure_add_invalidate_notifier(ctx->closure, ctx, + closure_invalidated_cb); + + return ctx->handler_id; +} + + /* * Local variables: * c-indent-level: 4 |