libnm: order the property updates
Don't let a later property update finish than the sooner one. This wouldn't happen most of time, apart from a special case when the latter update of a object array property is to an empty list. In that case the latter update would complete sooner and when the earlier update finishes the list would contain objects which are supposed to be gone already.
This commit is contained in:
@@ -95,6 +95,8 @@ typedef struct {
|
|||||||
GSList *reload_results;
|
GSList *reload_results;
|
||||||
guint reload_remaining;
|
guint reload_remaining;
|
||||||
GError *reload_error;
|
GError *reload_error;
|
||||||
|
|
||||||
|
GSList *pending; /* ordered list of pending property updates. */
|
||||||
} NMObjectPrivate;
|
} NMObjectPrivate;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -447,7 +449,7 @@ typedef struct {
|
|||||||
GObject **objects;
|
GObject **objects;
|
||||||
int length, remaining;
|
int length, remaining;
|
||||||
|
|
||||||
GPtrArray *array;
|
gboolean array;
|
||||||
const char *property_name;
|
const char *property_name;
|
||||||
} ObjectCreatedData;
|
} ObjectCreatedData;
|
||||||
|
|
||||||
@@ -458,12 +460,10 @@ odata_free (gpointer data)
|
|||||||
|
|
||||||
g_object_unref (odata->self);
|
g_object_unref (odata->self);
|
||||||
g_free (odata->objects);
|
g_free (odata->objects);
|
||||||
if (odata->array)
|
|
||||||
g_ptr_array_unref (odata->array);
|
|
||||||
g_slice_free (ObjectCreatedData, odata);
|
g_slice_free (ObjectCreatedData, odata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void object_property_maybe_complete (ObjectCreatedData *odata);
|
static void object_property_maybe_complete (NMObject *self);
|
||||||
|
|
||||||
|
|
||||||
typedef void (*NMObjectCreateCallbackFunc) (GObject *, const char *, gpointer);
|
typedef void (*NMObjectCreateCallbackFunc) (GObject *, const char *, gpointer);
|
||||||
@@ -523,8 +523,7 @@ create_async_inited (GObject *object, GAsyncResult *result, gpointer user_data)
|
|||||||
while (priv->waiters) {
|
while (priv->waiters) {
|
||||||
odata = priv->waiters->data;
|
odata = priv->waiters->data;
|
||||||
priv->waiters = g_slist_remove (priv->waiters, odata);
|
priv->waiters = g_slist_remove (priv->waiters, odata);
|
||||||
object_property_maybe_complete (odata);
|
object_property_maybe_complete (odata->self);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -729,104 +728,117 @@ already_awaits (ObjectCreatedData *odata, GObject *object)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
object_property_maybe_complete (ObjectCreatedData *odata)
|
object_property_maybe_complete (NMObject *self)
|
||||||
{
|
{
|
||||||
NMObject *self = odata->self;
|
|
||||||
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
|
||||||
PropertyInfo *pi = odata->pi;
|
/* The odata may hold the last reference. */
|
||||||
gboolean different = TRUE;
|
_nm_unused gs_unref_object NMObject *self_keep_alive = g_object_ref (self);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Only complete the array property load when all the objects are initialized. */
|
while (priv->pending) {
|
||||||
for (i = 0; i < odata->length; i++) {
|
ObjectCreatedData *odata = priv->pending->data;
|
||||||
GObject *obj = odata->objects[i];
|
PropertyInfo *pi = odata->pi;
|
||||||
NMObjectPrivate *obj_priv;
|
gboolean different = TRUE;
|
||||||
|
|
||||||
/* Could not load the object. Perhaps it was removed. */
|
if (odata->remaining > 0)
|
||||||
if (!obj)
|
return;
|
||||||
continue;
|
|
||||||
|
|
||||||
obj_priv = NM_OBJECT_GET_PRIVATE (obj);
|
/* Only complete the array property load when all the objects are initialized. */
|
||||||
if (!obj_priv->inited) {
|
for (i = 0; i < odata->length; i++) {
|
||||||
|
GObject *obj = odata->objects[i];
|
||||||
|
NMObjectPrivate *obj_priv;
|
||||||
|
|
||||||
/* The object is not finished because we block its creation. */
|
/* Could not load the object. Perhaps it was removed. */
|
||||||
if (already_awaits (odata, obj))
|
if (!obj)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!g_slist_find (obj_priv->waiters, odata))
|
obj_priv = NM_OBJECT_GET_PRIVATE (obj);
|
||||||
obj_priv->waiters = g_slist_prepend (obj_priv->waiters, odata);
|
if (!obj_priv->inited) {
|
||||||
return;
|
|
||||||
|
/* The object is not finished because we block its creation. */
|
||||||
|
if (already_awaits (odata, obj))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!g_slist_find (obj_priv->waiters, odata))
|
||||||
|
obj_priv->waiters = g_slist_prepend (obj_priv->waiters, odata);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (odata->array) {
|
if (odata->array) {
|
||||||
GPtrArray *pi_old = *((GPtrArray **) pi->field);
|
GPtrArray *old = *((GPtrArray **) pi->field);
|
||||||
GPtrArray *old = odata->array;
|
GPtrArray *new;
|
||||||
GPtrArray *new;
|
|
||||||
|
|
||||||
/* Build up new array */
|
/* Build up new array */
|
||||||
new = g_ptr_array_new_full (odata->length, g_object_unref);
|
new = g_ptr_array_new_full (odata->length, g_object_unref);
|
||||||
for (i = 0; i < odata->length; i++)
|
for (i = 0; i < odata->length; i++)
|
||||||
add_to_object_array_unique (new, odata->objects[i]);
|
add_to_object_array_unique (new, odata->objects[i]);
|
||||||
|
|
||||||
*((GPtrArray **) pi->field) = new;
|
*((GPtrArray **) pi->field) = new;
|
||||||
|
|
||||||
if (pi->signal_prefix) {
|
if (pi->signal_prefix) {
|
||||||
GPtrArray *added = g_ptr_array_sized_new (3);
|
GPtrArray *added = g_ptr_array_sized_new (3);
|
||||||
GPtrArray *removed = g_ptr_array_sized_new (3);
|
GPtrArray *removed = g_ptr_array_sized_new (3);
|
||||||
|
|
||||||
/* Find objects in 'old' that do not exist in 'new' */
|
if (old) {
|
||||||
array_diff (old, new, removed);
|
/* Find objects in 'old' that do not exist in 'new' */
|
||||||
|
array_diff (old, new, removed);
|
||||||
|
|
||||||
/* Find objects in 'new' that do not exist in old */
|
/* Find objects in 'new' that do not exist in old */
|
||||||
array_diff (new, old, added);
|
array_diff (new, old, added);
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < new->len; i++)
|
||||||
|
g_ptr_array_add (added, g_ptr_array_index (new, i));
|
||||||
|
}
|
||||||
|
|
||||||
/* Emit added & removed */
|
/* Emit added & removed */
|
||||||
for (i = 0; i < removed->len; i++) {
|
for (i = 0; i < removed->len; i++) {
|
||||||
queue_added_removed_signal (self,
|
queue_added_removed_signal (self,
|
||||||
pi->signal_prefix,
|
pi->signal_prefix,
|
||||||
g_ptr_array_index (removed, i),
|
g_ptr_array_index (removed, i),
|
||||||
FALSE);
|
FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < added->len; i++) {
|
||||||
|
queue_added_removed_signal (self,
|
||||||
|
pi->signal_prefix,
|
||||||
|
g_ptr_array_index (added, i),
|
||||||
|
TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
different = removed->len || added->len;
|
||||||
|
g_ptr_array_unref (added);
|
||||||
|
g_ptr_array_unref (removed);
|
||||||
|
} else {
|
||||||
|
/* No added/removed signals to send, just replace the property with
|
||||||
|
* the new values.
|
||||||
|
*/
|
||||||
|
different = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < added->len; i++) {
|
/* Free old array last since it will release references, thus freeing
|
||||||
queue_added_removed_signal (self,
|
* any objects in the 'removed' array.
|
||||||
pi->signal_prefix,
|
|
||||||
g_ptr_array_index (added, i),
|
|
||||||
TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
different = removed->len || added->len;
|
|
||||||
g_ptr_array_unref (added);
|
|
||||||
g_ptr_array_unref (removed);
|
|
||||||
} else {
|
|
||||||
/* No added/removed signals to send, just replace the property with
|
|
||||||
* the new values.
|
|
||||||
*/
|
*/
|
||||||
different = TRUE;
|
if (old)
|
||||||
|
g_ptr_array_unref (old);
|
||||||
|
} else {
|
||||||
|
GObject **obj_p = pi->field;
|
||||||
|
|
||||||
|
different = (*obj_p != odata->objects[0]);
|
||||||
|
if (*obj_p)
|
||||||
|
g_object_unref (*obj_p);
|
||||||
|
*obj_p = odata->objects[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free old array last since it will release references, thus freeing
|
if (different && odata->property_name)
|
||||||
* any objects in the 'removed' array.
|
_nm_object_queue_notify (self, odata->property_name);
|
||||||
*/
|
|
||||||
if (pi_old)
|
|
||||||
g_ptr_array_unref (pi_old);
|
|
||||||
} else {
|
|
||||||
GObject **obj_p = pi->field;
|
|
||||||
|
|
||||||
different = (*obj_p != odata->objects[0]);
|
if (--priv->reload_remaining == 0)
|
||||||
if (*obj_p)
|
reload_complete (self, TRUE);
|
||||||
g_object_unref (*obj_p);
|
|
||||||
*obj_p = odata->objects[0];
|
priv->pending = g_slist_remove (priv->pending, odata);
|
||||||
|
odata_free (odata);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (different && odata->property_name)
|
|
||||||
_nm_object_queue_notify (self, odata->property_name);
|
|
||||||
|
|
||||||
if (--priv->reload_remaining == 0)
|
|
||||||
reload_complete (self, FALSE);
|
|
||||||
|
|
||||||
odata_free (odata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -844,8 +856,7 @@ object_created (GObject *obj, const char *path, gpointer user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
odata->objects[--odata->remaining] = obj;
|
odata->objects[--odata->remaining] = obj;
|
||||||
if (!odata->remaining)
|
object_property_maybe_complete (odata->self);
|
||||||
object_property_maybe_complete (odata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@@ -862,9 +873,10 @@ handle_object_property (NMObject *self, const char *property_name, GVariant *val
|
|||||||
odata->pi = pi;
|
odata->pi = pi;
|
||||||
odata->objects = g_new0 (GObject *, 1);
|
odata->objects = g_new0 (GObject *, 1);
|
||||||
odata->length = odata->remaining = 1;
|
odata->length = odata->remaining = 1;
|
||||||
odata->array = NULL;
|
odata->array = FALSE;
|
||||||
odata->property_name = property_name;
|
odata->property_name = property_name;
|
||||||
|
|
||||||
|
priv->pending = g_slist_append (priv->pending, odata);
|
||||||
priv->reload_remaining++;
|
priv->reload_remaining++;
|
||||||
|
|
||||||
path = g_variant_get_string (value, NULL);
|
path = g_variant_get_string (value, NULL);
|
||||||
@@ -901,7 +913,6 @@ handle_object_array_property (NMObject *self, const char *property_name, GVarian
|
|||||||
GPtrArray **array = pi->field;
|
GPtrArray **array = pi->field;
|
||||||
const char *path;
|
const char *path;
|
||||||
ObjectCreatedData *odata;
|
ObjectCreatedData *odata;
|
||||||
guint i, len = *array ? (*array)->len : 0;
|
|
||||||
|
|
||||||
npaths = g_variant_n_children (value);
|
npaths = g_variant_n_children (value);
|
||||||
|
|
||||||
@@ -910,17 +921,14 @@ handle_object_array_property (NMObject *self, const char *property_name, GVarian
|
|||||||
odata->pi = pi;
|
odata->pi = pi;
|
||||||
odata->objects = g_new0 (GObject *, npaths);
|
odata->objects = g_new0 (GObject *, npaths);
|
||||||
odata->length = odata->remaining = npaths;
|
odata->length = odata->remaining = npaths;
|
||||||
|
odata->array = TRUE;
|
||||||
odata->property_name = property_name;
|
odata->property_name = property_name;
|
||||||
|
|
||||||
/* Objects known at this point. */
|
priv->pending = g_slist_append (priv->pending, odata);
|
||||||
odata->array = g_ptr_array_new_full (len, g_object_unref);
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
g_ptr_array_add (odata->array, g_object_ref (g_ptr_array_index (*array, i)));
|
|
||||||
|
|
||||||
priv->reload_remaining++;
|
priv->reload_remaining++;
|
||||||
|
|
||||||
if (npaths == 0) {
|
if (npaths == 0) {
|
||||||
object_property_maybe_complete (odata);
|
object_property_maybe_complete (self);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user