mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-07-23 19:24:38 +00:00
libnm-glib: queue added/removed signals and suppress uninitialized notifications
This is a straightforward copy of the changes done in libnm. It is done to cope
with test failures due to redundant or misordered signals. See
commit 52ae28f6e5
for a detailed explanation.
This commit is contained in:
parent
2e1e7ff0e6
commit
d681cbfd3d
|
@ -58,7 +58,7 @@ typedef struct {
|
|||
const char *signal_prefix;
|
||||
} PropertyInfo;
|
||||
|
||||
static void reload_complete (NMObject *object);
|
||||
static void reload_complete (NMObject *object, gboolean emit_now);
|
||||
|
||||
typedef struct {
|
||||
DBusGConnection *connection;
|
||||
|
@ -72,7 +72,7 @@ typedef struct {
|
|||
NMObject *parent;
|
||||
gboolean suppress_property_updates;
|
||||
|
||||
GSList *notify_props;
|
||||
GSList *notify_items;
|
||||
guint32 notify_id;
|
||||
gboolean inited;
|
||||
|
||||
|
@ -114,6 +114,27 @@ nm_object_error_quark (void)
|
|||
return quark;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
NOTIFY_SIGNAL_PENDING_NONE,
|
||||
NOTIFY_SIGNAL_PENDING_ADDED,
|
||||
NOTIFY_SIGNAL_PENDING_REMOVED,
|
||||
NOTIFY_SIGNAL_PENDING_ADDED_REMOVED,
|
||||
} NotifySignalPending;
|
||||
|
||||
typedef struct {
|
||||
const char *property;
|
||||
const char *signal_prefix;
|
||||
NotifySignalPending pending;
|
||||
NMObject *changed;
|
||||
} NotifyItem;
|
||||
|
||||
static void
|
||||
notify_item_free (NotifyItem *item)
|
||||
{
|
||||
g_clear_object (&item->changed);
|
||||
g_slice_free (NotifyItem, item);
|
||||
}
|
||||
|
||||
static void
|
||||
proxy_name_owner_changed (DBusGProxy *proxy,
|
||||
const char *name,
|
||||
|
@ -297,8 +318,8 @@ dispose (GObject *object)
|
|||
priv->notify_id = 0;
|
||||
}
|
||||
|
||||
g_slist_free_full (priv->notify_props, g_free);
|
||||
priv->notify_props = NULL;
|
||||
g_slist_free_full (priv->notify_items, (GDestroyNotify) notify_item_free);
|
||||
priv->notify_items = NULL;
|
||||
|
||||
g_slist_free_full (priv->property_interfaces, g_free);
|
||||
priv->property_interfaces = NULL;
|
||||
|
@ -484,52 +505,155 @@ deferred_notify_cb (gpointer data)
|
|||
{
|
||||
NMObject *object = NM_OBJECT (data);
|
||||
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
||||
NMObjectClass *object_class = NM_OBJECT_GET_CLASS (object);
|
||||
GSList *props, *iter;
|
||||
|
||||
priv->notify_id = 0;
|
||||
|
||||
/* Clear priv->notify_props early so that an NMObject subclass that
|
||||
/* Wait until all reloads are done before notifying */
|
||||
if (priv->reload_remaining)
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
/* Clear priv->notify_items early so that an NMObject subclass that
|
||||
* listens to property changes can queue up other property changes
|
||||
* during the g_object_notify() call separately from the property
|
||||
* list we're iterating.
|
||||
*/
|
||||
props = g_slist_reverse (priv->notify_props);
|
||||
priv->notify_props = NULL;
|
||||
props = g_slist_reverse (priv->notify_items);
|
||||
priv->notify_items = NULL;
|
||||
|
||||
g_object_ref (object);
|
||||
|
||||
/* Emit property change notifications first */
|
||||
for (iter = props; iter; iter = g_slist_next (iter)) {
|
||||
g_object_notify (G_OBJECT (object), (const char *) iter->data);
|
||||
g_free (iter->data);
|
||||
NotifyItem *item = iter->data;
|
||||
|
||||
if (item->property)
|
||||
g_object_notify (G_OBJECT (object), item->property);
|
||||
}
|
||||
|
||||
/* And added/removed signals second */
|
||||
for (iter = props; iter; iter = g_slist_next (iter)) {
|
||||
NotifyItem *item = iter->data;
|
||||
char buf[50];
|
||||
gint ret = 0;
|
||||
|
||||
switch (item->pending) {
|
||||
case NOTIFY_SIGNAL_PENDING_ADDED:
|
||||
ret = g_snprintf (buf, sizeof (buf), "%s-added", item->signal_prefix);
|
||||
break;
|
||||
case NOTIFY_SIGNAL_PENDING_REMOVED:
|
||||
ret = g_snprintf (buf, sizeof (buf), "%s-removed", item->signal_prefix);
|
||||
break;
|
||||
case NOTIFY_SIGNAL_PENDING_ADDED_REMOVED:
|
||||
// XXX
|
||||
if (object_class->object_creation_failed)
|
||||
object_class->object_creation_failed (object, NULL, g_strdup (nm_object_get_path (item->changed)));
|
||||
break;
|
||||
case NOTIFY_SIGNAL_PENDING_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ret > 0) {
|
||||
g_assert (ret < sizeof (buf));
|
||||
g_signal_emit_by_name (object, buf, item->changed);
|
||||
}
|
||||
}
|
||||
g_object_unref (object);
|
||||
|
||||
g_slist_free (props);
|
||||
return FALSE;
|
||||
g_slist_free_full (props, (GDestroyNotify) notify_item_free);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
_nm_object_defer_notify (NMObject *object)
|
||||
{
|
||||
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
||||
|
||||
if (!priv->notify_id)
|
||||
priv->notify_id = g_idle_add_full (G_PRIORITY_LOW, deferred_notify_cb, object, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
_nm_object_queue_notify_full (NMObject *object,
|
||||
const char *property,
|
||||
const char *signal_prefix,
|
||||
gboolean added,
|
||||
NMObject *changed)
|
||||
{
|
||||
NMObjectPrivate *priv;
|
||||
NotifyItem *item;
|
||||
GSList *iter;
|
||||
|
||||
g_return_if_fail (NM_IS_OBJECT (object));
|
||||
g_return_if_fail (!signal_prefix != !property);
|
||||
g_return_if_fail (!signal_prefix == !changed);
|
||||
|
||||
priv = NM_OBJECT_GET_PRIVATE (object);
|
||||
_nm_object_defer_notify (object);
|
||||
|
||||
property = g_intern_string (property);
|
||||
signal_prefix = g_intern_string (signal_prefix);
|
||||
for (iter = priv->notify_items; iter; iter = g_slist_next (iter)) {
|
||||
item = iter->data;
|
||||
|
||||
if (property && (property == item->property))
|
||||
return;
|
||||
|
||||
/* Collapse signals for the same object (such as "added->removed") to
|
||||
* ensure we don't emit signals when their sum should have no effect.
|
||||
* The "added->removed->removed" sequence requires special handling,
|
||||
* hence the addition of the ADDED_REMOVED state to ensure that no
|
||||
* signal is emitted in this case:
|
||||
*
|
||||
* Without the ADDED_REMOVED state:
|
||||
* NONE + added -> ADDED
|
||||
* ADDED + removed -> NONE
|
||||
* NONE + removed -> REMOVED (would emit 'removed' signal)
|
||||
*
|
||||
* With the ADDED_REMOVED state:
|
||||
* NONE | ADDED_REMOVED + added -> ADDED
|
||||
* ADDED + removed -> ADDED_REMOVED
|
||||
* ADDED_REMOVED + removed -> ADDED_REMOVED (emits no signal)
|
||||
*/
|
||||
if (signal_prefix && (changed == item->changed) && (item->signal_prefix == signal_prefix)) {
|
||||
switch (item->pending) {
|
||||
case NOTIFY_SIGNAL_PENDING_ADDED:
|
||||
if (!added)
|
||||
item->pending = NOTIFY_SIGNAL_PENDING_ADDED_REMOVED;
|
||||
break;
|
||||
case NOTIFY_SIGNAL_PENDING_REMOVED:
|
||||
if (added)
|
||||
item->pending = NOTIFY_SIGNAL_PENDING_NONE;
|
||||
break;
|
||||
case NOTIFY_SIGNAL_PENDING_ADDED_REMOVED:
|
||||
if (added)
|
||||
item->pending = NOTIFY_SIGNAL_PENDING_ADDED;
|
||||
break;
|
||||
case NOTIFY_SIGNAL_PENDING_NONE:
|
||||
item->pending = added ? NOTIFY_SIGNAL_PENDING_ADDED : NOTIFY_SIGNAL_PENDING_REMOVED;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
item = g_slice_new0 (NotifyItem);
|
||||
item->property = property;
|
||||
if (signal_prefix) {
|
||||
item->signal_prefix = signal_prefix;
|
||||
item->pending = added ? NOTIFY_SIGNAL_PENDING_ADDED : NOTIFY_SIGNAL_PENDING_REMOVED;
|
||||
item->changed = changed ? g_object_ref (changed) : NULL;
|
||||
}
|
||||
priv->notify_items = g_slist_prepend (priv->notify_items, item);
|
||||
}
|
||||
|
||||
void
|
||||
_nm_object_queue_notify (NMObject *object, const char *property)
|
||||
{
|
||||
NMObjectPrivate *priv;
|
||||
gboolean found = FALSE;
|
||||
GSList *iter;
|
||||
|
||||
g_return_if_fail (NM_IS_OBJECT (object));
|
||||
g_return_if_fail (property != NULL);
|
||||
|
||||
priv = NM_OBJECT_GET_PRIVATE (object);
|
||||
if (!priv->notify_id)
|
||||
priv->notify_id = g_idle_add_full (G_PRIORITY_LOW, deferred_notify_cb, object, NULL);
|
||||
|
||||
for (iter = priv->notify_props; iter; iter = g_slist_next (iter)) {
|
||||
if (!strcmp ((char *) iter->data, property)) {
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
priv->notify_props = g_slist_prepend (priv->notify_props, g_strdup (property));
|
||||
_nm_object_queue_notify_full (object, property, NULL, FALSE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -759,17 +883,12 @@ array_diff (GPtrArray *needles, GPtrArray *haystack, GPtrArray *diff)
|
|||
}
|
||||
|
||||
static void
|
||||
emit_added_removed_signal (NMObject *self,
|
||||
const char *signal_prefix,
|
||||
NMObject *changed,
|
||||
gboolean added)
|
||||
queue_added_removed_signal (NMObject *self,
|
||||
const char *signal_prefix,
|
||||
NMObject *changed,
|
||||
gboolean added)
|
||||
{
|
||||
char buf[50];
|
||||
int ret;
|
||||
|
||||
ret = g_snprintf (buf, sizeof (buf), "%s-%s", signal_prefix, added ? "added" : "removed");
|
||||
g_assert (ret < sizeof (buf));
|
||||
g_signal_emit_by_name (self, buf, changed);
|
||||
_nm_object_queue_notify_full (self, NULL, signal_prefix, added, changed);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -809,17 +928,17 @@ object_property_complete (ObjectCreatedData *odata)
|
|||
|
||||
/* Emit added & removed */
|
||||
for (i = 0; i < removed->len; i++) {
|
||||
emit_added_removed_signal (self,
|
||||
pi->signal_prefix,
|
||||
g_ptr_array_index (removed, i),
|
||||
FALSE);
|
||||
queue_added_removed_signal (self,
|
||||
pi->signal_prefix,
|
||||
g_ptr_array_index (removed, i),
|
||||
FALSE);
|
||||
}
|
||||
|
||||
for (i = 0; i < added->len; i++) {
|
||||
emit_added_removed_signal (self,
|
||||
pi->signal_prefix,
|
||||
g_ptr_array_index (added, i),
|
||||
TRUE);
|
||||
queue_added_removed_signal (self,
|
||||
pi->signal_prefix,
|
||||
g_ptr_array_index (added, i),
|
||||
TRUE);
|
||||
}
|
||||
|
||||
different = removed->len || added->len;
|
||||
|
@ -850,8 +969,8 @@ object_property_complete (ObjectCreatedData *odata)
|
|||
if (different && odata->property_name)
|
||||
_nm_object_queue_notify (self, odata->property_name);
|
||||
|
||||
if (priv->reload_results && --priv->reload_remaining == 0)
|
||||
reload_complete (self);
|
||||
if (--priv->reload_remaining == 0)
|
||||
reload_complete (self, FALSE);
|
||||
|
||||
g_object_unref (self);
|
||||
g_free (odata->objects);
|
||||
|
@ -898,8 +1017,7 @@ handle_object_property (NMObject *self, const char *property_name, GValue *value
|
|||
odata->array = FALSE;
|
||||
odata->property_name = property_name;
|
||||
|
||||
if (priv->reload_results)
|
||||
priv->reload_remaining++;
|
||||
priv->reload_remaining++;
|
||||
|
||||
path = g_value_get_boxed (value);
|
||||
|
||||
|
@ -946,8 +1064,7 @@ handle_object_array_property (NMObject *self, const char *property_name, GValue
|
|||
odata->array = TRUE;
|
||||
odata->property_name = property_name;
|
||||
|
||||
if (priv->reload_results)
|
||||
priv->reload_remaining++;
|
||||
priv->reload_remaining++;
|
||||
|
||||
if (paths->len == 0) {
|
||||
object_property_complete (odata);
|
||||
|
@ -1214,6 +1331,8 @@ _nm_object_reload_properties (NMObject *object, GError **error)
|
|||
if (!priv->property_interfaces || !priv->nm_running)
|
||||
return TRUE;
|
||||
|
||||
priv->reload_remaining++;
|
||||
|
||||
for (p = priv->property_interfaces; p; p = p->next) {
|
||||
if (!dbus_g_proxy_call (priv->properties_proxy, "GetAll", error,
|
||||
G_TYPE_STRING, p->data,
|
||||
|
@ -1226,6 +1345,9 @@ _nm_object_reload_properties (NMObject *object, GError **error)
|
|||
g_hash_table_destroy (props);
|
||||
}
|
||||
|
||||
if (--priv->reload_remaining == 0)
|
||||
reload_complete (object, TRUE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -1322,13 +1444,22 @@ _nm_object_set_property (NMObject *object,
|
|||
}
|
||||
|
||||
static void
|
||||
reload_complete (NMObject *object)
|
||||
reload_complete (NMObject *object, gboolean emit_now)
|
||||
{
|
||||
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
||||
GSimpleAsyncResult *simple;
|
||||
GSList *results, *iter;
|
||||
GError *error;
|
||||
|
||||
if (emit_now) {
|
||||
if (priv->notify_id) {
|
||||
g_source_remove (priv->notify_id);
|
||||
priv->notify_id = 0;
|
||||
}
|
||||
deferred_notify_cb (object);
|
||||
} else
|
||||
_nm_object_defer_notify (object);
|
||||
|
||||
results = priv->reload_results;
|
||||
priv->reload_results = NULL;
|
||||
error = priv->reload_error;
|
||||
|
@ -1371,7 +1502,7 @@ reload_got_properties (DBusGProxy *proxy, DBusGProxyCall *call,
|
|||
}
|
||||
|
||||
if (--priv->reload_remaining == 0)
|
||||
reload_complete (object);
|
||||
reload_complete (object, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in a new issue