feedback-manager: Track DBus clients and end feedbacks if they go away
When the client goes away we build up a list of events to end feedback for to make sure the hash table isn't modified in place. tThe alternative approach would be to disconnect the on_event_feedbacks_ended handler, duplicate the DBus signal emission and use g_hash_table_foreach_remove but that looks less robust than using the same code path. Closes: #25
This commit is contained in:
@@ -47,7 +47,10 @@ typedef struct _FbdFeedbackManager {
|
|||||||
FbdFeedbackTheme *theme;
|
FbdFeedbackTheme *theme;
|
||||||
guint next_id;
|
guint next_id;
|
||||||
|
|
||||||
|
/* Key: event id, value: event */
|
||||||
GHashTable *events;
|
GHashTable *events;
|
||||||
|
/* Key: DBus name, value: watch_id */
|
||||||
|
GHashTable *clients;
|
||||||
|
|
||||||
/* Hardware interaction */
|
/* Hardware interaction */
|
||||||
GUdevClient *client;
|
GUdevClient *client;
|
||||||
@@ -214,6 +217,72 @@ on_feedbackd_setting_changed (FbdFeedbackManager *self,
|
|||||||
fbd_feedback_manager_set_profile (self, profile);
|
fbd_feedback_manager_set_profile (self, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_client_vanished (GDBusConnection *connection,
|
||||||
|
const gchar *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
FbdFeedbackManager *self = FBD_FEEDBACK_MANAGER (user_data);
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
FbdEvent *event;
|
||||||
|
GSList *l;
|
||||||
|
g_autoptr (GSList) events = NULL;
|
||||||
|
|
||||||
|
g_return_if_fail (name);
|
||||||
|
|
||||||
|
g_debug ("Client %s vanished", name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare a list of events to end feedback for so we don't modify
|
||||||
|
* the hash table in place when 'feedbacks-ended' fires.
|
||||||
|
*/
|
||||||
|
g_hash_table_iter_init (&iter, self->events);
|
||||||
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
||||||
|
event = FBD_EVENT (value);
|
||||||
|
if (!g_strcmp0 (fbd_event_get_sender (event), name))
|
||||||
|
events = g_slist_append (events, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (l = events; l; l = l->next) {
|
||||||
|
event = l->data;
|
||||||
|
g_debug ("Ending event %s (%d) since %s vanished",
|
||||||
|
fbd_event_get_event (event),
|
||||||
|
fbd_event_get_id (event),
|
||||||
|
name);
|
||||||
|
fbd_event_end_feedbacks (event);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_remove (self->clients, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
watch_client (FbdFeedbackManager *self, GDBusMethodInvocation *invocation)
|
||||||
|
{
|
||||||
|
guint watch_id;
|
||||||
|
GDBusConnection *conn = g_dbus_method_invocation_get_connection (invocation);
|
||||||
|
const char *sender = g_dbus_method_invocation_get_sender (invocation);
|
||||||
|
|
||||||
|
watch_id = g_bus_watch_name_on_connection (conn,
|
||||||
|
sender,
|
||||||
|
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||||
|
NULL,
|
||||||
|
on_client_vanished,
|
||||||
|
self,
|
||||||
|
NULL);
|
||||||
|
g_hash_table_insert (self->clients, g_strdup (sender), GUINT_TO_POINTER (watch_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_client_watch (gpointer data)
|
||||||
|
{
|
||||||
|
guint watch_id = GPOINTER_TO_UINT (data);
|
||||||
|
|
||||||
|
if (watch_id == 0)
|
||||||
|
return;
|
||||||
|
g_bus_unwatch_name (watch_id);
|
||||||
|
}
|
||||||
|
|
||||||
static FbdFeedbackProfileLevel
|
static FbdFeedbackProfileLevel
|
||||||
get_max_level (FbdFeedbackProfileLevel global_level,
|
get_max_level (FbdFeedbackProfileLevel global_level,
|
||||||
FbdFeedbackProfileLevel app_level,
|
FbdFeedbackProfileLevel app_level,
|
||||||
@@ -320,6 +389,7 @@ fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object,
|
|||||||
self,
|
self,
|
||||||
G_CONNECT_SWAPPED);
|
G_CONNECT_SWAPPED);
|
||||||
fbd_event_run_feedbacks (event);
|
fbd_event_run_feedbacks (event);
|
||||||
|
watch_client (self, invocation);
|
||||||
} else {
|
} else {
|
||||||
g_hash_table_remove (self->events, GUINT_TO_POINTER (event_id));
|
g_hash_table_remove (self->events, GUINT_TO_POINTER (event_id));
|
||||||
lfb_gdbus_feedback_emit_feedback_ended (LFB_GDBUS_FEEDBACK (self), event_id,
|
lfb_gdbus_feedback_emit_feedback_ended (LFB_GDBUS_FEEDBACK (self), event_id,
|
||||||
@@ -451,6 +521,7 @@ fbd_feedback_manager_dispose (GObject *object)
|
|||||||
g_clear_object (&self->leds);
|
g_clear_object (&self->leds);
|
||||||
g_clear_object (&self->client);
|
g_clear_object (&self->client);
|
||||||
g_clear_pointer (&self->events, g_hash_table_destroy);
|
g_clear_pointer (&self->events, g_hash_table_destroy);
|
||||||
|
g_clear_pointer (&self->clients, g_hash_table_destroy);
|
||||||
|
|
||||||
G_OBJECT_CLASS (fbd_feedback_manager_parent_class)->dispose (object);
|
G_OBJECT_CLASS (fbd_feedback_manager_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
@@ -488,6 +559,10 @@ fbd_feedback_manager_init (FbdFeedbackManager *self)
|
|||||||
g_direct_equal,
|
g_direct_equal,
|
||||||
NULL,
|
NULL,
|
||||||
(GDestroyNotify)g_object_unref);
|
(GDestroyNotify)g_object_unref);
|
||||||
|
self->clients = g_hash_table_new_full (g_str_hash,
|
||||||
|
g_str_equal,
|
||||||
|
g_free,
|
||||||
|
free_client_watch);
|
||||||
}
|
}
|
||||||
|
|
||||||
FbdFeedbackManager *
|
FbdFeedbackManager *
|
||||||
|
|||||||
Reference in New Issue
Block a user