diff --git a/gobject/gsignal.c b/gobject/gsignal.c index 2e2dd615b..c4375b111 100644 --- a/gobject/gsignal.c +++ b/gobject/gsignal.c @@ -1340,7 +1340,8 @@ g_signal_new (const gchar *signal_name, /* optimize NOP emissions with NULL class handlers */ if (signal_id && G_TYPE_IS_INSTANTIATABLE (itype) && return_type == G_TYPE_NONE && - class_offset && class_offset < MAX_TEST_CLASS_OFFSET) + class_offset && class_offset < MAX_TEST_CLASS_OFFSET && + ~signal_flags & G_SIGNAL_MUST_COLLECT) { SignalNode *node; @@ -1632,7 +1633,9 @@ g_signal_newv (const gchar *signal_name, node->emission_hooks = NULL; if (class_closure) signal_add_class_closure (node, 0, class_closure); - else if (G_TYPE_IS_INSTANTIATABLE (itype) && return_type == G_TYPE_NONE) + else if (G_TYPE_IS_INSTANTIATABLE (itype) && + return_type == G_TYPE_NONE && + ~signal_flags & G_SIGNAL_MUST_COLLECT) { /* optimize NOP emissions */ node->test_class_offset = TEST_CLASS_MAGIC; @@ -2913,7 +2916,7 @@ g_signal_emit_valist (gpointer instance, GValue *param_values; SignalNode *node; guint i, n_params; - + g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); g_return_if_fail (signal_id > 0); @@ -2976,6 +2979,7 @@ g_signal_emit_valist (gpointer instance, SIGNAL_LOCK (); } SIGNAL_UNLOCK (); + instance_and_params->g_type = 0; g_value_init (instance_and_params, G_TYPE_FROM_INSTANCE (instance)); g_value_set_instance (instance_and_params, instance); diff --git a/gobject/gsignal.h b/gobject/gsignal.h index 3064d1fcb..24ef842cc 100644 --- a/gobject/gsignal.h +++ b/gobject/gsignal.h @@ -108,6 +108,8 @@ typedef gboolean (*GSignalAccumulator) (GSignalInvocationHint *ihint, * of as object methods which can be called generically by * third-party code. * @G_SIGNAL_NO_HOOKS: No emissions hooks are supported for this signal. + * @G_SIGNAL_MUST_COLLECT: Varargs signal emission will always collect the + * arguments, even if there are no signal handlers connected. Since 2.30. * * The signal flags are used to specify a signal's behaviour, the overall * signal description outlines how especially the RUN flags control the @@ -121,14 +123,15 @@ typedef enum G_SIGNAL_NO_RECURSE = 1 << 3, G_SIGNAL_DETAILED = 1 << 4, G_SIGNAL_ACTION = 1 << 5, - G_SIGNAL_NO_HOOKS = 1 << 6 + G_SIGNAL_NO_HOOKS = 1 << 6, + G_SIGNAL_MUST_COLLECT = 1 << 7 } GSignalFlags; /** * G_SIGNAL_FLAGS_MASK: * * A mask for all #GSignalFlags bits. */ -#define G_SIGNAL_FLAGS_MASK 0x7f +#define G_SIGNAL_FLAGS_MASK 0xff /** * GConnectFlags: * @G_CONNECT_AFTER: whether the handler should be called before or after the diff --git a/gobject/tests/Makefile.am b/gobject/tests/Makefile.am index 2ea23e4fd..010f04b89 100644 --- a/gobject/tests/Makefile.am +++ b/gobject/tests/Makefile.am @@ -9,6 +9,7 @@ TEST_PROGS += \ boxed \ enums \ param \ + signals \ threadtests \ dynamictests \ binding \ diff --git a/gobject/tests/signals.c b/gobject/tests/signals.c new file mode 100644 index 000000000..f4b8240fd --- /dev/null +++ b/gobject/tests/signals.c @@ -0,0 +1,91 @@ +#include + +typedef struct _Test Test; +typedef struct _TestClass TestClass; + +struct _Test +{ + GObject parent_instance; +}; + +struct _TestClass +{ + GObjectClass parent_class; + + void (* variant_changed) (Test *, GVariant *); +}; + +static GType test_get_type (void); +G_DEFINE_TYPE (Test, test, G_TYPE_OBJECT) + +static void +test_init (Test *test) +{ +} + +static void +test_class_init (TestClass *klass) +{ + g_signal_new ("variant-changed-no-slot", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VARIANT, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + g_signal_new ("variant-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT, + G_STRUCT_OFFSET (TestClass, variant_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VARIANT, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); +} + +static void +test_variant_signal (void) +{ + Test *test; + GVariant *v; + + /* Tests that the signal emission consumes the variant, + * even if there are no handlers connected. + */ + + test = g_object_new (test_get_type (), NULL); + + v = g_variant_new_boolean (TRUE); + g_variant_ref (v); + g_assert (g_variant_is_floating (v)); + g_signal_emit_by_name (test, "variant-changed-no-slot", v); + g_assert (!g_variant_is_floating (v)); + g_variant_unref (v); + + v = g_variant_new_boolean (TRUE); + g_variant_ref (v); + g_assert (g_variant_is_floating (v)); + g_signal_emit_by_name (test, "variant-changed", v); + g_assert (!g_variant_is_floating (v)); + g_variant_unref (v); + + g_object_unref (test); +} + +/* --- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gobject/signals/variant", test_variant_signal); + + return g_test_run (); +}