(root)/
glib-2.79.0/
gio/
tests/
gdbus-close-pending.c
       1  /* GDBus regression test - close a stream when a message remains to be written
       2   *
       3   * Copyright © 2006-2010 Red Hat, Inc.
       4   * Copyright © 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 <stdlib.h>
      27  #include <string.h>
      28  
      29  #include <gio/gio.h>
      30  
      31  #ifdef G_OS_UNIX
      32  # include <unistd.h>
      33  
      34  # include <glib/glib-unix.h>
      35  # include <gio/gunixinputstream.h>
      36  # include <gio/gunixoutputstream.h>
      37  # include <gio/gunixconnection.h>
      38  #else
      39  # error This test is currently Unix-specific due to use of g_unix_open_pipe()
      40  #endif
      41  
      42  #include "gdbus-tests.h"
      43  
      44  #define CLOSE_TIME_MS 1
      45  #define N_REPEATS_SLOW 5000
      46  #define N_REPEATS 100
      47  
      48  /* ---------- MyIOStream ------------------------------------------------- */
      49  
      50  #define MY_TYPE_IO_STREAM  (my_io_stream_get_type ())
      51  #define MY_IO_STREAM(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), MY_TYPE_IO_STREAM, MyIOStream))
      52  #define MY_IS_IO_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MY_TYPE_IO_STREAM))
      53  
      54  typedef struct
      55  {
      56    GIOStream parent_instance;
      57    GInputStream *input_stream;
      58    GOutputStream *output_stream;
      59  } MyIOStream;
      60  
      61  typedef struct
      62  {
      63    GIOStreamClass parent_class;
      64  } MyIOStreamClass;
      65  
      66  static GType my_io_stream_get_type (void) G_GNUC_CONST;
      67  
      68  G_DEFINE_TYPE (MyIOStream, my_io_stream, G_TYPE_IO_STREAM)
      69  
      70  static void
      71  my_io_stream_finalize (GObject *object)
      72  {
      73    MyIOStream *stream = MY_IO_STREAM (object);
      74    g_object_unref (stream->input_stream);
      75    g_object_unref (stream->output_stream);
      76    G_OBJECT_CLASS (my_io_stream_parent_class)->finalize (object);
      77  }
      78  
      79  static void
      80  my_io_stream_init (MyIOStream *stream)
      81  {
      82  }
      83  
      84  static GInputStream *
      85  my_io_stream_get_input_stream (GIOStream *_stream)
      86  {
      87    MyIOStream *stream = MY_IO_STREAM (_stream);
      88    return stream->input_stream;
      89  }
      90  
      91  static GOutputStream *
      92  my_io_stream_get_output_stream (GIOStream *_stream)
      93  {
      94    MyIOStream *stream = MY_IO_STREAM (_stream);
      95    return stream->output_stream;
      96  }
      97  
      98  static void
      99  my_io_stream_class_init (MyIOStreamClass *klass)
     100  {
     101    GObjectClass *gobject_class;
     102    GIOStreamClass *giostream_class;
     103  
     104    gobject_class = G_OBJECT_CLASS (klass);
     105    gobject_class->finalize = my_io_stream_finalize;
     106  
     107    giostream_class = G_IO_STREAM_CLASS (klass);
     108    giostream_class->get_input_stream  = my_io_stream_get_input_stream;
     109    giostream_class->get_output_stream = my_io_stream_get_output_stream;
     110  }
     111  
     112  static GIOStream *
     113  my_io_stream_new (GInputStream  *input_stream,
     114                    GOutputStream *output_stream)
     115  {
     116    MyIOStream *stream;
     117    g_return_val_if_fail (G_IS_INPUT_STREAM (input_stream), NULL);
     118    g_return_val_if_fail (G_IS_OUTPUT_STREAM (output_stream), NULL);
     119    stream = MY_IO_STREAM (g_object_new (MY_TYPE_IO_STREAM, NULL));
     120    stream->input_stream = g_object_ref (input_stream);
     121    stream->output_stream = g_object_ref (output_stream);
     122    return G_IO_STREAM (stream);
     123  }
     124  
     125  /* ---------- MySlowCloseOutputStream ------------------------------------ */
     126  
     127  typedef struct
     128  {
     129    GFilterOutputStream parent_instance;
     130  } MySlowCloseOutputStream;
     131  
     132  typedef struct
     133  {
     134    GFilterOutputStreamClass parent_class;
     135  } MySlowCloseOutputStreamClass;
     136  
     137  #define MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM \
     138    (my_slow_close_output_stream_get_type ())
     139  #define MY_OUTPUT_STREAM(o) \
     140    (G_TYPE_CHECK_INSTANCE_CAST ((o), MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM, \
     141                                 MySlowCloseOutputStream))
     142  #define MY_IS_SLOW_CLOSE_OUTPUT_STREAM(o) \
     143    (G_TYPE_CHECK_INSTANCE_TYPE ((o), MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM))
     144  
     145  static GType my_slow_close_output_stream_get_type (void) G_GNUC_CONST;
     146  
     147  G_DEFINE_TYPE (MySlowCloseOutputStream, my_slow_close_output_stream,
     148                 G_TYPE_FILTER_OUTPUT_STREAM)
     149  
     150  static void
     151  my_slow_close_output_stream_init (MySlowCloseOutputStream *stream)
     152  {
     153  }
     154  
     155  static gboolean
     156  my_slow_close_output_stream_close (GOutputStream  *stream,
     157                                     GCancellable   *cancellable,
     158                                     GError        **error)
     159  {
     160    g_usleep (CLOSE_TIME_MS * 1000);
     161    return G_OUTPUT_STREAM_CLASS (my_slow_close_output_stream_parent_class)->
     162      close_fn (stream, cancellable, error);
     163  }
     164  
     165  typedef struct {
     166      GOutputStream *stream;
     167      gint io_priority;
     168      GCancellable *cancellable;
     169      GAsyncReadyCallback callback;
     170      gpointer user_data;
     171  } DelayedClose;
     172  
     173  static void
     174  delayed_close_free (gpointer data)
     175  {
     176    DelayedClose *df = data;
     177  
     178    g_object_unref (df->stream);
     179    if (df->cancellable)
     180      g_object_unref (df->cancellable);
     181    g_free (df);
     182  }
     183  
     184  static gboolean
     185  delayed_close_cb (gpointer data)
     186  {
     187    DelayedClose *df = data;
     188  
     189    G_OUTPUT_STREAM_CLASS (my_slow_close_output_stream_parent_class)->
     190      close_async (df->stream, df->io_priority, df->cancellable, df->callback,
     191                   df->user_data);
     192  
     193    return G_SOURCE_REMOVE;
     194  }
     195  
     196  static void
     197  my_slow_close_output_stream_close_async  (GOutputStream            *stream,
     198                                            int                       io_priority,
     199                                            GCancellable             *cancellable,
     200                                            GAsyncReadyCallback       callback,
     201                                            gpointer                  user_data)
     202  {
     203    GSource *later;
     204    DelayedClose *df;
     205  
     206    df = g_new0 (DelayedClose, 1);
     207    df->stream = g_object_ref (stream);
     208    df->io_priority = io_priority;
     209    df->cancellable = (cancellable != NULL ? g_object_ref (cancellable) : NULL);
     210    df->callback = callback;
     211    df->user_data = user_data;
     212  
     213    later = g_timeout_source_new (CLOSE_TIME_MS);
     214    g_source_set_callback (later, delayed_close_cb, df, delayed_close_free);
     215    g_source_attach (later, g_main_context_get_thread_default ());
     216  }
     217  
     218  static gboolean
     219  my_slow_close_output_stream_close_finish  (GOutputStream  *stream,
     220                                  GAsyncResult   *result,
     221                                  GError        **error)
     222  {
     223    return G_OUTPUT_STREAM_CLASS (my_slow_close_output_stream_parent_class)->
     224      close_finish (stream, result, error);
     225  }
     226  
     227  static void
     228  my_slow_close_output_stream_class_init (MySlowCloseOutputStreamClass *klass)
     229  {
     230    GOutputStreamClass *ostream_class;
     231  
     232    ostream_class = G_OUTPUT_STREAM_CLASS (klass);
     233    ostream_class->close_fn = my_slow_close_output_stream_close;
     234    ostream_class->close_async = my_slow_close_output_stream_close_async;
     235    ostream_class->close_finish = my_slow_close_output_stream_close_finish;
     236  }
     237  
     238  static GIOStream *
     239  my_io_stream_new_for_fds (gint fd_in, gint fd_out)
     240  {
     241    GIOStream *stream;
     242    GInputStream *input_stream;
     243    GOutputStream *real_output_stream;
     244    GOutputStream *output_stream;
     245  
     246    input_stream = g_unix_input_stream_new (fd_in, TRUE);
     247    real_output_stream = g_unix_output_stream_new (fd_out, TRUE);
     248    output_stream = g_object_new (MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM,
     249                                  "base-stream", real_output_stream,
     250                                  NULL);
     251    stream = my_io_stream_new (input_stream, output_stream);
     252    g_object_unref (input_stream);
     253    g_object_unref (output_stream);
     254    g_object_unref (real_output_stream);
     255    return stream;
     256  }
     257  
     258  /* ---------- Tests ------------------------------------------------------ */
     259  
     260  typedef struct {
     261    gint server_to_client[2];
     262    gint client_to_server[2];
     263    GIOStream *server_iostream;
     264    GDBusConnection *server_conn;
     265    GIOStream *iostream;
     266    GDBusConnection *connection;
     267    gchar *guid;
     268    GError *error;
     269  } Fixture;
     270  
     271  static void
     272  setup (Fixture       *f,
     273         gconstpointer  context)
     274  {
     275    f->guid = g_dbus_generate_guid ();
     276  }
     277  
     278  static void
     279  teardown (Fixture       *f,
     280            gconstpointer  context)
     281  {
     282    g_clear_object (&f->server_iostream);
     283    g_clear_object (&f->server_conn);
     284    g_clear_object (&f->iostream);
     285    g_clear_object (&f->connection);
     286    g_clear_error (&f->error);
     287    g_free (f->guid);
     288  }
     289  
     290  static void
     291  on_new_conn (GObject      *source,
     292               GAsyncResult *res,
     293               gpointer      user_data)
     294  {
     295    GDBusConnection **connection = user_data;
     296    GError *error = NULL;
     297  
     298    *connection = g_dbus_connection_new_for_address_finish (res, &error);
     299    g_assert_no_error (error);
     300  }
     301  
     302  static void
     303  test_once (Fixture       *f,
     304             gconstpointer  context)
     305  {
     306    GDBusMessage *message;
     307    gboolean pipe_res;
     308  
     309    pipe_res = g_unix_open_pipe (f->server_to_client, O_CLOEXEC, &f->error);
     310    g_assert (pipe_res);
     311    pipe_res = g_unix_open_pipe (f->client_to_server, O_CLOEXEC, &f->error);
     312    g_assert (pipe_res);
     313  
     314    f->server_iostream = my_io_stream_new_for_fds (f->client_to_server[0],
     315                                                   f->server_to_client[1]);
     316    f->iostream = my_io_stream_new_for_fds (f->server_to_client[0],
     317                                            f->client_to_server[1]);
     318  
     319    g_dbus_connection_new (f->server_iostream,
     320                           f->guid,
     321                           (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER |
     322                            G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS),
     323                           NULL /* auth observer */,
     324                           NULL /* cancellable */,
     325                           on_new_conn, &f->server_conn);
     326  
     327    g_dbus_connection_new (f->iostream,
     328                           NULL,
     329                           G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
     330                           NULL /* auth observer */,
     331                           NULL /* cancellable */,
     332                           on_new_conn, &f->connection);
     333  
     334    while (f->server_conn == NULL || f->connection == NULL)
     335      g_main_context_iteration (NULL, TRUE);
     336  
     337    /*
     338     * queue a message - it'll sometimes be sent while the close is pending,
     339     * triggering the bug
     340     */
     341    message = g_dbus_message_new_signal ("/", "com.example.Foo", "Bar");
     342    g_dbus_connection_send_message (f->connection, message, 0, NULL, &f->error);
     343    g_assert_no_error (f->error);
     344    g_object_unref (message);
     345  
     346    /* close the connection (deliberately or via last-unref) */
     347    if (g_strcmp0 (context, "unref") == 0)
     348      {
     349        g_clear_object (&f->connection);
     350      }
     351    else
     352      {
     353        g_dbus_connection_close_sync (f->connection, NULL, &f->error);
     354        g_assert_no_error (f->error);
     355      }
     356  
     357    /* either way, wait for the connection to close */
     358    while (!g_dbus_connection_is_closed (f->server_conn))
     359      g_main_context_iteration (NULL, TRUE);
     360  
     361    /* clean up before the next run */
     362    g_clear_object (&f->iostream);
     363    g_clear_object (&f->server_iostream);
     364    g_clear_object (&f->connection);
     365    g_clear_object (&f->server_conn);
     366    g_clear_error (&f->error);
     367  }
     368  
     369  static void
     370  test_many_times (Fixture       *f,
     371                   gconstpointer  context)
     372  {
     373    guint i, n_repeats;
     374  
     375    if (g_test_slow ())
     376      n_repeats = N_REPEATS_SLOW;
     377    else
     378      n_repeats = N_REPEATS;
     379  
     380    for (i = 0; i < n_repeats; i++)
     381      test_once (f, context);
     382  }
     383  
     384  int
     385  main (int   argc,
     386        char *argv[])
     387  {
     388    g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
     389  
     390    g_test_add ("/gdbus/close-pending", Fixture, "close",
     391        setup, test_many_times, teardown);
     392    g_test_add ("/gdbus/unref-pending", Fixture, "unref",
     393        setup, test_many_times, teardown);
     394  
     395    return g_test_run();
     396  }