| /* |
| * Copyright (C) 2018 Richard Hughes <richard@hughsie.com> |
| * |
| * SPDX-License-Identifier: LGPL-2.1+ |
| */ |
| |
| #define G_LOG_DOMAIN "FuIdle" |
| |
| #include "config.h" |
| |
| #include <glib-object.h> |
| |
| #include "fu-idle.h" |
| #include "fu-mutex.h" |
| |
| static void fu_idle_finalize (GObject *obj); |
| |
| struct _FuIdle |
| { |
| GObject parent_instance; |
| GPtrArray *items; /* of FuIdleItem */ |
| GRWLock items_mutex; |
| guint idle_id; |
| guint timeout; |
| FwupdStatus status; |
| }; |
| |
| enum { |
| PROP_0, |
| PROP_STATUS, |
| PROP_LAST |
| }; |
| |
| static void |
| fu_idle_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) |
| { |
| FuIdle *self = FU_IDLE (object); |
| switch (prop_id) { |
| case PROP_STATUS: |
| g_value_set_uint (value, self->status); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| fu_idle_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) |
| { |
| switch (prop_id) { |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| typedef struct { |
| gchar *reason; |
| guint32 token; |
| } FuIdleItem; |
| |
| G_DEFINE_TYPE (FuIdle, fu_idle, G_TYPE_OBJECT) |
| |
| FwupdStatus |
| fu_idle_get_status (FuIdle *self) |
| { |
| g_return_val_if_fail (FU_IS_IDLE (self), FWUPD_STATUS_UNKNOWN); |
| return self->status; |
| } |
| |
| static void |
| fu_idle_set_status (FuIdle *self, FwupdStatus status) |
| { |
| if (self->status == status) |
| return; |
| self->status = status; |
| g_debug ("status now %s", fwupd_status_to_string (status)); |
| g_object_notify (G_OBJECT (self), "status"); |
| } |
| |
| static gboolean |
| fu_idle_check_cb (gpointer user_data) |
| { |
| FuIdle *self = FU_IDLE (user_data); |
| fu_idle_set_status (self, FWUPD_STATUS_SHUTDOWN); |
| return G_SOURCE_CONTINUE; |
| } |
| |
| static void |
| fu_idle_start (FuIdle *self) |
| { |
| if (self->idle_id != 0) |
| return; |
| if (self->timeout == 0) |
| return; |
| self->idle_id = g_timeout_add_seconds (self->timeout, fu_idle_check_cb, self); |
| } |
| |
| static void |
| fu_idle_stop (FuIdle *self) |
| { |
| if (self->idle_id == 0) |
| return; |
| g_source_remove (self->idle_id); |
| self->idle_id = 0; |
| } |
| |
| void |
| fu_idle_reset (FuIdle *self) |
| { |
| g_return_if_fail (FU_IS_IDLE (self)); |
| fu_idle_stop (self); |
| if (self->items->len == 0) |
| fu_idle_start (self); |
| } |
| |
| void |
| fu_idle_uninhibit (FuIdle *self, guint32 token) |
| { |
| g_autoptr(GRWLockWriterLocker) locker = g_rw_lock_writer_locker_new (&self->items_mutex); |
| |
| g_return_if_fail (FU_IS_IDLE (self)); |
| g_return_if_fail (token != 0); |
| g_return_if_fail (locker != NULL); |
| |
| for (guint i = 0; i < self->items->len; i++) { |
| FuIdleItem *item = g_ptr_array_index (self->items, i); |
| if (item->token == token) { |
| g_debug ("uninhibiting: %s", item->reason); |
| g_ptr_array_remove_index (self->items, i); |
| break; |
| } |
| } |
| fu_idle_reset (self); |
| } |
| |
| guint32 |
| fu_idle_inhibit (FuIdle *self, const gchar *reason) |
| { |
| FuIdleItem *item; |
| g_autoptr(GRWLockWriterLocker) locker = g_rw_lock_writer_locker_new (&self->items_mutex); |
| |
| g_return_val_if_fail (FU_IS_IDLE (self), 0); |
| g_return_val_if_fail (reason != NULL, 0); |
| g_return_val_if_fail (locker != NULL, 0); |
| |
| g_debug ("inhibiting: %s", reason); |
| item = g_new0 (FuIdleItem, 1); |
| item->reason = g_strdup (reason); |
| item->token = g_random_int_range (1, G_MAXINT); |
| g_ptr_array_add (self->items, item); |
| fu_idle_reset (self); |
| return item->token; |
| } |
| |
| void |
| fu_idle_set_timeout (FuIdle *self, guint timeout) |
| { |
| g_return_if_fail (FU_IS_IDLE (self)); |
| g_debug ("setting timeout to %us", timeout); |
| self->timeout = timeout; |
| fu_idle_reset (self); |
| } |
| |
| static void |
| fu_idle_item_free (FuIdleItem *item) |
| { |
| g_free (item->reason); |
| g_free (item); |
| } |
| |
| FuIdleLocker * |
| fu_idle_locker_new (FuIdle *self, const gchar *reason) |
| { |
| FuIdleLocker *locker = g_new0 (FuIdleLocker, 1); |
| locker->idle = g_object_ref (self); |
| locker->token = fu_idle_inhibit (self, reason); |
| return locker; |
| } |
| |
| void |
| fu_idle_locker_free (FuIdleLocker *locker) |
| { |
| if (locker == NULL) |
| return; |
| fu_idle_uninhibit (locker->idle, locker->token); |
| g_object_unref (locker->idle); |
| g_free (locker); |
| } |
| |
| static void |
| fu_idle_class_init (FuIdleClass *klass) |
| { |
| GObjectClass *object_class = G_OBJECT_CLASS (klass); |
| GParamSpec *pspec; |
| |
| object_class->finalize = fu_idle_finalize; |
| object_class->get_property = fu_idle_get_property; |
| object_class->set_property = fu_idle_set_property; |
| |
| pspec = g_param_spec_uint ("status", NULL, NULL, |
| FWUPD_STATUS_UNKNOWN, |
| FWUPD_STATUS_LAST, |
| FWUPD_STATUS_UNKNOWN, |
| G_PARAM_READABLE | |
| G_PARAM_STATIC_NAME); |
| g_object_class_install_property (object_class, PROP_STATUS, pspec); |
| } |
| |
| static void |
| fu_idle_init (FuIdle *self) |
| { |
| self->status = FWUPD_STATUS_IDLE; |
| self->items = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_idle_item_free); |
| g_rw_lock_init (&self->items_mutex); |
| } |
| |
| static void |
| fu_idle_finalize (GObject *obj) |
| { |
| FuIdle *self = FU_IDLE (obj); |
| |
| fu_idle_stop (self); |
| g_rw_lock_clear (&self->items_mutex); |
| g_ptr_array_unref (self->items); |
| |
| G_OBJECT_CLASS (fu_idle_parent_class)->finalize (obj); |
| } |
| |
| FuIdle * |
| fu_idle_new (void) |
| { |
| FuIdle *self; |
| self = g_object_new (FU_TYPE_IDLE, NULL); |
| return FU_IDLE (self); |
| } |