summaryrefslogtreecommitdiff
path: root/src/up-daemon.c
diff options
context:
space:
mode:
authorBenjamin Berg <bberg@redhat.com>2022-05-09 11:22:03 +0200
committerBenjamin Berg <bberg@redhat.com>2022-05-10 09:31:46 +0200
commit2a3da575d1ba586030a983ad2d63ad8f7d4b2700 (patch)
tree7499279d638c0470150de544e4df893893a3983e /src/up-daemon.c
parent3b287504ce26758ca46bbbc49a38096899d895af (diff)
downloadupower-2a3da575d1ba586030a983ad2d63ad8f7d4b2700.tar.gz
daemon: Trigger device refresh based on new properties
This uses a GSource and a simple looping approach to trigger device refresh when needed. We don't use the g_timeout_add_seconds rounding again to synchronize with other applications, but we will eventually poll everything at the same time. Also, we will not do wakeups if an update happens regularily enough due to e.g. a uevent.
Diffstat (limited to 'src/up-daemon.c')
-rw-r--r--src/up-daemon.c91
1 files changed, 91 insertions, 0 deletions
diff --git a/src/up-daemon.c b/src/up-daemon.c
index b645137..70ca495 100644
--- a/src/up-daemon.c
+++ b/src/up-daemon.c
@@ -45,6 +45,7 @@ struct UpDaemonPrivate
GHashTable *poll_timeouts;
gboolean poll_paused;
GHashTable *idle_signals;
+ GSource *poll_source;
int critical_action_lock_fd;
/* Display battery properties */
@@ -751,10 +752,19 @@ static void
up_daemon_device_changed_cb (UpDevice *device, GParamSpec *pspec, UpDaemon *daemon)
{
UpDeviceKind type;
+ const char *prop;
g_return_if_fail (UP_IS_DAEMON (daemon));
g_return_if_fail (UP_IS_DEVICE (device));
+ prop = g_param_spec_get_name (pspec);
+ if (!daemon->priv->poll_paused &&
+ ((g_strcmp0 (prop, "poll-timeout") == 0) ||
+ (g_strcmp0 (prop, "last-refresh") == 0))) {
+ g_source_set_ready_time (daemon->priv->poll_source, 0);
+ return;
+ }
+
/* refresh battery devices when AC state changes */
g_object_get (device,
"type", &type,
@@ -767,6 +777,76 @@ up_daemon_device_changed_cb (UpDevice *device, GParamSpec *pspec, UpDaemon *daem
up_daemon_update_warning_level (daemon);
}
+static gboolean
+up_daemon_poll_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+ UpDaemon *daemon = UP_DAEMON (user_data);
+ UpDaemonPrivate *priv = daemon->priv;
+ g_autoptr(GPtrArray) array = NULL;
+ guint i;
+ UpDevice *device;
+ gint64 ready_time = G_MAXINT64;
+ gint64 now = g_source_get_time (priv->poll_source);
+ gint max_dispatch_timeout = 0;
+
+ g_source_set_ready_time (priv->poll_source, -1);
+ g_assert (callback == NULL);
+
+ if (daemon->priv->poll_paused)
+ return G_SOURCE_CONTINUE;
+
+ /* Find the earliest device that needs a refresh. */
+ array = up_device_list_get_array (priv->power_devices);
+ for (i = 0; i < array->len; i += 1) {
+ gint timeout;
+ gint64 last_refresh;
+ gint64 poll_time;
+ gint64 dispatch_time;
+ device = (UpDevice *) g_ptr_array_index (array, i);
+ g_object_get (device,
+ "poll-timeout", &timeout,
+ "last-refresh", &last_refresh,
+ NULL);
+
+ if (timeout <= 0)
+ continue;
+
+ poll_time = last_refresh + timeout * G_USEC_PER_SEC;
+
+ /* Allow dispatching early if another device got dispatched.
+ * i.e. device polling will synchronize eventually.
+ */
+ dispatch_time = poll_time - MIN(timeout, max_dispatch_timeout) * G_USEC_PER_SEC / 2;
+
+ if (now >= dispatch_time) {
+ g_debug ("up_daemon_poll_dispatch: refreshing %s", up_exported_device_get_native_path (UP_EXPORTED_DEVICE (device)));
+ up_device_refresh_internal (device, UP_REFRESH_POLL);
+ max_dispatch_timeout = MAX(max_dispatch_timeout, timeout);
+
+ /* We'll wake up again immediately and then
+ * calculate the correct time to re-poll. */
+ }
+
+ ready_time = MIN(ready_time, poll_time);
+ }
+
+ if (ready_time == G_MAXINT64)
+ ready_time = -1;
+
+ /* Set the ready time (if it was not modified externally) */
+ if (g_source_get_ready_time (priv->poll_source) == -1)
+ g_source_set_ready_time (priv->poll_source, ready_time);
+
+ return G_SOURCE_CONTINUE;
+}
+
+GSourceFuncs poll_source_funcs = {
+ .prepare = NULL,
+ .check = NULL,
+ .dispatch = up_daemon_poll_dispatch,
+ .finalize = NULL,
+};
+
typedef struct {
guint id;
guint timeout;
@@ -1029,6 +1109,9 @@ up_daemon_device_added_cb (UpBackend *backend, UpDevice *device, UpDaemon *daemo
g_signal_connect (device, "notify",
G_CALLBACK (up_daemon_device_changed_cb), daemon);
+ /* Ensure we poll the new device if needed */
+ g_source_set_ready_time (daemon->priv->poll_source, 0);
+
/* emit */
object_path = up_device_get_object_path (device);
g_debug ("emitting added: %s", object_path);
@@ -1130,6 +1213,13 @@ up_daemon_init (UpDaemon *daemon)
daemon->priv->config = up_config_new ();
daemon->priv->power_devices = up_device_list_new ();
daemon->priv->display_device = up_device_new (daemon, NULL);
+ daemon->priv->poll_source = g_source_new (&poll_source_funcs, sizeof (GSource));
+
+ g_source_set_callback (daemon->priv->poll_source, NULL, daemon, NULL);
+ g_source_set_name (daemon->priv->poll_source, "up-device-poll");
+ g_source_attach (daemon->priv->poll_source, NULL);
+ /* g_source_destroy removes the last reference */
+ g_source_unref (daemon->priv->poll_source);
daemon->priv->use_percentage_for_policy = up_config_get_boolean (daemon->priv->config, "UsePercentageForPolicy");
load_percentage_policy (daemon, FALSE);
@@ -1205,6 +1295,7 @@ up_daemon_finalize (GObject *object)
g_clear_pointer (&priv->poll_timeouts, g_hash_table_destroy);
g_clear_pointer (&priv->idle_signals, g_hash_table_destroy);
+ g_clear_pointer (&daemon->priv->poll_source, g_source_destroy);
g_object_unref (priv->power_devices);
g_object_unref (priv->display_device);