diff options
author | Jiří Klimeš <jklimes@redhat.com> | 2014-12-10 08:33:08 +0100 |
---|---|---|
committer | Jiří Klimeš <jklimes@redhat.com> | 2014-12-15 15:23:49 +0100 |
commit | 9ec818ac2c5b89315d6f126ba6355a8817098931 (patch) | |
tree | b18ddca51da4ea7ccb953162fd41ea39a03bef38 | |
parent | 1b1d3ca32570f7351e2364aa113878cb7606866e (diff) | |
download | NetworkManager-9ec818ac2c5b89315d6f126ba6355a8817098931.tar.gz |
libnm: emit all NMDevice::state-changed signals
When multiple state-changed signals was issued in fast succession, libnm
only emitted the last one. This is not optimal, because clients lose
information. Fix that by queueing the signals and emit all of them.
Improves commit 829234fd.
-rw-r--r-- | libnm/nm-device.c | 65 |
1 files changed, 44 insertions, 21 deletions
diff --git a/libnm/nm-device.c b/libnm/nm-device.c index e80ac707d8..43eca3a465 100644 --- a/libnm/nm-device.c +++ b/libnm/nm-device.c @@ -90,7 +90,7 @@ typedef struct { NMIPConfig *ip6_config; NMDhcpConfig *dhcp6_config; NMDeviceState state; - NMDeviceState last_seen_state; + GSList *state_data_queue; NMDeviceStateReason reason; NMActiveConnection *active_connection; @@ -219,36 +219,57 @@ typedef struct { } StateChangeData; static void +free_state_change_data (gpointer data) +{ + g_slice_free (StateChangeData, data); +} + +static void +emit_state_changed (NMDevice *device, StateChangeData *data) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + GSList *item, *iter, *old_list; + + item = g_slist_find (priv->state_data_queue, data); + + if (!item) + return; + + old_list = priv->state_data_queue; + priv->state_data_queue = item->next; + item->next = NULL; + + /* Emit all signals in the list end free it */ + for (iter = old_list; iter; iter = g_slist_next (iter)) { + StateChangeData *tmp = iter->data; + + /* Ensure that nm_device_get_state() will return the right value even if + * we haven't processed the corresponding PropertiesChanged yet. + */ + priv->state = tmp->new_state; + + g_signal_emit (device, signals[STATE_CHANGED], 0, + tmp->new_state, tmp->old_state, tmp->reason); + } + g_slist_free_full (old_list, free_state_change_data); +} + +static void device_state_change_reloaded (GObject *object, GAsyncResult *result, gpointer user_data) { NMDevice *self = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); StateChangeData *data = user_data; - NMDeviceState old_state = data->old_state; - NMDeviceState new_state = data->new_state; - NMDeviceStateReason reason = data->reason; - - g_slice_free (StateChangeData, data); _nm_object_reload_properties_finish (NM_OBJECT (object), result, NULL); /* If the device changes state several times in rapid succession, then we'll * queue several reload_properties() calls, and there's no guarantee that - * they'll finish in the right order. In that case, only emit the signal - * for the last one. + * they'll finish in the right order. In that case, we use our own queue for + * emitting state-changed signals. */ - if (priv->last_seen_state != new_state) - return; - - /* Ensure that nm_device_get_state() will return the right value even if - * we haven't processed the corresponding PropertiesChanged yet. - */ - priv->state = new_state; - - g_signal_emit (self, signals[STATE_CHANGED], 0, - new_state, old_state, reason); + emit_state_changed (self, data); } static void @@ -270,12 +291,12 @@ device_state_changed (NMDBusDevice *proxy, * in the process of asynchronously reading the new values. * Wait for that to finish before emitting the signal. */ - priv->last_seen_state = new_state; - data = g_slice_new (StateChangeData); data->old_state = old_state; data->new_state = new_state; data->reason = reason; + + priv->state_data_queue = g_slist_append (priv->state_data_queue, data); _nm_object_reload_properties_async (NM_OBJECT (user_data), NULL, device_state_change_reloaded, @@ -368,6 +389,8 @@ finalize (GObject *object) g_free (priv->type_description); g_free (priv->physical_port_id); + g_slist_free_full (priv->state_data_queue, free_state_change_data); + G_OBJECT_CLASS (nm_device_parent_class)->finalize (object); } |