Richard Hughes | 75b965d | 2018-11-15 13:51:21 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 Richard Hughes <richard@hughsie.com> |
| 3 | * |
| 4 | * SPDX-License-Identifier: LGPL-2.1+ |
| 5 | */ |
| 6 | |
| 7 | #define G_LOG_DOMAIN "FuIdle" |
| 8 | |
| 9 | #include "config.h" |
| 10 | |
| 11 | #include <glib-object.h> |
| 12 | |
| 13 | #include "fu-idle.h" |
| 14 | #include "fu-mutex.h" |
| 15 | |
| 16 | static void fu_idle_finalize (GObject *obj); |
| 17 | |
| 18 | struct _FuIdle |
| 19 | { |
| 20 | GObject parent_instance; |
| 21 | GPtrArray *items; /* of FuIdleItem */ |
Richard Hughes | 161e9b5 | 2019-06-12 14:22:45 +0100 | [diff] [blame] | 22 | GRWLock items_mutex; |
Richard Hughes | 75b965d | 2018-11-15 13:51:21 +0000 | [diff] [blame] | 23 | guint idle_id; |
| 24 | guint timeout; |
| 25 | FwupdStatus status; |
| 26 | }; |
| 27 | |
| 28 | enum { |
| 29 | PROP_0, |
| 30 | PROP_STATUS, |
| 31 | PROP_LAST |
| 32 | }; |
| 33 | |
| 34 | static void |
| 35 | fu_idle_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) |
| 36 | { |
| 37 | FuIdle *self = FU_IDLE (object); |
| 38 | switch (prop_id) { |
| 39 | case PROP_STATUS: |
| 40 | g_value_set_uint (value, self->status); |
| 41 | break; |
| 42 | default: |
| 43 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| 44 | break; |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | static void |
| 49 | fu_idle_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) |
| 50 | { |
| 51 | switch (prop_id) { |
| 52 | default: |
| 53 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| 54 | break; |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | typedef struct { |
| 59 | gchar *reason; |
| 60 | guint32 token; |
| 61 | } FuIdleItem; |
| 62 | |
| 63 | G_DEFINE_TYPE (FuIdle, fu_idle, G_TYPE_OBJECT) |
| 64 | |
| 65 | FwupdStatus |
| 66 | fu_idle_get_status (FuIdle *self) |
| 67 | { |
| 68 | g_return_val_if_fail (FU_IS_IDLE (self), FWUPD_STATUS_UNKNOWN); |
| 69 | return self->status; |
| 70 | } |
| 71 | |
| 72 | static void |
| 73 | fu_idle_set_status (FuIdle *self, FwupdStatus status) |
| 74 | { |
| 75 | if (self->status == status) |
| 76 | return; |
| 77 | self->status = status; |
| 78 | g_debug ("status now %s", fwupd_status_to_string (status)); |
| 79 | g_object_notify (G_OBJECT (self), "status"); |
| 80 | } |
| 81 | |
| 82 | static gboolean |
| 83 | fu_idle_check_cb (gpointer user_data) |
| 84 | { |
| 85 | FuIdle *self = FU_IDLE (user_data); |
| 86 | fu_idle_set_status (self, FWUPD_STATUS_SHUTDOWN); |
| 87 | return G_SOURCE_CONTINUE; |
| 88 | } |
| 89 | |
| 90 | static void |
| 91 | fu_idle_start (FuIdle *self) |
| 92 | { |
| 93 | if (self->idle_id != 0) |
| 94 | return; |
| 95 | if (self->timeout == 0) |
| 96 | return; |
| 97 | self->idle_id = g_timeout_add_seconds (self->timeout, fu_idle_check_cb, self); |
| 98 | } |
| 99 | |
| 100 | static void |
| 101 | fu_idle_stop (FuIdle *self) |
| 102 | { |
| 103 | if (self->idle_id == 0) |
| 104 | return; |
| 105 | g_source_remove (self->idle_id); |
| 106 | self->idle_id = 0; |
| 107 | } |
| 108 | |
| 109 | void |
| 110 | fu_idle_reset (FuIdle *self) |
| 111 | { |
| 112 | g_return_if_fail (FU_IS_IDLE (self)); |
| 113 | fu_idle_stop (self); |
| 114 | if (self->items->len == 0) |
| 115 | fu_idle_start (self); |
| 116 | } |
| 117 | |
| 118 | void |
| 119 | fu_idle_uninhibit (FuIdle *self, guint32 token) |
| 120 | { |
Richard Hughes | 0fe4914 | 2019-11-22 16:56:38 +0000 | [diff] [blame] | 121 | g_autoptr(GRWLockWriterLocker) locker = g_rw_lock_writer_locker_new (&self->items_mutex); |
Richard Hughes | 75b965d | 2018-11-15 13:51:21 +0000 | [diff] [blame] | 122 | |
| 123 | g_return_if_fail (FU_IS_IDLE (self)); |
| 124 | g_return_if_fail (token != 0); |
| 125 | g_return_if_fail (locker != NULL); |
| 126 | |
| 127 | for (guint i = 0; i < self->items->len; i++) { |
| 128 | FuIdleItem *item = g_ptr_array_index (self->items, i); |
| 129 | if (item->token == token) { |
| 130 | g_debug ("uninhibiting: %s", item->reason); |
| 131 | g_ptr_array_remove_index (self->items, i); |
| 132 | break; |
| 133 | } |
| 134 | } |
| 135 | fu_idle_reset (self); |
| 136 | } |
| 137 | |
| 138 | guint32 |
| 139 | fu_idle_inhibit (FuIdle *self, const gchar *reason) |
| 140 | { |
| 141 | FuIdleItem *item; |
Richard Hughes | 0fe4914 | 2019-11-22 16:56:38 +0000 | [diff] [blame] | 142 | g_autoptr(GRWLockWriterLocker) locker = g_rw_lock_writer_locker_new (&self->items_mutex); |
Richard Hughes | 75b965d | 2018-11-15 13:51:21 +0000 | [diff] [blame] | 143 | |
| 144 | g_return_val_if_fail (FU_IS_IDLE (self), 0); |
| 145 | g_return_val_if_fail (reason != NULL, 0); |
| 146 | g_return_val_if_fail (locker != NULL, 0); |
| 147 | |
| 148 | g_debug ("inhibiting: %s", reason); |
| 149 | item = g_new0 (FuIdleItem, 1); |
| 150 | item->reason = g_strdup (reason); |
| 151 | item->token = g_random_int_range (1, G_MAXINT); |
| 152 | g_ptr_array_add (self->items, item); |
| 153 | fu_idle_reset (self); |
| 154 | return item->token; |
| 155 | } |
| 156 | |
| 157 | void |
| 158 | fu_idle_set_timeout (FuIdle *self, guint timeout) |
| 159 | { |
| 160 | g_return_if_fail (FU_IS_IDLE (self)); |
| 161 | g_debug ("setting timeout to %us", timeout); |
| 162 | self->timeout = timeout; |
| 163 | fu_idle_reset (self); |
| 164 | } |
| 165 | |
| 166 | static void |
| 167 | fu_idle_item_free (FuIdleItem *item) |
| 168 | { |
| 169 | g_free (item->reason); |
| 170 | g_free (item); |
| 171 | } |
| 172 | |
| 173 | FuIdleLocker * |
| 174 | fu_idle_locker_new (FuIdle *self, const gchar *reason) |
| 175 | { |
| 176 | FuIdleLocker *locker = g_new0 (FuIdleLocker, 1); |
| 177 | locker->idle = g_object_ref (self); |
| 178 | locker->token = fu_idle_inhibit (self, reason); |
| 179 | return locker; |
| 180 | } |
| 181 | |
| 182 | void |
| 183 | fu_idle_locker_free (FuIdleLocker *locker) |
| 184 | { |
| 185 | if (locker == NULL) |
| 186 | return; |
| 187 | fu_idle_uninhibit (locker->idle, locker->token); |
| 188 | g_object_unref (locker->idle); |
| 189 | g_free (locker); |
| 190 | } |
| 191 | |
| 192 | static void |
| 193 | fu_idle_class_init (FuIdleClass *klass) |
| 194 | { |
| 195 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
| 196 | GParamSpec *pspec; |
| 197 | |
| 198 | object_class->finalize = fu_idle_finalize; |
| 199 | object_class->get_property = fu_idle_get_property; |
| 200 | object_class->set_property = fu_idle_set_property; |
| 201 | |
| 202 | pspec = g_param_spec_uint ("status", NULL, NULL, |
| 203 | FWUPD_STATUS_UNKNOWN, |
| 204 | FWUPD_STATUS_LAST, |
| 205 | FWUPD_STATUS_UNKNOWN, |
| 206 | G_PARAM_READABLE | |
| 207 | G_PARAM_STATIC_NAME); |
| 208 | g_object_class_install_property (object_class, PROP_STATUS, pspec); |
| 209 | } |
| 210 | |
| 211 | static void |
| 212 | fu_idle_init (FuIdle *self) |
| 213 | { |
| 214 | self->status = FWUPD_STATUS_IDLE; |
| 215 | self->items = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_idle_item_free); |
Richard Hughes | 161e9b5 | 2019-06-12 14:22:45 +0100 | [diff] [blame] | 216 | g_rw_lock_init (&self->items_mutex); |
Richard Hughes | 75b965d | 2018-11-15 13:51:21 +0000 | [diff] [blame] | 217 | } |
| 218 | |
| 219 | static void |
| 220 | fu_idle_finalize (GObject *obj) |
| 221 | { |
| 222 | FuIdle *self = FU_IDLE (obj); |
| 223 | |
| 224 | fu_idle_stop (self); |
Richard Hughes | 161e9b5 | 2019-06-12 14:22:45 +0100 | [diff] [blame] | 225 | g_rw_lock_clear (&self->items_mutex); |
Richard Hughes | aae22e4 | 2020-06-22 21:32:59 +0100 | [diff] [blame] | 226 | g_ptr_array_unref (self->items); |
Richard Hughes | 75b965d | 2018-11-15 13:51:21 +0000 | [diff] [blame] | 227 | |
| 228 | G_OBJECT_CLASS (fu_idle_parent_class)->finalize (obj); |
| 229 | } |
| 230 | |
| 231 | FuIdle * |
| 232 | fu_idle_new (void) |
| 233 | { |
| 234 | FuIdle *self; |
| 235 | self = g_object_new (FU_TYPE_IDLE, NULL); |
| 236 | return FU_IDLE (self); |
| 237 | } |