checkpoint: let each checkpoint schedule its own timeout

Instead of scheduling one timeout only, let each checkpoint instance
individually schedule a timeout. This has some overhead, but glib
is supposed to make scheduling many timers efficient. Otherwise,
glib should be fixed.

This simplifies in my opinion the code, because it's up to each
checkpoint to maintain its own timeout.

Later we will also add a AdjustRollbackTimeout operation, which
allow to reschedule the timeout. It also seems slightly simpler,
if scheduling of the timeout is done by the NMCheckpoint instance
itself.
This commit is contained in:
Thomas Haller
2018-03-27 19:02:15 +02:00
parent 6f28749ad4
commit 5fb65b7f96
3 changed files with 65 additions and 66 deletions

View File

@@ -36,7 +36,6 @@ struct _NMCheckpointManager {
NMManager *_manager;
GParamSpec *property_spec;
CList checkpoints_lst_head;
guint rollback_timeout_id;
};
#define GET_MANAGER(self) \
@@ -57,10 +56,6 @@ struct _NMCheckpointManager {
/*****************************************************************************/
static void update_rollback_timeout (NMCheckpointManager *self);
/*****************************************************************************/
static void
notify_checkpoints (NMCheckpointManager *self) {
g_object_notify_by_pspec ((GObject *) GET_MANAGER (self),
@@ -74,6 +69,8 @@ destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint)
nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (checkpoint)));
nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
nm_checkpoint_set_timeout_callback (checkpoint, NULL, NULL);
c_list_unlink (&checkpoint->checkpoints_lst);
notify_checkpoints (self);
@@ -92,51 +89,14 @@ rollback_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint)
return result;
}
static gboolean
rollback_timeout_cb (NMCheckpointManager *self)
{
NMCheckpoint *checkpoint, *checkpoint_safe;
gint64 ts, now;
self->rollback_timeout_id = 0;
now = nm_utils_get_monotonic_timestamp_ms ();
c_list_for_each_entry_safe (checkpoint, checkpoint_safe, &self->checkpoints_lst_head, checkpoints_lst) {
ts = nm_checkpoint_get_rollback_ts (checkpoint);
if (ts && ts <= now) {
gs_unref_variant GVariant *result = NULL;
result = rollback_checkpoint (self, checkpoint);
}
}
update_rollback_timeout (self);
return G_SOURCE_REMOVE;
}
static void
update_rollback_timeout (NMCheckpointManager *self)
rollback_timeout_cb (NMCheckpoint *checkpoint,
gpointer user_data)
{
NMCheckpoint *checkpoint;
gint64 ts, delta, next = G_MAXINT64;
NMCheckpointManager *self = user_data;
gs_unref_variant GVariant *result = NULL;
c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) {
ts = nm_checkpoint_get_rollback_ts (checkpoint);
if (ts && ts < next)
next = ts;
}
nm_clear_g_source (&self->rollback_timeout_id);
if (next != G_MAXINT64) {
delta = MAX (next - nm_utils_get_monotonic_timestamp_ms (), 0);
self->rollback_timeout_id = g_timeout_add (delta,
(GSourceFunc) rollback_timeout_cb,
self);
_LOGT ("update timeout: next check in %" G_GINT64_FORMAT " ms", delta);
}
result = rollback_checkpoint (self, checkpoint);
}
static NMCheckpoint *
@@ -224,9 +184,9 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
nm_dbus_object_export (NM_DBUS_OBJECT (checkpoint));
nm_checkpoint_set_timeout_callback (checkpoint, rollback_timeout_cb, self);
c_list_link_tail (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst);
notify_checkpoints (self);
update_rollback_timeout (self);
return checkpoint;
}
@@ -361,6 +321,5 @@ nm_checkpoint_manager_free (NMCheckpointManager *self)
return;
nm_checkpoint_manager_destroy_all (self);
nm_clear_g_source (&self->rollback_timeout_id);
g_slice_free (NMCheckpointManager, self);
}

View File

