GDBusCapabilityFlags
g_dbus_connection_get_capabilities
g_dbus_connection_get_peer_credentials
+g_dbus_connection_get_last_serial
GDBusCallFlags
g_dbus_connection_call
g_dbus_connection_call_finish
GHashTable *map_object_path_to_es; /* gchar* -> ExportedSubtree* */
GHashTable *map_id_to_es; /* guint -> ExportedSubtree* */
+ /* Map used for storing last used serials for each thread, protected by @lock */
+ GHashTable *map_thread_to_last_serial;
+
/* Structure used for message filters, protected by @lock */
GPtrArray *filters;
g_hash_table_unref (connection->map_id_to_es);
g_hash_table_unref (connection->map_object_path_to_es);
+ g_hash_table_unref (connection->map_thread_to_last_serial);
+
g_main_context_unref (connection->main_context_at_construction);
g_free (connection->machine_id);
connection->map_id_to_es = g_hash_table_new (g_direct_hash,
g_direct_equal);
+ connection->map_thread_to_last_serial = g_hash_table_new (g_direct_hash,
+ g_direct_equal);
+
connection->main_context_at_construction = g_main_context_ref_thread_default ();
connection->filters = g_ptr_array_new ();
/* ---------------------------------------------------------------------------------------------------- */
+/**
+ * g_dbus_connection_get_last_serial:
+ * @connection: A #GDBusConnection.
+ *
+ * Retrieves the last serial number assigned to a #GDBusMessage on
+ * the current thread. This includes messages sent via both low-level
+ * API such as g_dbus_connection_send_message() as well as
+ * high-level API such as g_dbus_connection_emit_signal(),
+ * g_dbus_connection_call() or g_dbus_proxy_call().
+ *
+ * Returns: the last used serial or zero when no message has been sent
+ * within the current thread.
+ *
+ * Since: 2.34
+ */
+guint32
+g_dbus_connection_get_last_serial (GDBusConnection *connection)
+{
+ guint32 ret;
+
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
+
+ CONNECTION_LOCK (connection);
+ ret = GPOINTER_TO_UINT (g_hash_table_lookup (connection->map_thread_to_last_serial,
+ g_thread_self ()));
+ CONNECTION_UNLOCK (connection);
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
/* Can be called by any thread, with the connection lock held */
static gboolean
g_dbus_connection_send_message_unlocked (GDBusConnection *connection,
if (out_serial != NULL)
*out_serial = serial_to_use;
+ /* store used serial for the current thread */
+ /* TODO: watch the thread disposal and remove associated record
+ * from hashtable
+ * - see https://bugzilla.gnome.org/show_bug.cgi?id=676825#c7
+ */
+ g_hash_table_replace (connection->map_thread_to_last_serial,
+ g_thread_self (),
+ GUINT_TO_POINTER (serial_to_use));
+
if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL))
g_dbus_message_set_serial (message, serial_to_use);
const gchar *g_dbus_connection_get_guid (GDBusConnection *connection);
const gchar *g_dbus_connection_get_unique_name (GDBusConnection *connection);
GCredentials *g_dbus_connection_get_peer_credentials (GDBusConnection *connection);
+
+GLIB_AVAILABLE_IN_2_34
+guint32 g_dbus_connection_get_last_serial (GDBusConnection *connection);
+
gboolean g_dbus_connection_get_exit_on_close (GDBusConnection *connection);
void g_dbus_connection_set_exit_on_close (GDBusConnection *connection,
gboolean exit_on_close);
g_dbus_connection_get_exit_on_close
g_dbus_connection_get_guid
g_dbus_connection_get_peer_credentials
+g_dbus_connection_get_last_serial
g_dbus_connection_get_stream
g_dbus_connection_get_unique_name
g_dbus_connection_is_closed
session_bus_down ();
}
+/* ---------------------------------------------------------------------------------------------------- */
+
+#define NUM_THREADS 50
+
+static void
+send_bogus_message (GDBusConnection *c, guint32 *out_serial)
+{
+ GDBusMessage *m;
+ GError *error;
+
+ m = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */
+ "/org/freedesktop/DBus", /* path */
+ "org.freedesktop.DBus", /* interface */
+ "GetNameOwner");
+ g_dbus_message_set_body (m, g_variant_new ("(s)", "org.freedesktop.DBus"));
+ error = NULL;
+ g_dbus_connection_send_message (c, m, G_DBUS_SEND_MESSAGE_FLAGS_NONE, out_serial, &error);
+ g_assert_no_error (error);
+}
+
+static gpointer
+serials_thread_func (GDBusConnection *c)
+{
+ guint32 message_serial;
+
+ /* No calls on this thread yet */
+ g_assert_cmpint (g_dbus_connection_get_last_serial(c), ==, 0);
+
+ /* Send a bogus message and store its serial */
+ message_serial = 0;
+ send_bogus_message (c, &message_serial);
+
+ /* Give it some time to actually send the message out */
+ g_usleep (250000);
+
+ g_assert_cmpint (g_dbus_connection_get_last_serial(c), !=, 0);
+ g_assert_cmpint (g_dbus_connection_get_last_serial(c), ==, message_serial);
+
+ return NULL;
+}
+
+static void
+test_connection_serials (void)
+{
+ GDBusConnection *c;
+ GError *error;
+ GThread *pool[NUM_THREADS];
+ int i;
+
+ session_bus_up ();
+
+ error = NULL;
+ c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (c != NULL);
+
+ /* Status after initialization */
+ g_assert_cmpint (g_dbus_connection_get_last_serial (c), ==, 1);
+
+ /* Send a bogus message */
+ send_bogus_message (c, NULL);
+ g_assert_cmpint (g_dbus_connection_get_last_serial (c), ==, 2);
+
+ /* Start the threads */
+ for (i = 0; i < NUM_THREADS; i++)
+ pool[i] = g_thread_new (NULL, (GThreadFunc) serials_thread_func, c);
+
+ /* Wait until threads are finished */
+ for (i = 0; i < NUM_THREADS; i++)
+ {
+ g_thread_join (pool[i]);
+ g_thread_unref (pool[i]);
+ }
+
+ /* No calls in between on this thread, should be the last value */
+ g_assert_cmpint (g_dbus_connection_get_last_serial (c), ==, 2);
+
+ send_bogus_message (c, NULL);
+
+ /* All above calls + calls in threads */
+ g_assert_cmpint (g_dbus_connection_get_last_serial (c), ==, 3 + NUM_THREADS);
+
+ g_object_unref (c);
+
+ session_bus_down ();
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
static void
test_connection_basic (void)
{
g_test_add_func ("/gdbus/connection/send", test_connection_send);
g_test_add_func ("/gdbus/connection/signals", test_connection_signals);
g_test_add_func ("/gdbus/connection/filter", test_connection_filter);
+ g_test_add_func ("/gdbus/connection/serials", test_connection_serials);
return g_test_run();
}