287e7784fe25ddf3a607fe9b5a21158bb4fff70d
[platform/upstream/glib.git] / 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  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19  * Boston, MA 02111-1307, USA.
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   g_object_unref (df->cancellable);
180   g_free (df);
181 }
182
183 static gboolean
184 delayed_close_cb (gpointer data)
185 {
186   DelayedClose *df = data;
187
188   G_OUTPUT_STREAM_CLASS (my_slow_close_output_stream_parent_class)->
189     close_async (df->stream, df->io_priority, df->cancellable, df->callback,
190                  df->user_data);
191
192   return FALSE;
193 }
194
195 static void
196 my_slow_close_output_stream_close_async  (GOutputStream            *stream,
197                                           int                       io_priority,
198                                           GCancellable             *cancellable,
199                                           GAsyncReadyCallback       callback,
200                                           gpointer                  user_data)
201 {
202   GSource *later;
203   DelayedClose *df;
204
205   df = g_new0 (DelayedClose, 1);
206   df->stream = g_object_ref (stream);
207   df->io_priority = io_priority;
208   df->cancellable = (cancellable != NULL ? g_object_ref (cancellable) : NULL);
209   df->callback = callback;
210   df->user_data = user_data;
211
212   later = g_timeout_source_new (CLOSE_TIME_MS);
213   g_source_set_callback (later, delayed_close_cb, df, delayed_close_free);
214   g_source_attach (later, g_main_context_get_thread_default ());
215 }
216
217 static gboolean
218 my_slow_close_output_stream_close_finish  (GOutputStream  *stream,
219                                 GAsyncResult   *result,
220                                 GError        **error)
221 {
222   return G_OUTPUT_STREAM_CLASS (my_slow_close_output_stream_parent_class)->
223     close_finish (stream, result, error);
224 }
225
226 static void
227 my_slow_close_output_stream_class_init (MySlowCloseOutputStreamClass *klass)
228 {
229   GOutputStreamClass *ostream_class;
230
231   ostream_class = G_OUTPUT_STREAM_CLASS (klass);
232   ostream_class->close_fn = my_slow_close_output_stream_close;
233   ostream_class->close_async = my_slow_close_output_stream_close_async;
234   ostream_class->close_finish = my_slow_close_output_stream_close_finish;
235 }
236
237 static GIOStream *
238 my_io_stream_new_for_fds (gint fd_in, gint fd_out)
239 {
240   GIOStream *stream;
241   GInputStream *input_stream;
242   GOutputStream *real_output_stream;
243   GOutputStream *output_stream;
244
245   input_stream = g_unix_input_stream_new (fd_in, TRUE);
246   real_output_stream = g_unix_output_stream_new (fd_out, TRUE);
247   output_stream = g_object_new (MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM,
248                                 "base-stream", real_output_stream,
249                                 NULL);
250   stream = my_io_stream_new (input_stream, output_stream);
251   g_object_unref (input_stream);
252   g_object_unref (output_stream);
253   g_object_unref (real_output_stream);
254   return stream;
255 }
256
257 /* ---------- Tests ------------------------------------------------------ */
258
259 typedef struct {
260   gint server_to_client[2];
261   gint client_to_server[2];
262   GIOStream *server_iostream;
263   GDBusConnection *server_conn;
264   GIOStream *iostream;
265   GDBusConnection *connection;
266   gchar *guid;
267   GError *error;
268 } Fixture;
269
270 static void
271 setup (Fixture       *f,
272        gconstpointer  context)
273 {
274   f->guid = g_dbus_generate_guid ();
275 }
276
277 static void
278 teardown (Fixture       *f,
279           gconstpointer  context)
280 {
281   g_clear_object (&f->server_iostream);
282   g_clear_object (&f->server_conn);
283   g_clear_object (&f->iostream);
284   g_clear_object (&f->connection);
285   g_clear_error (&f->error);
286   g_free (f->guid);
287 }
288
289 static void
290 on_new_conn (GObject      *source,
291              GAsyncResult *res,
292              gpointer      user_data)
293 {
294   GDBusConnection **connection = user_data;
295   GError *error = NULL;
296
297   *connection = g_dbus_connection_new_for_address_finish (res, &error);
298   g_assert_no_error (error);
299 }
300
301 static void
302 test_once (Fixture       *f,
303            gconstpointer  context)
304 {
305   GDBusMessage *message;
306   gboolean pipe_res;
307
308   pipe_res = g_unix_open_pipe (f->server_to_client, FD_CLOEXEC, &f->error);
309   g_assert (pipe_res);
310   pipe_res = g_unix_open_pipe (f->client_to_server, FD_CLOEXEC, &f->error);
311   g_assert (pipe_res);
312
313   f->server_iostream = my_io_stream_new_for_fds (f->client_to_server[0],
314                                                  f->server_to_client[1]);
315   f->iostream = my_io_stream_new_for_fds (f->server_to_client[0],
316                                           f->client_to_server[1]);
317
318   g_dbus_connection_new (f->server_iostream,
319                          f->guid,
320                          (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER |
321                           G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS),
322                          NULL /* auth observer */,
323                          NULL /* cancellable */,
324                          on_new_conn, &f->server_conn);
325
326   g_dbus_connection_new (f->iostream,
327                          NULL,
328                          G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
329                          NULL /* auth observer */,
330                          NULL /* cancellable */,
331                          on_new_conn, &f->connection);
332
333   while (f->server_conn == NULL || f->connection == NULL)
334     g_main_context_iteration (NULL, TRUE);
335
336   /*
337    * queue a message - it'll sometimes be sent while the close is pending,
338    * triggering the bug
339    */
340   message = g_dbus_message_new_signal ("/", "com.example.Foo", "Bar");
341   g_dbus_connection_send_message (f->connection, message, 0, NULL, &f->error);
342   g_assert_no_error (f->error);
343   g_object_unref (message);
344
345   /* close the connection (deliberately or via last-unref) */
346   if (g_strcmp0 (context, "unref") == 0)
347     {
348       g_clear_object (&f->connection);
349     }
350   else
351     {
352       g_dbus_connection_close_sync (f->connection, NULL, &f->error);
353       g_assert_no_error (f->error);
354     }
355
356   /* either way, wait for the connection to close */
357   while (!g_dbus_connection_is_closed (f->server_conn))
358     g_main_context_iteration (NULL, TRUE);
359
360   /* clean up before the next run */
361   g_clear_object (&f->iostream);
362   g_clear_object (&f->server_iostream);
363   g_clear_object (&f->connection);
364   g_clear_object (&f->server_conn);
365   g_clear_error (&f->error);
366 }
367
368 static void
369 test_many_times (Fixture       *f,
370                  gconstpointer  context)
371 {
372   guint i, n_repeats;
373
374   if (g_test_slow ())
375     n_repeats = N_REPEATS_SLOW;
376   else
377     n_repeats = N_REPEATS;
378
379   for (i = 0; i < n_repeats; i++)
380     test_once (f, context);
381 }
382
383 int
384 main (int   argc,
385       char *argv[])
386 {
387   g_type_init ();
388   g_test_init (&argc, &argv, 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 }