@@ -57,13 +57,17 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE (
struct _NMCheckpointPrivate {
/* properties */
GHashTable *devices;
gint64 created;
guint32 rollback_timeout;
gint64 created_at_ms;
guint32 rollback_timeout_s;
guint timeout_id;
/* private members */
/* private members */
NMManager *manager;
gint64 rollback_ts;
NMCheckpointCreateFlags flags;
GHashTable *connection_uuids;
NMCheckpointTimeoutCallback timeout_cb;
gpointer timeout_data;
};
struct _NMCheckpointClass {
@@ -96,12 +100,18 @@ G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_DBUS_OBJECT)
/*****************************************************************************/
guint64
nm_checkpoint_get_rollback_ts (NMCheckpoint *self)
void
nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
NMCheckpointTimeoutCallback callback,
gpointer user_data)
{
g_return_val_if_fail (NM_IS_CHECKPOINT (self), 0);
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
return NM_CHECKPOINT_GET_PRIVATE (self)->rollback_ts;
/* in glib world, we would have a GSignal for this. But as there
* is only one subscriber, it's simpler to just set and unset(!)
* the callback this way. */
priv->timeout_cb = callback;
priv->timeout_data = user_data;
}
gboolean
@@ -374,6 +384,7 @@ device_checkpoint_create (NMDevice *device)
NMActRequest *act_request;
nm_assert (NM_IS_DEVICE (device));
nm_assert (nm_device_is_real (device));
path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device));
@@ -421,6 +432,21 @@ device_checkpoint_destroy (gpointer data)
g_slice_free (DeviceCheckpoint, dev_checkpoint);
}
static gboolean
_timeout_cb (gpointer user_data)
{
NMCheckpoint *self = user_data;
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
priv->timeout_id = 0;
if (priv->timeout_cb)
priv->timeout_cb (self, priv->timeout_data);
/* beware, @self likely got destroyed! */
return G_SOURCE_REMOVE;
}
/*****************************************************************************/
static void
@@ -437,10 +463,12 @@ get_property (GObject *object, guint prop_id,
FALSE);
break;
case PROP_CREATED:
g_value_set_int64 (value, priv->created);
g_value_set_int64 (value,
nm_utils_monotonic_timestamp_as_boottime (priv->created_at_ms,
NM_UTILS_NS_PER_MSEC));
break;
case PROP_ROLLBACK_TIMEOUT:
g_value_set_uint (value, priv->rollback_timeout);
g_value_set_uint (value, priv->rollback_timeout_s);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -466,12 +494,13 @@ nm_checkpoint_init (NMCheckpoint *self)
}
NMCheckpoint *
nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout_s,
NMCheckpointCreateFlags flags, GError **error)
{
NMCheckpoint *self;
NMCheckpointPrivate *priv;
NMSettingsConnection *const *con;
gint64 rollback_timeout_ms;
guint i;
g_return_val_if_fail (manager, NULL);
@@ -490,14 +519,17 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
priv = NM_CHECKPOINT_GET_PRIVATE (self);
priv->manager = manager;
priv->created = nm_utils_monotonic_timestamp_as_boottime (nm_utils_get_monotonic_timestamp_ms (),
NM_UTILS_NS_PER_MSEC);
priv->rollback_timeout = rollback_timeout;
priv->rollback_ts = rollback_timeout ?
(nm_utils_get_monotonic_timestamp_ms () + ((gint64) rollback_timeout * 1000)) :
0;
priv->rollback_timeout_s = rollback_timeout_s;
priv->created_at_ms = nm_utils_get_monotonic_timestamp_ms ();
priv->flags = flags;
if (rollback_timeout_s != 0) {
rollback_timeout_ms = ((gint64) rollback_timeout_s) * 1000;
priv->timeout_id = g_timeout_add (NM_MIN (rollback_timeout_ms, (gint64) G_MAXUINT32),
_timeout_cb,
self);
}
if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
priv->connection_uuids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) {
@@ -528,6 +560,8 @@ dispose (GObject *object)
g_clear_pointer (&priv->devices, g_hash_table_unref);
g_clear_pointer (&priv->connection_uuids, g_hash_table_unref);
nm_clear_g_source (&priv->timeout_id);
G_OBJECT_CLASS (nm_checkpoint_parent_class)->dispose (object);
}

View File

@@ -50,7 +50,13 @@ GType nm_checkpoint_get_type (void);
NMCheckpoint *nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
NMCheckpointCreateFlags flags, GError **error);
guint64 nm_checkpoint_get_rollback_ts (NMCheckpoint *checkpoint);
typedef void (*NMCheckpointTimeoutCallback) (NMCheckpoint *self,
gpointer user_data);
void nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
NMCheckpointTimeoutCallback callback,
gpointer user_data);
gboolean nm_checkpoint_includes_device (NMCheckpoint *checkpoint, NMDevice *device);
GVariant *nm_checkpoint_rollback (NMCheckpoint *self);