*
* D-Bus helper library
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
#define error(fmt...)
#define debug(fmt...)
+static DBusHandlerResult name_exit_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
static guint listener_id = 0;
static GSList *name_listeners = NULL;
struct name_callback {
- GDBusWatchFunction func;
+ GDBusWatchFunction conn_func;
+ GDBusWatchFunction disc_func;
void *user_data;
guint id;
};
DBusConnection *connection;
char *name;
GSList *callbacks;
+ GSList *processed;
+ gboolean lock;
};
static struct name_data *name_data_find(DBusConnection *connection,
current != NULL; current = current->next) {
struct name_data *data = current->data;
- if (name == NULL && data->name == NULL) {
- if (connection == data->connection)
- return data;
- } else {
- if (strcmp(name, data->name) == 0)
- return data;
- }
+ if (connection != data->connection)
+ continue;
+
+ if (name == NULL || g_str_equal(name, data->name))
+ return data;
}
return NULL;
}
-static struct name_callback *name_callback_find(GSList *callbacks,
- GDBusWatchFunction func, void *user_data)
+static struct name_callback *name_callback_find(GSList *callbacks, guint id)
{
GSList *current;
for (current = callbacks; current != NULL; current = current->next) {
struct name_callback *cb = current->data;
- if (cb->func == func && cb->user_data == user_data)
+ if (cb->id == id)
return cb;
}
for (l = data->callbacks; l != NULL; l = l->next) {
struct name_callback *cb = l->data;
- if (cb->func)
- cb->func(cb->user_data);
+ if (cb->disc_func)
+ cb->disc_func(data->connection, cb->user_data);
g_free(cb);
}
}
static int name_data_add(DBusConnection *connection, const char *name,
- GDBusWatchFunction func, void *user_data, guint id)
+ GDBusWatchFunction connect,
+ GDBusWatchFunction disconnect,
+ void *user_data, guint id)
{
int first = 1;
struct name_data *data = NULL;
cb = g_new(struct name_callback, 1);
- cb->func = func;
+ cb->conn_func = connect;
+ cb->disc_func = disconnect;
cb->user_data = user_data;
cb->id = id;
name_listeners = g_slist_append(name_listeners, data);
done:
- data->callbacks = g_slist_append(data->callbacks, cb);
+ if (data->lock)
+ data->processed = g_slist_append(data->processed, cb);
+ else
+ data->callbacks = g_slist_append(data->callbacks, cb);
+
return first;
}
static void name_data_remove(DBusConnection *connection,
- const char *name, GDBusWatchFunction func, void *user_data)
+ const char *name, guint id)
{
struct name_data *data;
struct name_callback *cb = NULL;
if (!data)
return;
- cb = name_callback_find(data->callbacks, func, user_data);
+ cb = name_callback_find(data->callbacks, id);
if (cb) {
data->callbacks = g_slist_remove(data->callbacks, cb);
g_free(cb);
}
- if (!data->callbacks) {
- name_listeners = g_slist_remove(name_listeners, data);
- name_data_free(data);
- }
+ if (data->callbacks)
+ return;
+
+ name_listeners = g_slist_remove(name_listeners, data);
+ name_data_free(data);
+
+ /* Remove filter if there are no listeners left for the connection */
+ data = name_data_find(connection, NULL);
+ if (!data)
+ dbus_connection_remove_filter(connection,
+ name_exit_filter,
+ NULL);
}
static gboolean add_match(DBusConnection *connection, const char *name)
static DBusHandlerResult name_exit_filter(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
- GSList *l;
struct name_data *data;
+ struct name_callback *cb;
char *name, *old, *new;
if (!dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
- /* We are not interested of service creations */
- if (*new != '\0')
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
data = name_data_find(connection, name);
if (!data) {
error("Got NameOwnerChanged signal for %s which has no listeners", name);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
- for (l = data->callbacks; l != NULL; l = l->next) {
- struct name_callback *cb = l->data;
- cb->func(cb->user_data);
+ data->lock = TRUE;
+
+ while (data->callbacks) {
+ cb = data->callbacks->data;
+
+ if (*new == '\0') {
+ if (cb->disc_func)
+ cb->disc_func(connection, cb->user_data);
+ } else {
+ if (cb->conn_func)
+ cb->conn_func(connection, cb->user_data);
+ }
+
+ /* Check if the watch was removed/freed by the callback
+ * function */
+ if (!g_slist_find(data->callbacks, cb))
+ continue;
+
+ data->callbacks = g_slist_remove(data->callbacks, cb);
+
+ if (!cb->conn_func || !cb->disc_func) {
+ g_free(cb);
+ continue;
+ }
+
+ data->processed = g_slist_append(data->processed, cb);
}
+ data->callbacks = data->processed;
+ data->processed = NULL;
+ data->lock = FALSE;
+
+ if (data->callbacks)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
name_listeners = g_slist_remove(name_listeners, data);
name_data_free(data);
+ /* Remove filter if there no listener left for the connection */
+ data = name_data_find(connection, NULL);
+ if (!data)
+ dbus_connection_remove_filter(connection, name_exit_filter,
+ NULL);
+
remove_match(connection, name);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
-guint g_dbus_add_disconnect_watch(DBusConnection *connection,
- const char *name,
- GDBusWatchFunction func,
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction connect,
+ GDBusWatchFunction disconnect,
void *user_data, GDBusDestroyFunction destroy)
{
int first;
- if (!listener_id) {
+ if (!name_data_find(connection, NULL)) {
if (!dbus_connection_add_filter(connection,
name_exit_filter, NULL, NULL)) {
error("dbus_connection_add_filter() failed");
}
listener_id++;
- first = name_data_add(connection, name, func, user_data, listener_id);
+ first = name_data_add(connection, name, connect, disconnect,
+ user_data, listener_id);
/* The filter is already added if this is not the first callback
* registration for the name */
if (!first)
debug("name_listener_add(%s)", name);
if (!add_match(connection, name)) {
- name_data_remove(connection, name, func, user_data);
+ name_data_remove(connection, name, listener_id);
return 0;
}
}
return listener_id;
}
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction func,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ return g_dbus_add_service_watch(connection, name, NULL, func,
+ user_data, destroy);
+}
+
guint g_dbus_add_signal_watch(DBusConnection *connection,
const char *rule, GDBusSignalFunction function,
void *user_data, GDBusDestroyFunction destroy)
if (cb->id == id)
goto remove;
}
+ for (lcb = data->processed; lcb; lcb = lcb->next) {
+ cb = lcb->data;
+ if (cb->id == id)
+ goto remove;
+ }
}
return FALSE;
remove:
data->callbacks = g_slist_remove(data->callbacks, cb);
+ data->processed = g_slist_remove(data->processed, cb);
g_free(cb);
- /* Don't remove the filter if other callbacks exist */
- if (data->callbacks)
+ /* Don't remove the filter if other callbacks exist or data is lock
+ * processing callbacks */
+ if (data->callbacks || data->lock)
return TRUE;
if (data->name) {
name_listeners = g_slist_remove(name_listeners, data);
name_data_free(data);
+ /* Remove filter if there are no listeners left for the connection */
+ data = name_data_find(connection, NULL);
+ if (!data)
+ dbus_connection_remove_filter(connection, name_exit_filter,
+ NULL);
+
return TRUE;
}
{
struct name_data *data;
- data = name_data_find(connection, NULL);
- if (!data) {
- error("name_listener_indicate_disconnect: no listener found");
- return;
+ while ((data = name_data_find(connection, NULL))) {
+ name_listeners = g_slist_remove(name_listeners, data);
+ name_data_call_and_free(data);
}
- debug("name_listener_indicate_disconnect");
-
- name_data_call_and_free(data);
+ dbus_connection_remove_filter(connection, name_exit_filter, NULL);
}