1 /* GLib testing framework examples and tests
2 * Copyright (C) 2008 Red Hat, Inc
4 * This work is provided "as is"; redistribution and modification
5 * in whole or in part, in any medium, physical or electronic is
6 * permitted without restriction.
8 * This work is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * In no event shall the authors or contributors be liable for any
13 * direct, indirect, incidental, special, exemplary, or consequential
14 * damages (including, but not limited to, procurement of substitute
15 * goods or services; loss of use, data, or profits; or business
16 * interruption) however caused and on any theory of liability, whether
17 * in contract, strict liability, or tort (including negligence or
18 * otherwise) arising in any way out of the use of this software, even
19 * if advised of the possibility of such damage.
22 #include <glib/glib.h>
24 #include <gio/gwin32inputstream.h>
25 #include <gio/gwin32outputstream.h>
33 #define DATA "abcdefghijklmnopqrstuvwxyz"
35 int writer_pipe
[2], reader_pipe
[2];
36 GCancellable
*writer_cancel
, *reader_cancel
, *main_cancel
;
40 writer_thread (gpointer user_data
)
43 gssize nwrote
, offset
;
47 g_assert (DuplicateHandle (GetCurrentProcess (),
48 (HANDLE
) (gintptr
) _get_osfhandle (writer_pipe
[1]),
52 DUPLICATE_SAME_ACCESS
));
53 close (writer_pipe
[1]);
55 out
= g_win32_output_stream_new (out_handle
, TRUE
);
61 while (offset
< (gssize
) sizeof (DATA
))
63 nwrote
= g_output_stream_write (out
, DATA
+ offset
,
64 sizeof (DATA
) - offset
,
66 if (nwrote
<= 0 || err
!= NULL
)
71 g_assert (nwrote
> 0 || err
!= NULL
);
75 if (g_cancellable_is_cancelled (writer_cancel
))
77 g_cancellable_cancel (main_cancel
);
82 g_warning ("writer: %s", err
->message
);
83 g_assert_not_reached ();
87 reader_thread (gpointer user_data
)
90 gssize nread
= 0, total
;
92 char buf
[sizeof (DATA
)];
95 g_assert (DuplicateHandle (GetCurrentProcess (),
96 (HANDLE
) (gintptr
) _get_osfhandle (reader_pipe
[0]),
100 DUPLICATE_SAME_ACCESS
));
101 close (reader_pipe
[0]);
103 in
= g_win32_input_stream_new (in_handle
, TRUE
);
108 while (total
< (gssize
) sizeof (DATA
))
110 nread
= g_input_stream_read (in
, buf
+ total
, sizeof (buf
) - total
,
111 reader_cancel
, &err
);
112 if (nread
<= 0 || err
!= NULL
)
122 g_assert (err
== NULL
);
128 g_assert_cmpstr (buf
, ==, DATA
);
129 g_assert (!g_cancellable_is_cancelled (reader_cancel
));
133 g_warning ("reader: %s", err
->message
);
134 g_assert_not_reached ();
137 char main_buf
[sizeof (DATA
)];
138 gssize main_len
, main_offset
;
140 static void readable (GObject
*source
, GAsyncResult
*res
, gpointer user_data
);
141 static void writable (GObject
*source
, GAsyncResult
*res
, gpointer user_data
);
144 do_main_cancel (GOutputStream
*out
)
146 g_output_stream_close (out
, NULL
, NULL
);
147 g_main_loop_quit (loop
);
151 readable (GObject
*source
, GAsyncResult
*res
, gpointer user_data
)
153 GInputStream
*in
= G_INPUT_STREAM (source
);
154 GOutputStream
*out
= user_data
;
157 main_len
= g_input_stream_read_finish (in
, res
, &err
);
159 if (g_cancellable_is_cancelled (main_cancel
))
161 do_main_cancel (out
);
165 g_assert (err
== NULL
);
168 g_output_stream_write_async (out
, main_buf
, main_len
,
169 G_PRIORITY_DEFAULT
, main_cancel
,
174 writable (GObject
*source
, GAsyncResult
*res
, gpointer user_data
)
176 GOutputStream
*out
= G_OUTPUT_STREAM (source
);
177 GInputStream
*in
= user_data
;
181 nwrote
= g_output_stream_write_finish (out
, res
, &err
);
183 if (g_cancellable_is_cancelled (main_cancel
))
185 do_main_cancel (out
);
189 g_assert (err
== NULL
);
190 g_assert_cmpint (nwrote
, <=, main_len
- main_offset
);
192 main_offset
+= nwrote
;
193 if (main_offset
== main_len
)
195 g_input_stream_read_async (in
, main_buf
, sizeof (main_buf
),
196 G_PRIORITY_DEFAULT
, main_cancel
,
201 g_output_stream_write_async (out
, main_buf
+ main_offset
,
202 main_len
- main_offset
,
203 G_PRIORITY_DEFAULT
, main_cancel
,
209 timeout (gpointer cancellable
)
211 g_cancellable_cancel (cancellable
);
218 GThread
*writer
, *reader
;
221 HANDLE in_handle
, out_handle
;
223 /* Split off two (additional) threads, a reader and a writer. From
224 * the writer thread, write data synchronously in small chunks,
225 * which gets read asynchronously by the main thread and then
226 * written asynchronously to the reader thread, which reads it
227 * synchronously. Eventually a timeout in the main thread will cause
228 * it to cancel the writer thread, which will in turn cancel the
229 * read op in the main thread, which will then close the pipe to
230 * the reader thread, causing the read op to fail.
233 g_assert (_pipe (writer_pipe
, 10, _O_BINARY
) == 0 && _pipe (reader_pipe
, 10, _O_BINARY
) == 0);
235 writer_cancel
= g_cancellable_new ();
236 reader_cancel
= g_cancellable_new ();
237 main_cancel
= g_cancellable_new ();
239 writer
= g_thread_new ("writer", writer_thread
, NULL
);
240 reader
= g_thread_new ("reader", reader_thread
, NULL
);
242 g_assert (DuplicateHandle (GetCurrentProcess (),
243 (HANDLE
) (gintptr
) _get_osfhandle (writer_pipe
[0]),
244 GetCurrentProcess (),
247 DUPLICATE_SAME_ACCESS
));
248 close (writer_pipe
[0]);
250 g_assert (DuplicateHandle (GetCurrentProcess (),
251 (HANDLE
) (gintptr
) _get_osfhandle (reader_pipe
[1]),
252 GetCurrentProcess (),
255 DUPLICATE_SAME_ACCESS
));
256 close (reader_pipe
[1]);
258 in
= g_win32_input_stream_new (in_handle
, TRUE
);
259 out
= g_win32_output_stream_new (out_handle
, TRUE
);
261 g_input_stream_read_async (in
, main_buf
, sizeof (main_buf
),
262 G_PRIORITY_DEFAULT
, main_cancel
,
265 g_timeout_add (500, timeout
, writer_cancel
);
267 loop
= g_main_loop_new (NULL
, TRUE
);
268 g_main_loop_run (loop
);
269 g_main_loop_unref (loop
);
271 g_thread_join (reader
);
272 g_thread_join (writer
);
274 g_object_unref (main_cancel
);
275 g_object_unref (reader_cancel
);
276 g_object_unref (writer_cancel
);
278 g_object_unref (out
);
281 typedef struct _PipeIOOverlapReader
283 char buf
[sizeof (DATA
)];
286 GCancellable
*cancellable
;
288 } PipeIOOverlapReader
;
290 #define TEST_PIPE_IO_OVERLAP (1024 * 4)
293 pipe_io_overlap_reader_thread (gpointer user_data
)
295 PipeIOOverlapReader
*p
= user_data
;
300 for (i
= 0; i
< TEST_PIPE_IO_OVERLAP
; ++i
) {
301 memset (p
->buf
, 0, sizeof (p
->buf
));
302 g_input_stream_read_all (p
->in
, p
->buf
, sizeof (p
->buf
),
305 g_assert_cmpuint (read
, ==, sizeof (p
->buf
));
306 g_assert_no_error (err
);
307 g_assert_cmpstr (p
->buf
, ==, DATA
);
314 pipe_io_overlap_writer_thread (gpointer user_data
)
316 GOutputStream
*out
= user_data
;
321 for (i
= 0; i
< TEST_PIPE_IO_OVERLAP
; ++i
) {
322 g_output_stream_write_all (out
, DATA
, sizeof (DATA
),
323 &bytes_written
, NULL
, &err
);
325 g_assert_cmpuint (bytes_written
, ==, sizeof (DATA
));
326 g_assert_no_error (err
);
333 test_pipe_io_overlap (void)
335 GOutputStream
*out_server
, *out_client
;
336 GThread
*writer_server
, *writer_client
;
337 PipeIOOverlapReader rs
, rc
;
338 HANDLE server
, client
;
341 g_snprintf (name
, sizeof (name
),
342 "\\\\.\\pipe\\gtest-io-overlap-%u", (guint
) GetCurrentProcessId ());
344 server
= CreateNamedPipe (name
,
345 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
346 PIPE_READMODE_BYTE
| PIPE_WAIT
,
348 g_assert (server
!= INVALID_HANDLE_VALUE
);
350 client
= CreateFile (name
, GENERIC_WRITE
| GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
351 g_assert (client
!= INVALID_HANDLE_VALUE
);
353 out_server
= g_win32_output_stream_new (server
, TRUE
);
354 writer_server
= g_thread_new ("writer_server", pipe_io_overlap_writer_thread
, out_server
);
355 rs
.in
= g_win32_input_stream_new (server
, TRUE
);
356 rs
.thread
= g_thread_new ("reader_server", pipe_io_overlap_reader_thread
, &rs
);
358 out_client
= g_win32_output_stream_new (client
, TRUE
);
359 writer_client
= g_thread_new ("writer_client", pipe_io_overlap_writer_thread
, out_client
);
360 rc
.in
= g_win32_input_stream_new (client
, TRUE
);
361 rc
.thread
= g_thread_new ("reader_client", pipe_io_overlap_reader_thread
, &rc
);
363 g_thread_join (writer_client
);
364 g_thread_join (writer_server
);
365 g_thread_join (rc
.thread
);
366 g_thread_join (rs
.thread
);
368 g_object_unref (rs
.in
);
369 g_object_unref (rc
.in
);
370 g_object_unref (out_server
);
371 g_object_unref (out_client
);
375 pipe_io_concurrent_writer_thread (gpointer user_data
)
377 GOutputStream
*out
= user_data
;
381 g_output_stream_write_all (out
, DATA
, 1, &bytes_written
, NULL
, &err
);
383 g_assert_cmpuint (bytes_written
, ==, 1);
384 g_assert_no_error (err
);
390 pipe_io_concurrent_reader_thread (gpointer user_data
)
392 PipeIOOverlapReader
*p
= user_data
;
396 memset (p
->buf
, 0, sizeof (p
->buf
));
397 p
->success
= g_input_stream_read_all (p
->in
, p
->buf
, 1, &read
, p
->cancellable
, &err
);
399 /* only one thread will succeed, the other will be cancelled */
402 /* continue the main thread */
403 write (writer_pipe
[1], "", 1);
404 g_assert_cmpuint (read
, ==, 1);
405 g_assert_no_error (err
);
412 test_pipe_io_concurrent (void)
414 GOutputStream
*out_server
;
415 GThread
*writer_server
;
416 PipeIOOverlapReader rc1
, rc2
;
417 HANDLE server
, client
;
420 g_snprintf (name
, sizeof (name
),
421 "\\\\.\\pipe\\gtest-io-concurrent-%u", (guint
) GetCurrentProcessId ());
423 server
= CreateNamedPipe (name
,
424 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
425 PIPE_READMODE_BYTE
| PIPE_WAIT
,
427 g_assert (server
!= INVALID_HANDLE_VALUE
);
428 g_assert (_pipe (writer_pipe
, 10, _O_BINARY
) == 0);
430 client
= CreateFile (name
, GENERIC_WRITE
| GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_FLAG_OVERLAPPED
, NULL
);
431 g_assert (client
!= INVALID_HANDLE_VALUE
);
433 rc1
.in
= g_win32_input_stream_new (client
, TRUE
);
435 rc1
.cancellable
= g_cancellable_new ();
436 rc1
.thread
= g_thread_new ("reader_client", pipe_io_concurrent_reader_thread
, &rc1
);
438 rc2
.in
= g_win32_input_stream_new (client
, TRUE
);
440 rc2
.cancellable
= g_cancellable_new ();
441 rc2
.thread
= g_thread_new ("reader_client", pipe_io_concurrent_reader_thread
, &rc2
);
443 /* FIXME: how to synchronize on both reader thread waiting in read,
444 before starting the writer thread? */
445 g_usleep (G_USEC_PER_SEC
/ 10);
447 out_server
= g_win32_output_stream_new (server
, TRUE
);
448 writer_server
= g_thread_new ("writer_server", pipe_io_concurrent_writer_thread
, out_server
);
450 read (writer_pipe
[0], &c
, 1);
452 g_assert (rc1
.success
^ rc2
.success
);
454 g_cancellable_cancel (rc1
.cancellable
);
455 g_cancellable_cancel (rc2
.cancellable
);
457 g_thread_join (writer_server
);
458 g_thread_join (rc1
.thread
);
459 g_thread_join (rc2
.thread
);
461 g_object_unref (rc1
.in
);
462 g_object_unref (rc2
.in
);
463 g_object_unref (out_server
);
465 close (writer_pipe
[0]);
466 close (writer_pipe
[1]);
470 readable_cancel (GObject
*source
, GAsyncResult
*res
, gpointer user_data
)
472 GInputStream
*in
= G_INPUT_STREAM (source
);
476 len
= g_input_stream_read_finish (in
, res
, &err
);
477 g_assert_cmpint (len
, ==, -1);
478 g_assert_error (err
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
);
481 g_main_loop_quit (loop
);
485 test_pipe_io_cancel (void)
489 HANDLE in_handle
, out_handle
;
492 g_snprintf (name
, sizeof (name
),
493 "\\\\.\\pipe\\gtest-io-cancel-%u", (guint
) GetCurrentProcessId ());
495 in_handle
= CreateNamedPipe (name
,
496 PIPE_ACCESS_INBOUND
| FILE_FLAG_OVERLAPPED
,
497 PIPE_READMODE_BYTE
| PIPE_WAIT
,
499 g_assert (in_handle
!= INVALID_HANDLE_VALUE
);
501 out_handle
= CreateFile (name
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
502 g_assert (out_handle
!= INVALID_HANDLE_VALUE
);
504 in
= g_win32_input_stream_new (in_handle
, TRUE
);
505 out
= g_win32_output_stream_new (out_handle
, TRUE
);
507 reader_cancel
= g_cancellable_new ();
508 g_input_stream_read_async (in
, main_buf
, sizeof (main_buf
),
509 G_PRIORITY_DEFAULT
, reader_cancel
,
510 readable_cancel
, out
);
512 g_timeout_add (500, timeout
, reader_cancel
);
514 loop
= g_main_loop_new (NULL
, TRUE
);
515 g_main_loop_run (loop
);
516 g_main_loop_unref (loop
);
518 g_object_unref (reader_cancel
);
520 g_object_unref (out
);
527 g_test_init (&argc
, &argv
, NULL
);
529 g_test_add_func ("/win32-streams/pipe-io-test", test_pipe_io
);
530 g_test_add_func ("/win32-streams/pipe-io-cancel-test", test_pipe_io_cancel
);
531 g_test_add_func ("/win32-streams/pipe-io-overlap-test", test_pipe_io_overlap
);
532 g_test_add_func ("/win32-streams/pipe-io-concurrent-test", test_pipe_io_concurrent
);