(root)/
glib-2.79.0/
gio/
tests/
gdbus-proxy-threads.c
       1  /* Test case for GNOME #651133
       2   *
       3   * Copyright (C) 2008-2010 Red Hat, Inc.
       4   * Copyright (C) 2011 Nokia Corporation
       5   *
       6   * SPDX-License-Identifier: LGPL-2.1-or-later
       7   *
       8   * This library is free software; you can redistribute it and/or
       9   * modify it under the terms of the GNU Lesser General Public
      10   * License as published by the Free Software Foundation; either
      11   * version 2.1 of the License, or (at your option) any later version.
      12   *
      13   * This library is distributed in the hope that it will be useful,
      14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16   * Lesser General Public License for more details.
      17   *
      18   * You should have received a copy of the GNU Lesser General
      19   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      20   *
      21   * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
      22   */
      23  
      24  #include <config.h>
      25  
      26  #include <unistd.h>
      27  #include <string.h>
      28  
      29  #include <gio/gio.h>
      30  
      31  #include "gdbus-tests.h"
      32  
      33  #ifdef HAVE_DBUS1
      34  # include <dbus/dbus-shared.h>
      35  #else
      36  # define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
      37  # define DBUS_PATH_DBUS "/org/freedesktop/DBus"
      38  # define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
      39  # define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
      40  # define DBUS_RELEASE_NAME_REPLY_RELEASED 1
      41  #endif
      42  
      43  #define MY_NAME "com.example.Test.Myself"
      44  /* This many threads create and destroy GDBusProxy instances, in addition
      45   * to the main thread processing their NameOwnerChanged signals.
      46   * N_THREADS_MAX is used with "-m slow", N_THREADS otherwise.
      47   */
      48  #define N_THREADS_MAX 10
      49  #define N_THREADS 2
      50  /* This many GDBusProxy instances are created by each thread. */
      51  #define N_REPEATS 100
      52  /* The main thread requests/releases a name this many times as rapidly as
      53   * possible, before performing one "slow" cycle that waits for each method
      54   * call result (and therefore, due to D-Bus total ordering, all previous
      55   * method calls) to prevent requests from piling up infinitely. The more calls
      56   * are made rapidly, the better we reproduce bugs.
      57   */
      58  #define N_RAPID_CYCLES 50
      59  
      60  static GMainLoop *loop;
      61  
      62  static gpointer
      63  run_proxy_thread (gpointer data)
      64  {
      65    GDBusConnection *connection = data;
      66    int i;
      67  
      68    g_assert (g_main_context_get_thread_default () == NULL);
      69  
      70    for (i = 0; i < N_REPEATS; i++)
      71      {
      72        GDBusProxy *proxy;
      73        GError *error = NULL;
      74        GVariant *ret;
      75  
      76        if (g_test_verbose ())
      77          g_printerr (".");
      78  
      79        proxy = g_dbus_proxy_new_sync (connection,
      80                                       G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
      81                                       G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
      82                                       NULL,
      83                                       MY_NAME,
      84                                       "/com/example/TestObject",
      85                                       "com.example.Frob",
      86                                       NULL,
      87                                       &error);
      88        g_assert_no_error (error);
      89        g_assert (proxy != NULL);
      90        g_dbus_proxy_set_default_timeout (proxy, G_MAXINT);
      91  
      92        ret = g_dbus_proxy_call_sync (proxy, "StupidMethod", NULL,
      93                                      G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
      94                                      NULL, NULL);
      95        /*
      96         * we expect this to fail - if we have the name at the moment, we called
      97         * an unimplemented method, and if not, there was nothing to call
      98         */
      99        g_assert (ret == NULL);
     100  
     101        /*
     102         * this races with the NameOwnerChanged signal being emitted in an
     103         * idle
     104         */
     105        g_object_unref (proxy);
     106      }
     107  
     108    g_main_loop_quit (loop);
     109    return NULL;
     110  }
     111  
     112  static void release_name (GDBusConnection *connection, gboolean wait);
     113  
     114  static void
     115  request_name_cb (GObject *source,
     116                   GAsyncResult *res,
     117                   gpointer user_data)
     118  {
     119    GDBusConnection *connection = G_DBUS_CONNECTION (source);
     120    GError *error = NULL;
     121    GVariant *var;
     122    GVariant *child;
     123  
     124    var = g_dbus_connection_call_finish (connection, res, &error);
     125    g_assert_no_error (error);
     126    child = g_variant_get_child_value (var, 0);
     127    g_assert_cmpuint (g_variant_get_uint32 (child),
     128                      ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
     129  
     130    release_name (connection, TRUE);
     131    g_variant_unref (child);
     132    g_variant_unref (var);
     133  }
     134  
     135  static void
     136  request_name (GDBusConnection *connection,
     137                gboolean         wait)
     138  {
     139    g_dbus_connection_call (connection,
     140                            DBUS_SERVICE_DBUS,
     141                            DBUS_PATH_DBUS,
     142                            DBUS_INTERFACE_DBUS,
     143                            "RequestName",
     144                            g_variant_new ("(su)", MY_NAME, 0),
     145                            G_VARIANT_TYPE ("(u)"),
     146                            G_DBUS_CALL_FLAGS_NONE,
     147                            -1,
     148                            NULL,
     149                            wait ? request_name_cb : NULL,
     150                            NULL);
     151  }
     152  
     153  static void
     154  release_name_cb (GObject *source,
     155                   GAsyncResult *res,
     156                   gpointer user_data)
     157  {
     158    GDBusConnection *connection = G_DBUS_CONNECTION (source);
     159    GError *error = NULL;
     160    GVariant *var;
     161    GVariant *child;
     162    int i;
     163  
     164    var = g_dbus_connection_call_finish (connection, res, &error);
     165    g_assert_no_error (error);
     166    child = g_variant_get_child_value (var, 0);
     167    g_assert_cmpuint (g_variant_get_uint32 (child),
     168                      ==, DBUS_RELEASE_NAME_REPLY_RELEASED);
     169  
     170    /* generate some rapid NameOwnerChanged signals to try to trigger crashes */
     171    for (i = 0; i < N_RAPID_CYCLES; i++)
     172      {
     173        request_name (connection, FALSE);
     174        release_name (connection, FALSE);
     175      }
     176  
     177    /* wait for dbus-daemon to catch up */
     178    request_name (connection, TRUE);
     179    g_variant_unref (child);
     180    g_variant_unref (var);
     181  }
     182  
     183  static void
     184  release_name (GDBusConnection *connection,
     185                gboolean         wait)
     186  {
     187    g_dbus_connection_call (connection,
     188                            DBUS_SERVICE_DBUS,
     189                            DBUS_PATH_DBUS,
     190                            DBUS_INTERFACE_DBUS,
     191                            "ReleaseName",
     192                            g_variant_new ("(s)", MY_NAME),
     193                            G_VARIANT_TYPE ("(u)"),
     194                            G_DBUS_CALL_FLAGS_NONE,
     195                            -1,
     196                            NULL,
     197                            wait ? release_name_cb : NULL,
     198                            NULL);
     199  }
     200  
     201  static void
     202  test_proxy (void)
     203  {
     204    GDBusConnection *connection;
     205    GError *error = NULL;
     206    GThread *proxy_threads[N_THREADS_MAX];
     207    int i;
     208    int n_threads;
     209  
     210    if (g_test_slow ())
     211      n_threads = N_THREADS_MAX;
     212    else
     213      n_threads = N_THREADS;
     214  
     215    session_bus_up ();
     216  
     217    loop = g_main_loop_new (NULL, TRUE);
     218  
     219    connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
     220                                 NULL,
     221                                 &error);
     222    g_assert_no_error (error);
     223  
     224    request_name (connection, TRUE);
     225  
     226    for (i = 0; i < n_threads; i++)
     227      {
     228        proxy_threads[i] = g_thread_new ("run-proxy",
     229                                         run_proxy_thread, connection);
     230      }
     231  
     232    g_main_loop_run (loop);
     233  
     234    for (i = 0; i < n_threads; i++)
     235      {
     236        g_thread_join (proxy_threads[i]);
     237      }
     238  
     239    g_object_unref (connection);
     240    g_main_loop_unref (loop);
     241  
     242    /* TODO: should call session_bus_down() but that requires waiting
     243     * for all the outstanding method calls to complete...
     244     */
     245    if (g_test_verbose ())
     246      g_printerr ("\n");
     247  }
     248  
     249  int
     250  main (int   argc,
     251        char *argv[])
     252  {
     253    g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
     254  
     255    g_test_dbus_unset ();
     256  
     257    g_test_add_func ("/gdbus/proxy/vs-threads", test_proxy);
     258  
     259    return g_test_run();
     260  }