Branch data Line data Source code
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 : 4889 : G_DEFINE_TYPE (MyIOStream, my_io_stream, G_TYPE_IO_STREAM)
69 : :
70 : : static void
71 : 399 : my_io_stream_finalize (GObject *object)
72 : : {
73 : 399 : MyIOStream *stream = MY_IO_STREAM (object);
74 : 399 : g_object_unref (stream->input_stream);
75 : 399 : g_object_unref (stream->output_stream);
76 : 399 : G_OBJECT_CLASS (my_io_stream_parent_class)->finalize (object);
77 : 399 : }
78 : :
79 : : static void
80 : 400 : my_io_stream_init (MyIOStream *stream)
81 : : {
82 : 400 : }
83 : :
84 : : static GInputStream *
85 : 2777 : my_io_stream_get_input_stream (GIOStream *_stream)
86 : : {
87 : 2777 : MyIOStream *stream = MY_IO_STREAM (_stream);
88 : 2777 : return stream->input_stream;
89 : : }
90 : :
91 : : static GOutputStream *
92 : 911 : my_io_stream_get_output_stream (GIOStream *_stream)
93 : : {
94 : 911 : MyIOStream *stream = MY_IO_STREAM (_stream);
95 : 911 : return stream->output_stream;
96 : : }
97 : :
98 : : static void
99 : 1 : my_io_stream_class_init (MyIOStreamClass *klass)
100 : : {
101 : : GObjectClass *gobject_class;
102 : : GIOStreamClass *giostream_class;
103 : :
104 : 1 : gobject_class = G_OBJECT_CLASS (klass);
105 : 1 : gobject_class->finalize = my_io_stream_finalize;
106 : :
107 : 1 : giostream_class = G_IO_STREAM_CLASS (klass);
108 : 1 : giostream_class->get_input_stream = my_io_stream_get_input_stream;
109 : 1 : giostream_class->get_output_stream = my_io_stream_get_output_stream;
110 : 1 : }
111 : :
112 : : static GIOStream *
113 : 400 : my_io_stream_new (GInputStream *input_stream,
114 : : GOutputStream *output_stream)
115 : : {
116 : : MyIOStream *stream;
117 : 400 : g_return_val_if_fail (G_IS_INPUT_STREAM (input_stream), NULL);
118 : 400 : g_return_val_if_fail (G_IS_OUTPUT_STREAM (output_stream), NULL);
119 : 400 : stream = MY_IO_STREAM (g_object_new (MY_TYPE_IO_STREAM, NULL));
120 : 400 : stream->input_stream = g_object_ref (input_stream);
121 : 400 : stream->output_stream = g_object_ref (output_stream);
122 : 400 : 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 : 402 : G_DEFINE_TYPE (MySlowCloseOutputStream, my_slow_close_output_stream,
148 : : G_TYPE_FILTER_OUTPUT_STREAM)
149 : :
150 : : static void
151 : 400 : my_slow_close_output_stream_init (MySlowCloseOutputStream *stream)
152 : : {
153 : 400 : }
154 : :
155 : : static gboolean
156 : 399 : my_slow_close_output_stream_close (GOutputStream *stream,
157 : : GCancellable *cancellable,
158 : : GError **error)
159 : : {
160 : 399 : g_usleep (CLOSE_TIME_MS * 1000);
161 : 399 : 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 : 399 : delayed_close_free (gpointer data)
175 : : {
176 : 399 : DelayedClose *df = data;
177 : :
178 : 399 : g_object_unref (df->stream);
179 : 399 : if (df->cancellable)
180 : 0 : g_object_unref (df->cancellable);
181 : 399 : g_free (df);
182 : 399 : }
183 : :
184 : : static gboolean
185 : 399 : delayed_close_cb (gpointer data)
186 : : {
187 : 399 : DelayedClose *df = data;
188 : :
189 : 399 : 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 : 399 : return G_SOURCE_REMOVE;
194 : : }
195 : :
196 : : static void
197 : 399 : 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 : 399 : df = g_new0 (DelayedClose, 1);
207 : 399 : df->stream = g_object_ref (stream);
208 : 399 : df->io_priority = io_priority;
209 : 399 : df->cancellable = (cancellable != NULL ? g_object_ref (cancellable) : NULL);
210 : 399 : df->callback = callback;
211 : 399 : df->user_data = user_data;
212 : :
213 : 399 : later = g_timeout_source_new (CLOSE_TIME_MS);
214 : 399 : g_source_set_callback (later, delayed_close_cb, df, delayed_close_free);
215 : 399 : g_source_attach (later, g_main_context_get_thread_default ());
216 : 399 : }
217 : :
218 : : static gboolean
219 : 399 : my_slow_close_output_stream_close_finish (GOutputStream *stream,
220 : : GAsyncResult *result,
221 : : GError **error)
222 : : {
223 : 399 : return G_OUTPUT_STREAM_CLASS (my_slow_close_output_stream_parent_class)->
224 : : close_finish (stream, result, error);
225 : : }
226 : :
227 : : static void
228 : 1 : my_slow_close_output_stream_class_init (MySlowCloseOutputStreamClass *klass)
229 : : {
230 : : GOutputStreamClass *ostream_class;
231 : :
232 : 1 : ostream_class = G_OUTPUT_STREAM_CLASS (klass);
233 : 1 : ostream_class->close_fn = my_slow_close_output_stream_close;
234 : 1 : ostream_class->close_async = my_slow_close_output_stream_close_async;
235 : 1 : ostream_class->close_finish = my_slow_close_output_stream_close_finish;
236 : 1 : }
237 : :
238 : : static GIOStream *
239 : 400 : 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 : 400 : input_stream = g_unix_input_stream_new (fd_in, TRUE);
247 : 400 : real_output_stream = g_unix_output_stream_new (fd_out, TRUE);
248 : 400 : output_stream = g_object_new (MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM,
249 : : "base-stream", real_output_stream,
250 : : NULL);
251 : 400 : stream = my_io_stream_new (input_stream, output_stream);
252 : 400 : g_object_unref (input_stream);
253 : 400 : g_object_unref (output_stream);
254 : 400 : g_object_unref (real_output_stream);
255 : 400 : 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 : 2 : setup (Fixture *f,
273 : : gconstpointer context)
274 : : {
275 : 2 : f->guid = g_dbus_generate_guid ();
276 : 2 : }
277 : :
278 : : static void
279 : 2 : teardown (Fixture *f,
280 : : gconstpointer context)
281 : : {
282 : 2 : g_clear_object (&f->server_iostream);
283 : 2 : g_clear_object (&f->server_conn);
284 : 2 : g_clear_object (&f->iostream);
285 : 2 : g_clear_object (&f->connection);
286 : 2 : g_clear_error (&f->error);
287 : 2 : g_free (f->guid);
288 : 2 : }
289 : :
290 : : static void
291 : 400 : on_new_conn (GObject *source,
292 : : GAsyncResult *res,
293 : : gpointer user_data)
294 : : {
295 : 400 : GDBusConnection **connection = user_data;
296 : 400 : GError *error = NULL;
297 : :
298 : 400 : *connection = g_dbus_connection_new_for_address_finish (res, &error);
299 : 400 : g_assert_no_error (error);
300 : 400 : }
301 : :
302 : : static void
303 : 200 : test_once (Fixture *f,
304 : : gconstpointer context)
305 : : {
306 : : GDBusMessage *message;
307 : : gboolean pipe_res;
308 : :
309 : 200 : pipe_res = g_unix_open_pipe (f->server_to_client, O_CLOEXEC, &f->error);
310 : 200 : g_assert (pipe_res);
311 : 200 : pipe_res = g_unix_open_pipe (f->client_to_server, O_CLOEXEC, &f->error);
312 : 200 : g_assert (pipe_res);
313 : :
314 : 200 : f->server_iostream = my_io_stream_new_for_fds (f->client_to_server[0],
315 : : f->server_to_client[1]);
316 : 200 : f->iostream = my_io_stream_new_for_fds (f->server_to_client[0],
317 : : f->client_to_server[1]);
318 : :
319 : 200 : g_dbus_connection_new (f->server_iostream,
320 : 200 : 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 : 200 : on_new_conn, &f->server_conn);
326 : :
327 : 200 : g_dbus_connection_new (f->iostream,
328 : : NULL,
329 : : G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
330 : : NULL /* auth observer */,
331 : : NULL /* cancellable */,
332 : 200 : on_new_conn, &f->connection);
333 : :
334 : 653 : while (f->server_conn == NULL || f->connection == NULL)
335 : 453 : 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 : 200 : message = g_dbus_message_new_signal ("/", "com.example.Foo", "Bar");
342 : 200 : g_dbus_connection_send_message (f->connection, message, 0, NULL, &f->error);
343 : 200 : g_assert_no_error (f->error);
344 : 200 : g_object_unref (message);
345 : :
346 : : /* close the connection (deliberately or via last-unref) */
347 : 200 : if (g_strcmp0 (context, "unref") == 0)
348 : : {
349 : 100 : g_clear_object (&f->connection);
350 : : }
351 : : else
352 : : {
353 : 100 : g_dbus_connection_close_sync (f->connection, NULL, &f->error);
354 : 100 : g_assert_no_error (f->error);
355 : : }
356 : :
357 : : /* either way, wait for the connection to close */
358 : 302 : while (!g_dbus_connection_is_closed (f->server_conn))
359 : 102 : g_main_context_iteration (NULL, TRUE);
360 : :
361 : : /* clean up before the next run */
362 : 200 : g_clear_object (&f->iostream);
363 : 200 : g_clear_object (&f->server_iostream);
364 : 200 : g_clear_object (&f->connection);
365 : 200 : g_clear_object (&f->server_conn);
366 : 200 : g_clear_error (&f->error);
367 : 200 : }
368 : :
369 : : static void
370 : 2 : test_many_times (Fixture *f,
371 : : gconstpointer context)
372 : : {
373 : : guint i, n_repeats;
374 : :
375 : 2 : if (g_test_slow ())
376 : 0 : n_repeats = N_REPEATS_SLOW;
377 : : else
378 : 2 : n_repeats = N_REPEATS;
379 : :
380 : 202 : for (i = 0; i < n_repeats; i++)
381 : 200 : test_once (f, context);
382 : 2 : }
383 : :
384 : : int
385 : 1 : main (int argc,
386 : : char *argv[])
387 : : {
388 : 1 : g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
389 : :
390 : 1 : g_test_add ("/gdbus/close-pending", Fixture, "close",
391 : : setup, test_many_times, teardown);
392 : 1 : g_test_add ("/gdbus/unref-pending", Fixture, "unref",
393 : : setup, test_many_times, teardown);
394 : :
395 : 1 : return g_test_run();
396 : : }
|