mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
Merge branch 'gsignal-emission-locks-cleanups' into 'main'
gsignal: Reduce lock/unlock pairs and ensure we always work on valid signal nodes See merge request GNOME/glib!2824
This commit is contained in:
commit
fe458559d8
@ -2305,7 +2305,6 @@ g_signal_chain_from_overridden_handler (gpointer instance,
|
||||
}
|
||||
|
||||
SIGNAL_UNLOCK ();
|
||||
instance_and_params->g_type = 0;
|
||||
g_value_init_from_instance (instance_and_params, instance);
|
||||
SIGNAL_LOCK ();
|
||||
|
||||
@ -3172,6 +3171,12 @@ g_signal_has_handler_pending (gpointer instance,
|
||||
return has_pending;
|
||||
}
|
||||
|
||||
static void
|
||||
signal_emitv_unlocked (const GValue *instance_and_params,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
GValue *return_value);
|
||||
|
||||
/**
|
||||
* g_signal_emitv:
|
||||
* @instance_and_params: (array): argument list for the signal emission.
|
||||
@ -3194,6 +3199,17 @@ g_signal_emitv (const GValue *instance_and_params,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
GValue *return_value)
|
||||
{
|
||||
SIGNAL_LOCK ();
|
||||
signal_emitv_unlocked (instance_and_params, signal_id, detail, return_value);
|
||||
SIGNAL_UNLOCK ();
|
||||
}
|
||||
|
||||
static void
|
||||
signal_emitv_unlocked (const GValue *instance_and_params,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
GValue *return_value)
|
||||
{
|
||||
gpointer instance;
|
||||
SignalNode *node;
|
||||
@ -3211,19 +3227,16 @@ g_signal_emitv (const GValue *instance_and_params,
|
||||
param_values = instance_and_params + 1;
|
||||
#endif
|
||||
|
||||
SIGNAL_LOCK ();
|
||||
node = LOOKUP_SIGNAL_NODE (signal_id);
|
||||
if (!node || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
|
||||
{
|
||||
g_critical ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
|
||||
SIGNAL_UNLOCK ();
|
||||
return;
|
||||
}
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if (detail && !(node->flags & G_SIGNAL_DETAILED))
|
||||
{
|
||||
g_critical ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
|
||||
SIGNAL_UNLOCK ();
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < node->n_params; i++)
|
||||
@ -3235,7 +3248,6 @@ g_signal_emitv (const GValue *instance_and_params,
|
||||
i,
|
||||
node->name,
|
||||
G_VALUE_TYPE_NAME (param_values + i));
|
||||
SIGNAL_UNLOCK ();
|
||||
return;
|
||||
}
|
||||
if (node->return_type != G_TYPE_NONE)
|
||||
@ -3246,7 +3258,6 @@ g_signal_emitv (const GValue *instance_and_params,
|
||||
G_STRLOC,
|
||||
type_debug_name (node->return_type),
|
||||
node->name);
|
||||
SIGNAL_UNLOCK ();
|
||||
return;
|
||||
}
|
||||
else if (!node->accumulator && !G_TYPE_CHECK_VALUE_TYPE (return_value, node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE))
|
||||
@ -3256,7 +3267,6 @@ g_signal_emitv (const GValue *instance_and_params,
|
||||
type_debug_name (node->return_type),
|
||||
node->name,
|
||||
G_VALUE_TYPE_NAME (return_value));
|
||||
SIGNAL_UNLOCK ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -3283,14 +3293,15 @@ g_signal_emitv (const GValue *instance_and_params,
|
||||
if (hlist == NULL || hlist->handlers == NULL)
|
||||
{
|
||||
/* nothing to do to emit this signal */
|
||||
SIGNAL_UNLOCK ();
|
||||
/* g_printerr ("omitting emission of \"%s\"\n", node->name); */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SIGNAL_UNLOCK ();
|
||||
signal_emit_unlocked_R (node, detail, instance, return_value, instance_and_params);
|
||||
/* Pass a stable node pointer, whose address can't change even if the
|
||||
* g_signal_nodes array gets reallocated. */
|
||||
SignalNode node_copy = *node;
|
||||
signal_emit_unlocked_R (&node_copy, detail, instance, return_value, instance_and_params);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
@ -3312,6 +3323,12 @@ accumulate (GSignalInvocationHint *ihint,
|
||||
return continue_emission;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
signal_emit_valist_unlocked (gpointer instance,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
va_list var_args);
|
||||
|
||||
/**
|
||||
* g_signal_emit_valist: (skip)
|
||||
* @instance: (type GObject.TypeInstance): the instance the signal is being
|
||||
@ -3333,36 +3350,60 @@ g_signal_emit_valist (gpointer instance,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
va_list var_args)
|
||||
{
|
||||
SIGNAL_LOCK ();
|
||||
if (signal_emit_valist_unlocked (instance, signal_id, detail, var_args))
|
||||
SIGNAL_UNLOCK ();
|
||||
}
|
||||
|
||||
/*<private>
|
||||
* signal_emit_valist_unlocked:
|
||||
* @instance: The instance to emit from
|
||||
* @signal_id: Signal id to emit
|
||||
* @detail: Signal detail
|
||||
* @var_args: Call arguments
|
||||
*
|
||||
* Returns: %TRUE if the signal mutex has been left locked
|
||||
*/
|
||||
static gboolean
|
||||
signal_emit_valist_unlocked (gpointer instance,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
va_list var_args)
|
||||
{
|
||||
GValue *instance_and_params;
|
||||
GType signal_return_type;
|
||||
GValue *param_values;
|
||||
SignalNode *node;
|
||||
guint i, n_params;
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
|
||||
g_return_if_fail (signal_id > 0);
|
||||
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), TRUE);
|
||||
g_return_val_if_fail (signal_id > 0, TRUE);
|
||||
|
||||
SIGNAL_LOCK ();
|
||||
node = LOOKUP_SIGNAL_NODE (signal_id);
|
||||
if (!node || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
|
||||
{
|
||||
g_critical ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
|
||||
SIGNAL_UNLOCK ();
|
||||
return;
|
||||
return TRUE;
|
||||
}
|
||||
#ifndef G_DISABLE_CHECKS
|
||||
if (detail && !(node->flags & G_SIGNAL_DETAILED))
|
||||
{
|
||||
g_critical ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
|
||||
SIGNAL_UNLOCK ();
|
||||
return;
|
||||
return TRUE;
|
||||
}
|
||||
#endif /* !G_DISABLE_CHECKS */
|
||||
|
||||
if (!node->single_va_closure_is_valid)
|
||||
node_update_single_va_closure (node);
|
||||
|
||||
/* There's no need to deep copy this, because a SignalNode instance won't
|
||||
* ever be destroyed, given that _g_signals_destroy() is not called in any
|
||||
* real program, however the SignalNode pointer could change, so just store
|
||||
* the struct contents references, so that we won't try to deference a
|
||||
* potentially invalid (or changed) pointer;
|
||||
*/
|
||||
SignalNode node_copy = *node;
|
||||
|
||||
if (node->single_va_closure != NULL)
|
||||
{
|
||||
HandlerList* hlist;
|
||||
@ -3415,32 +3456,26 @@ g_signal_emit_valist (gpointer instance,
|
||||
}
|
||||
}
|
||||
|
||||
if (fastpath && closure == NULL && node->return_type == G_TYPE_NONE)
|
||||
{
|
||||
SIGNAL_UNLOCK ();
|
||||
return;
|
||||
}
|
||||
if (fastpath && closure == NULL && node_copy.return_type == G_TYPE_NONE)
|
||||
return TRUE;
|
||||
|
||||
/* Don't allow no-recurse emission as we might have to restart, which means
|
||||
we will run multiple handlers and thus must ref all arguments */
|
||||
if (closure != NULL && (node->flags & (G_SIGNAL_NO_RECURSE)) != 0)
|
||||
if (closure != NULL && (node_copy.flags & (G_SIGNAL_NO_RECURSE)) != 0)
|
||||
fastpath = FALSE;
|
||||
|
||||
if (fastpath)
|
||||
{
|
||||
SignalAccumulator *accumulator;
|
||||
Emission emission;
|
||||
GValue *return_accu, accu = G_VALUE_INIT;
|
||||
GType instance_type = G_TYPE_FROM_INSTANCE (instance);
|
||||
GValue emission_return = G_VALUE_INIT;
|
||||
GType rtype = node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
gboolean static_scope = node->return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
GType rtype = node_copy.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
gboolean static_scope = node_copy.return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
|
||||
signal_id = node->signal_id;
|
||||
accumulator = node->accumulator;
|
||||
if (rtype == G_TYPE_NONE)
|
||||
return_accu = NULL;
|
||||
else if (accumulator)
|
||||
else if (node_copy.accumulator)
|
||||
return_accu = &accu;
|
||||
else
|
||||
return_accu = &emission_return;
|
||||
@ -3456,18 +3491,18 @@ g_signal_emit_valist (gpointer instance,
|
||||
if (fastpath_handler)
|
||||
handler_ref (fastpath_handler);
|
||||
|
||||
SIGNAL_UNLOCK ();
|
||||
|
||||
if (closure != NULL)
|
||||
{
|
||||
TRACE(GOBJECT_SIGNAL_EMIT(signal_id, detail, instance, instance_type));
|
||||
|
||||
SIGNAL_UNLOCK ();
|
||||
|
||||
if (rtype != G_TYPE_NONE)
|
||||
g_value_init (&emission_return, rtype);
|
||||
|
||||
if (accumulator)
|
||||
g_value_init (&accu, rtype);
|
||||
if (node_copy.accumulator)
|
||||
g_value_init (&accu, rtype);
|
||||
|
||||
if (closure != NULL)
|
||||
{
|
||||
/*
|
||||
* Coverity doesn’t understand the paired ref/unref here and seems
|
||||
* to ignore the ref, thus reports every call to g_signal_emit()
|
||||
@ -3482,12 +3517,15 @@ g_signal_emit_valist (gpointer instance,
|
||||
return_accu,
|
||||
instance,
|
||||
var_args,
|
||||
node->n_params,
|
||||
node->param_types);
|
||||
accumulate (&emission.ihint, &emission_return, &accu, accumulator);
|
||||
}
|
||||
node_copy.n_params,
|
||||
node_copy.param_types);
|
||||
accumulate (&emission.ihint, &emission_return, &accu, node_copy.accumulator);
|
||||
|
||||
SIGNAL_LOCK ();
|
||||
if (node_copy.accumulator)
|
||||
g_value_unset (&accu);
|
||||
|
||||
SIGNAL_LOCK ();
|
||||
}
|
||||
|
||||
emission.chain_type = G_TYPE_NONE;
|
||||
emission_pop (&emission);
|
||||
@ -3495,20 +3533,20 @@ g_signal_emit_valist (gpointer instance,
|
||||
if (fastpath_handler)
|
||||
handler_unref_R (signal_id, instance, fastpath_handler);
|
||||
|
||||
SIGNAL_UNLOCK ();
|
||||
|
||||
if (accumulator)
|
||||
g_value_unset (&accu);
|
||||
SIGNAL_UNLOCK ();
|
||||
|
||||
if (rtype != G_TYPE_NONE)
|
||||
{
|
||||
gchar *error = NULL;
|
||||
for (i = 0; i < node->n_params; i++)
|
||||
for (i = 0; i < node_copy.n_params; i++)
|
||||
{
|
||||
GType ptype = node->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
GType ptype = node_copy.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
G_VALUE_COLLECT_SKIP (ptype, var_args);
|
||||
}
|
||||
|
||||
if (closure == NULL)
|
||||
g_value_init (&emission_return, rtype);
|
||||
|
||||
G_VALUE_LCOPY (&emission_return,
|
||||
var_args,
|
||||
static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
|
||||
@ -3533,21 +3571,20 @@ g_signal_emit_valist (gpointer instance,
|
||||
g_object_unref (instance);
|
||||
#endif
|
||||
|
||||
return;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
SIGNAL_UNLOCK ();
|
||||
|
||||
n_params = node->n_params;
|
||||
signal_return_type = node->return_type;
|
||||
instance_and_params = g_newa0 (GValue, n_params + 1);
|
||||
instance_and_params = g_newa0 (GValue, node_copy.n_params + 1);
|
||||
param_values = instance_and_params + 1;
|
||||
|
||||
for (i = 0; i < node->n_params; i++)
|
||||
for (i = 0; i < node_copy.n_params; i++)
|
||||
{
|
||||
gchar *error;
|
||||
GType ptype = node->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
gboolean static_scope = node->param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
GType ptype = node_copy.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
gboolean static_scope = node_copy.param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
|
||||
G_VALUE_COLLECT_INIT (param_values + i, ptype,
|
||||
var_args,
|
||||
@ -3564,24 +3601,29 @@ g_signal_emit_valist (gpointer instance,
|
||||
while (i--)
|
||||
g_value_unset (param_values + i);
|
||||
|
||||
return;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
instance_and_params->g_type = 0;
|
||||
g_value_init_from_instance (instance_and_params, instance);
|
||||
if (signal_return_type == G_TYPE_NONE)
|
||||
signal_emit_unlocked_R (node, detail, instance, NULL, instance_and_params);
|
||||
if (node_copy.return_type == G_TYPE_NONE)
|
||||
{
|
||||
SIGNAL_LOCK ();
|
||||
signal_emit_unlocked_R (&node_copy, detail, instance, NULL, instance_and_params);
|
||||
SIGNAL_UNLOCK ();
|
||||
}
|
||||
else
|
||||
{
|
||||
GValue return_value = G_VALUE_INIT;
|
||||
gchar *error = NULL;
|
||||
GType rtype = signal_return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
gboolean static_scope = signal_return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
GType rtype = node_copy.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
gboolean static_scope = node_copy.return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
|
||||
g_value_init (&return_value, rtype);
|
||||
|
||||
signal_emit_unlocked_R (node, detail, instance, &return_value, instance_and_params);
|
||||
SIGNAL_LOCK ();
|
||||
signal_emit_unlocked_R (&node_copy, detail, instance, &return_value, instance_and_params);
|
||||
SIGNAL_UNLOCK ();
|
||||
|
||||
G_VALUE_LCOPY (&return_value,
|
||||
var_args,
|
||||
@ -3599,9 +3641,11 @@ g_signal_emit_valist (gpointer instance,
|
||||
*/
|
||||
}
|
||||
}
|
||||
for (i = 0; i < n_params; i++)
|
||||
for (i = 0; i < node_copy.n_params; i++)
|
||||
g_value_unset (param_values + i);
|
||||
g_value_unset (instance_and_params);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3663,19 +3707,41 @@ g_signal_emit_by_name (gpointer instance,
|
||||
|
||||
SIGNAL_LOCK ();
|
||||
signal_id = signal_parse_name (detailed_signal, itype, &detail, TRUE);
|
||||
SIGNAL_UNLOCK ();
|
||||
|
||||
if (signal_id)
|
||||
{
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, detailed_signal);
|
||||
g_signal_emit_valist (instance, signal_id, detail, var_args);
|
||||
if (signal_emit_valist_unlocked (instance, signal_id, detail, var_args))
|
||||
SIGNAL_UNLOCK ();
|
||||
va_end (var_args);
|
||||
}
|
||||
else
|
||||
g_critical ("%s: signal name '%s' is invalid for instance '%p' of type '%s'",
|
||||
G_STRLOC, detailed_signal, instance, g_type_name (itype));
|
||||
{
|
||||
SIGNAL_UNLOCK ();
|
||||
|
||||
g_critical ("%s: signal name '%s' is invalid for instance '%p' of type '%s'",
|
||||
G_STRLOC, detailed_signal, instance, g_type_name (itype));
|
||||
}
|
||||
}
|
||||
|
||||
G_ALWAYS_INLINE static inline GValue *
|
||||
maybe_init_accumulator_unlocked (SignalNode *node,
|
||||
GValue *emission_return,
|
||||
GValue *accumulator_value)
|
||||
{
|
||||
if (node->accumulator)
|
||||
{
|
||||
if (accumulator_value->g_type)
|
||||
return accumulator_value;
|
||||
|
||||
g_value_init (accumulator_value,
|
||||
node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
|
||||
return accumulator_value;
|
||||
}
|
||||
|
||||
return emission_return;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -3694,11 +3760,16 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
guint signal_id;
|
||||
gulong max_sequential_handler_number;
|
||||
gboolean return_value_altered = FALSE;
|
||||
|
||||
guint n_params;
|
||||
|
||||
TRACE(GOBJECT_SIGNAL_EMIT(node->signal_id, detail, instance, G_TYPE_FROM_INSTANCE (instance)));
|
||||
|
||||
SIGNAL_LOCK ();
|
||||
/* We expect this function to be called with a stable SignalNode pointer
|
||||
* that cannot change location, so accessing its stable members should
|
||||
* always work even after a lock/unlock.
|
||||
*/
|
||||
signal_id = node->signal_id;
|
||||
n_params = node->n_params + 1;
|
||||
|
||||
if (node->flags & G_SIGNAL_NO_RECURSE)
|
||||
{
|
||||
@ -3707,20 +3778,10 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
if (emission_node)
|
||||
{
|
||||
emission_node->state = EMISSION_RESTART;
|
||||
SIGNAL_UNLOCK ();
|
||||
return return_value_altered;
|
||||
}
|
||||
}
|
||||
accumulator = node->accumulator;
|
||||
if (accumulator)
|
||||
{
|
||||
SIGNAL_UNLOCK ();
|
||||
g_value_init (&accu, node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
|
||||
return_accu = &accu;
|
||||
SIGNAL_LOCK ();
|
||||
}
|
||||
else
|
||||
return_accu = emission_return;
|
||||
emission.instance = instance;
|
||||
emission.ihint.signal_id = node->signal_id;
|
||||
emission.ihint.detail = detail;
|
||||
@ -3748,9 +3809,10 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
|
||||
emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
|
||||
SIGNAL_UNLOCK ();
|
||||
return_accu = maybe_init_accumulator_unlocked (node, emission_return, &accu);
|
||||
g_closure_invoke (class_closure,
|
||||
return_accu,
|
||||
node->n_params + 1,
|
||||
n_params,
|
||||
instance_and_params,
|
||||
&emission.ihint);
|
||||
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
|
||||
@ -3765,35 +3827,131 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
else if (emission.state == EMISSION_RESTART)
|
||||
goto EMIT_RESTART;
|
||||
}
|
||||
|
||||
|
||||
if (node->emission_hooks)
|
||||
{
|
||||
gboolean need_destroy, was_in_call, may_recurse = TRUE;
|
||||
GHook *hook;
|
||||
GHook *static_emission_hooks[3];
|
||||
size_t n_emission_hooks = 0;
|
||||
const gboolean may_recurse = TRUE;
|
||||
guint i;
|
||||
|
||||
emission.state = EMISSION_HOOK;
|
||||
|
||||
/* Quick check to determine whether any hooks match this emission,
|
||||
* before committing to the more complex work of calling those hooks.
|
||||
* We save a few of them into a static array, to try to avoid further
|
||||
* allocations.
|
||||
*/
|
||||
hook = g_hook_first_valid (node->emission_hooks, may_recurse);
|
||||
while (hook)
|
||||
{
|
||||
SignalHook *signal_hook = SIGNAL_HOOK (hook);
|
||||
|
||||
|
||||
if (!signal_hook->detail || signal_hook->detail == detail)
|
||||
{
|
||||
GSignalEmissionHook hook_func = (GSignalEmissionHook) hook->func;
|
||||
|
||||
was_in_call = G_HOOK_IN_CALL (hook);
|
||||
hook->flags |= G_HOOK_FLAG_IN_CALL;
|
||||
SIGNAL_UNLOCK ();
|
||||
need_destroy = !hook_func (&emission.ihint, node->n_params + 1, instance_and_params, hook->data);
|
||||
SIGNAL_LOCK ();
|
||||
if (!was_in_call)
|
||||
hook->flags &= ~G_HOOK_FLAG_IN_CALL;
|
||||
if (need_destroy)
|
||||
g_hook_destroy_link (node->emission_hooks, hook);
|
||||
}
|
||||
{
|
||||
if (n_emission_hooks < G_N_ELEMENTS (static_emission_hooks))
|
||||
{
|
||||
static_emission_hooks[n_emission_hooks] =
|
||||
g_hook_ref (node->emission_hooks, hook);
|
||||
}
|
||||
|
||||
n_emission_hooks += 1;
|
||||
}
|
||||
|
||||
hook = g_hook_next_valid (node->emission_hooks, hook, may_recurse);
|
||||
}
|
||||
|
||||
|
||||
/* Re-iterate back through the matching hooks and copy them into
|
||||
* an array which won’t change when we unlock to call the
|
||||
* user-provided hook functions.
|
||||
* These functions may change hook configuration for this signal,
|
||||
* add / remove signal handlers, etc.
|
||||
*/
|
||||
if G_UNLIKELY (n_emission_hooks > 0)
|
||||
{
|
||||
guint8 static_hook_returns[G_N_ELEMENTS (static_emission_hooks)];
|
||||
GHook **emission_hooks = NULL;
|
||||
guint8 *hook_returns = NULL;
|
||||
|
||||
if G_LIKELY (n_emission_hooks <= G_N_ELEMENTS (static_emission_hooks))
|
||||
{
|
||||
emission_hooks = static_emission_hooks;
|
||||
hook_returns = static_hook_returns;
|
||||
}
|
||||
else
|
||||
{
|
||||
emission_hooks = g_newa (GHook *, n_emission_hooks);
|
||||
hook_returns = g_newa (guint8, n_emission_hooks);
|
||||
|
||||
/* We can't just memcpy the ones we have in the static array,
|
||||
* to the alloca()'d one because otherwise we'd get an invalid
|
||||
* ID assertion during unref
|
||||
*/
|
||||
i = 0;
|
||||
for (hook = g_hook_first_valid (node->emission_hooks, may_recurse);
|
||||
hook != NULL;
|
||||
hook = g_hook_next_valid (node->emission_hooks, hook, may_recurse))
|
||||
{
|
||||
SignalHook *signal_hook = SIGNAL_HOOK (hook);
|
||||
|
||||
if (!signal_hook->detail || signal_hook->detail == detail)
|
||||
{
|
||||
if (i < G_N_ELEMENTS (static_emission_hooks))
|
||||
{
|
||||
emission_hooks[i] = g_steal_pointer (&static_emission_hooks[i]);
|
||||
g_assert (emission_hooks[i] == hook);
|
||||
}
|
||||
else
|
||||
{
|
||||
emission_hooks[i] = g_hook_ref (node->emission_hooks, hook);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (i == n_emission_hooks);
|
||||
}
|
||||
|
||||
SIGNAL_UNLOCK ();
|
||||
|
||||
for (i = 0; i < n_emission_hooks; ++i)
|
||||
{
|
||||
GSignalEmissionHook hook_func;
|
||||
gboolean need_destroy;
|
||||
guint old_flags;
|
||||
|
||||
hook = emission_hooks[i];
|
||||
hook_func = (GSignalEmissionHook) hook->func;
|
||||
|
||||
old_flags = g_atomic_int_or (&hook->flags, G_HOOK_FLAG_IN_CALL);
|
||||
need_destroy = !hook_func (&emission.ihint, n_params,
|
||||
instance_and_params, hook->data);
|
||||
|
||||
if (!(old_flags & G_HOOK_FLAG_IN_CALL))
|
||||
{
|
||||
g_atomic_int_compare_and_exchange (&hook->flags,
|
||||
old_flags | G_HOOK_FLAG_IN_CALL,
|
||||
old_flags);
|
||||
}
|
||||
|
||||
hook_returns[i] = !!need_destroy;
|
||||
}
|
||||
|
||||
SIGNAL_LOCK ();
|
||||
|
||||
for (i = 0; i < n_emission_hooks; i++)
|
||||
{
|
||||
hook = emission_hooks[i];
|
||||
|
||||
g_hook_unref (node->emission_hooks, hook);
|
||||
|
||||
if (hook_returns[i])
|
||||
g_hook_destroy_link (node->emission_hooks, hook);
|
||||
}
|
||||
}
|
||||
|
||||
if (emission.state == EMISSION_RESTART)
|
||||
goto EMIT_RESTART;
|
||||
}
|
||||
@ -3818,9 +3976,10 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
handler->sequential_number < max_sequential_handler_number)
|
||||
{
|
||||
SIGNAL_UNLOCK ();
|
||||
return_accu = maybe_init_accumulator_unlocked (node, emission_return, &accu);
|
||||
g_closure_invoke (handler->closure,
|
||||
return_accu,
|
||||
node->n_params + 1,
|
||||
n_params,
|
||||
instance_and_params,
|
||||
&emission.ihint);
|
||||
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
|
||||
@ -3857,9 +4016,10 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
|
||||
emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
|
||||
SIGNAL_UNLOCK ();
|
||||
return_accu = maybe_init_accumulator_unlocked (node, emission_return, &accu);
|
||||
g_closure_invoke (class_closure,
|
||||
return_accu,
|
||||
node->n_params + 1,
|
||||
n_params,
|
||||
instance_and_params,
|
||||
&emission.ihint);
|
||||
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
|
||||
@ -3889,9 +4049,10 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
handler->sequential_number < max_sequential_handler_number)
|
||||
{
|
||||
SIGNAL_UNLOCK ();
|
||||
return_accu = maybe_init_accumulator_unlocked (node, emission_return, &accu);
|
||||
g_closure_invoke (handler->closure,
|
||||
return_accu,
|
||||
node->n_params + 1,
|
||||
n_params,
|
||||
instance_and_params,
|
||||
&emission.ihint);
|
||||
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
|
||||
@ -3938,7 +4099,7 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
}
|
||||
g_closure_invoke (class_closure,
|
||||
node->return_type != G_TYPE_NONE ? &accu : NULL,
|
||||
node->n_params + 1,
|
||||
n_params,
|
||||
instance_and_params,
|
||||
&emission.ihint);
|
||||
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
|
||||
@ -3959,7 +4120,6 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
handler_unref_R (signal_id, instance, handler_list);
|
||||
|
||||
emission_pop (&emission);
|
||||
SIGNAL_UNLOCK ();
|
||||
if (accumulator)
|
||||
g_value_unset (&accu);
|
||||
|
||||
|
@ -1130,12 +1130,35 @@ hook_func (GSignalInvocationHint *ihint,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
hook_func_removal (GSignalInvocationHint *ihint,
|
||||
guint n_params,
|
||||
const GValue *params,
|
||||
gpointer data)
|
||||
{
|
||||
gint *count = data;
|
||||
|
||||
(*count)++;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
simple_handler_remove_hook (GObject *sender,
|
||||
gpointer data)
|
||||
{
|
||||
gulong *hook = data;
|
||||
|
||||
g_signal_remove_emission_hook (simple_id, *hook);
|
||||
}
|
||||
|
||||
static void
|
||||
test_emission_hook (void)
|
||||
{
|
||||
GObject *test1, *test2;
|
||||
gint count = 0;
|
||||
gulong hook;
|
||||
gulong connection_id;
|
||||
|
||||
test1 = g_object_new (test_get_type (), NULL);
|
||||
test2 = g_object_new (test_get_type (), NULL);
|
||||
@ -1150,6 +1173,73 @@ test_emission_hook (void)
|
||||
g_signal_emit_by_name (test1, "simple");
|
||||
g_assert_cmpint (count, ==, 2);
|
||||
|
||||
count = 0;
|
||||
hook = g_signal_add_emission_hook (simple_id, 0, hook_func_removal, &count, NULL);
|
||||
g_assert_cmpint (count, ==, 0);
|
||||
g_signal_emit_by_name (test1, "simple");
|
||||
g_assert_cmpint (count, ==, 1);
|
||||
g_signal_emit_by_name (test2, "simple");
|
||||
g_assert_cmpint (count, ==, 1);
|
||||
|
||||
g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL,
|
||||
"*simple* had no hook * to remove");
|
||||
g_signal_remove_emission_hook (simple_id, hook);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
count = 0;
|
||||
hook = g_signal_add_emission_hook (simple_id, 0, hook_func, &count, NULL);
|
||||
connection_id = g_signal_connect (test1, "simple",
|
||||
G_CALLBACK (simple_handler_remove_hook), &hook);
|
||||
g_assert_cmpint (count, ==, 0);
|
||||
g_signal_emit_by_name (test1, "simple");
|
||||
g_assert_cmpint (count, ==, 1);
|
||||
g_signal_emit_by_name (test2, "simple");
|
||||
g_assert_cmpint (count, ==, 1);
|
||||
|
||||
g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL,
|
||||
"*simple* had no hook * to remove");
|
||||
g_signal_remove_emission_hook (simple_id, hook);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
g_clear_signal_handler (&connection_id, test1);
|
||||
|
||||
gulong hooks[10];
|
||||
count = 0;
|
||||
|
||||
for (size_t i = 0; i < G_N_ELEMENTS (hooks); ++i)
|
||||
hooks[i] = g_signal_add_emission_hook (simple_id, 0, hook_func, &count, NULL);
|
||||
|
||||
g_assert_cmpint (count, ==, 0);
|
||||
g_signal_emit_by_name (test1, "simple");
|
||||
g_assert_cmpint (count, ==, 10);
|
||||
g_signal_emit_by_name (test2, "simple");
|
||||
g_assert_cmpint (count, ==, 20);
|
||||
|
||||
for (size_t i = 0; i < G_N_ELEMENTS (hooks); ++i)
|
||||
g_signal_remove_emission_hook (simple_id, hooks[i]);
|
||||
|
||||
g_signal_emit_by_name (test1, "simple");
|
||||
g_assert_cmpint (count, ==, 20);
|
||||
|
||||
count = 0;
|
||||
|
||||
for (size_t i = 0; i < G_N_ELEMENTS (hooks); ++i)
|
||||
hooks[i] = g_signal_add_emission_hook (simple_id, 0, hook_func_removal, &count, NULL);
|
||||
|
||||
g_assert_cmpint (count, ==, 0);
|
||||
g_signal_emit_by_name (test1, "simple");
|
||||
g_assert_cmpint (count, ==, 10);
|
||||
g_signal_emit_by_name (test2, "simple");
|
||||
g_assert_cmpint (count, ==, 10);
|
||||
|
||||
for (size_t i = 0; i < G_N_ELEMENTS (hooks); ++i)
|
||||
{
|
||||
g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL,
|
||||
"*simple* had no hook * to remove");
|
||||
g_signal_remove_emission_hook (simple_id, hooks[i]);
|
||||
g_test_assert_expected_messages ();
|
||||
}
|
||||
|
||||
g_object_unref (test1);
|
||||
g_object_unref (test2);
|
||||
}
|
||||
@ -1818,6 +1908,143 @@ test_signal_is_valid_name (void)
|
||||
g_assert_false (g_signal_is_valid_name (invalid_names[i]));
|
||||
}
|
||||
|
||||
static void
|
||||
test_emitv (void)
|
||||
{
|
||||
GArray *values;
|
||||
GObject *test;
|
||||
GValue return_value = G_VALUE_INIT;
|
||||
gint count = 0;
|
||||
guint signal_id;
|
||||
gulong hook;
|
||||
gulong id;
|
||||
|
||||
test = g_object_new (test_get_type (), NULL);
|
||||
|
||||
values = g_array_new (TRUE, TRUE, sizeof (GValue));
|
||||
g_array_set_clear_func (values, (GDestroyNotify) g_value_unset);
|
||||
|
||||
g_array_set_size (values, 1);
|
||||
g_value_init (&g_array_index (values, GValue, 0), G_TYPE_OBJECT);
|
||||
g_value_set_object (&g_array_index (values, GValue, 0), test);
|
||||
hook = g_signal_add_emission_hook (simple_id, 0, hook_func, &count, NULL);
|
||||
g_assert_cmpint (count, ==, 0);
|
||||
g_signal_emitv ((GValue *) values->data, simple_id, 0, NULL);
|
||||
g_assert_cmpint (count, ==, 1);
|
||||
g_signal_remove_emission_hook (simple_id, hook);
|
||||
|
||||
g_array_set_size (values, 20);
|
||||
g_value_init (&g_array_index (values, GValue, 1), G_TYPE_INT);
|
||||
g_value_set_int (&g_array_index (values, GValue, 1), 42);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 2), G_TYPE_BOOLEAN);
|
||||
g_value_set_boolean (&g_array_index (values, GValue, 2), TRUE);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 3), G_TYPE_CHAR);
|
||||
g_value_set_schar (&g_array_index (values, GValue, 3), 17);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 4), G_TYPE_UCHAR);
|
||||
g_value_set_uchar (&g_array_index (values, GValue, 4), 140);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 5), G_TYPE_UINT);
|
||||
g_value_set_uint (&g_array_index (values, GValue, 5), G_MAXUINT - 42);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 6), G_TYPE_LONG);
|
||||
g_value_set_long (&g_array_index (values, GValue, 6), -1117);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 7), G_TYPE_ULONG);
|
||||
g_value_set_ulong (&g_array_index (values, GValue, 7), G_MAXULONG - 999);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 8), enum_type);
|
||||
g_value_set_enum (&g_array_index (values, GValue, 8), MY_ENUM_VALUE);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 9), flags_type);
|
||||
g_value_set_flags (&g_array_index (values, GValue, 9),
|
||||
MY_FLAGS_FIRST_BIT | MY_FLAGS_THIRD_BIT | MY_FLAGS_LAST_BIT);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 10), G_TYPE_FLOAT);
|
||||
g_value_set_float (&g_array_index (values, GValue, 10), 0.25);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 11), G_TYPE_DOUBLE);
|
||||
g_value_set_double (&g_array_index (values, GValue, 11), 1.5);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 12), G_TYPE_STRING);
|
||||
g_value_set_string (&g_array_index (values, GValue, 12), "Test");
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 13), G_TYPE_PARAM_LONG);
|
||||
g_value_take_param (&g_array_index (values, GValue, 13),
|
||||
g_param_spec_long ("param", "nick", "blurb", 0, 10, 4, 0));
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 14), G_TYPE_BYTES);
|
||||
g_value_take_boxed (&g_array_index (values, GValue, 14),
|
||||
g_bytes_new_static ("Blah", 5));
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 15), G_TYPE_POINTER);
|
||||
g_value_set_pointer (&g_array_index (values, GValue, 15), &enum_type);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 16), test_get_type ());
|
||||
g_value_set_object (&g_array_index (values, GValue, 16), test);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 17), G_TYPE_VARIANT);
|
||||
g_value_take_variant (&g_array_index (values, GValue, 17),
|
||||
g_variant_ref_sink (g_variant_new_uint16 (99)));
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 18), G_TYPE_INT64);
|
||||
g_value_set_int64 (&g_array_index (values, GValue, 18), G_MAXINT64 - 1234);
|
||||
|
||||
g_value_init (&g_array_index (values, GValue, 19), G_TYPE_UINT64);
|
||||
g_value_set_uint64 (&g_array_index (values, GValue, 19), G_MAXUINT64 - 123456);
|
||||
|
||||
id = g_signal_connect (test, "all-types", G_CALLBACK (all_types_handler_cb), &flags_type);
|
||||
signal_id = g_signal_lookup ("all-types", test_get_type ());
|
||||
g_assert_cmpuint (signal_id, >, 0);
|
||||
|
||||
count = 0;
|
||||
hook = g_signal_add_emission_hook (signal_id, 0, hook_func, &count, NULL);
|
||||
g_assert_cmpint (count, ==, 0);
|
||||
g_signal_emitv ((GValue *) values->data, signal_id, 0, NULL);
|
||||
g_assert_cmpint (count, ==, 1);
|
||||
g_signal_remove_emission_hook (signal_id, hook);
|
||||
g_clear_signal_handler (&id, test);
|
||||
|
||||
|
||||
signal_id = g_signal_lookup ("generic-marshaller-int-return", test_get_type ());
|
||||
g_assert_cmpuint (signal_id, >, 0);
|
||||
g_array_set_size (values, 1);
|
||||
|
||||
id = g_signal_connect (test,
|
||||
"generic-marshaller-int-return",
|
||||
G_CALLBACK (on_generic_marshaller_int_return_signed_1),
|
||||
NULL);
|
||||
|
||||
count = 0;
|
||||
hook = g_signal_add_emission_hook (signal_id, 0, hook_func, &count, NULL);
|
||||
g_assert_cmpint (count, ==, 0);
|
||||
g_value_init (&return_value, G_TYPE_INT);
|
||||
g_signal_emitv ((GValue *) values->data, signal_id, 0, &return_value);
|
||||
g_assert_cmpint (count, ==, 1);
|
||||
g_assert_cmpint (g_value_get_int (&return_value), ==, -30);
|
||||
g_signal_remove_emission_hook (signal_id, hook);
|
||||
g_clear_signal_handler (&id, test);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
|
||||
"*return*value*generic-marshaller-int-return*NULL*");
|
||||
g_signal_emitv ((GValue *) values->data, signal_id, 0, NULL);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
g_value_unset (&return_value);
|
||||
g_value_init (&return_value, G_TYPE_FLOAT);
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
|
||||
"*return*value*generic-marshaller-int-return*gfloat*");
|
||||
g_signal_emitv ((GValue *) values->data, signal_id, 0, &return_value);
|
||||
g_test_assert_expected_messages ();
|
||||
#endif
|
||||
|
||||
g_object_unref (test);
|
||||
g_array_unref (values);
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
int
|
||||
@ -1839,6 +2066,7 @@ main (int argc,
|
||||
g_test_add_func ("/gobject/signals/custom-marshaller", test_custom_marshaller);
|
||||
g_test_add_func ("/gobject/signals/connect", test_connect);
|
||||
g_test_add_func ("/gobject/signals/emission-hook", test_emission_hook);
|
||||
g_test_add_func ("/gobject/signals/emitv", test_emitv);
|
||||
g_test_add_func ("/gobject/signals/accumulator", test_accumulator);
|
||||
g_test_add_func ("/gobject/signals/accumulator-class", test_accumulator_class);
|
||||
g_test_add_func ("/gobject/signals/introspection", test_introspection);
|
||||
|
Loading…
Reference in New Issue
Block a user