(root)/
glib-2.79.0/
gio/
tests/
win32-streams.c
       1  /* GLib testing framework examples and tests
       2   * Copyright (C) 2008 Red Hat, Inc
       3   *
       4   * SPDX-License-Identifier: LicenseRef-old-glib-tests
       5   *
       6   * This work is provided "as is"; redistribution and modification
       7   * in whole or in part, in any medium, physical or electronic is
       8   * permitted without restriction.
       9   *
      10   * This work is distributed in the hope that it will be useful,
      11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      13   *
      14   * In no event shall the authors or contributors be liable for any
      15   * direct, indirect, incidental, special, exemplary, or consequential
      16   * damages (including, but not limited to, procurement of substitute
      17   * goods or services; loss of use, data, or profits; or business
      18   * interruption) however caused and on any theory of liability, whether
      19   * in contract, strict liability, or tort (including negligence or
      20   * otherwise) arising in any way out of the use of this software, even
      21   * if advised of the possibility of such damage.
      22   */
      23  
      24  #include <glib/glib.h>
      25  #include <gio/gio.h>
      26  #include <gio/gwin32inputstream.h>
      27  #include <gio/gwin32outputstream.h>
      28  #include <stdlib.h>
      29  #include <string.h>
      30  #include <fcntl.h>
      31  #include <io.h>
      32  
      33  #include <windows.h>
      34  
      35  #define DATA "abcdefghijklmnopqrstuvwxyz"
      36  
      37  int writer_pipe[2], reader_pipe[2];
      38  GCancellable *writer_cancel, *reader_cancel, *main_cancel;
      39  GMainLoop *loop;
      40  
      41  static gpointer
      42  writer_thread (gpointer user_data)
      43  {
      44    GOutputStream *out;
      45    gssize nwrote;
      46    size_t offset;
      47    GError *err = NULL;
      48    HANDLE out_handle;
      49  
      50    g_assert (DuplicateHandle (GetCurrentProcess (),
      51  			     (HANDLE) (gintptr) _get_osfhandle (writer_pipe[1]),
      52  			     GetCurrentProcess (),
      53  			     &out_handle,
      54  			     0, FALSE,
      55  			     DUPLICATE_SAME_ACCESS));
      56    close (writer_pipe[1]);
      57  
      58    out = g_win32_output_stream_new (out_handle, TRUE);
      59    do
      60      {
      61        g_usleep (10);
      62  
      63        offset = 0;
      64        while (offset < sizeof (DATA))
      65  	{
      66  	  nwrote = g_output_stream_write (out, DATA + offset,
      67  					  sizeof (DATA) - offset,
      68  					  writer_cancel, &err);
      69  	  if (nwrote <= 0 || err != NULL)
      70  	    break;
      71  	  offset += nwrote;
      72  	}
      73  
      74        g_assert (nwrote > 0 || err != NULL);
      75      }
      76    while (err == NULL);
      77  
      78    if (g_cancellable_is_cancelled (writer_cancel))
      79      {
      80        g_cancellable_cancel (main_cancel);
      81        g_object_unref (out);
      82        return NULL;
      83      }
      84  
      85    g_warning ("writer: %s", err->message);
      86    g_assert_not_reached ();
      87  }
      88  
      89  static gpointer
      90  reader_thread (gpointer user_data)
      91  {
      92    GInputStream *in;
      93    gssize nread = 0, total;
      94    GError *err = NULL;
      95    char buf[sizeof (DATA)];
      96    HANDLE in_handle;
      97  
      98    g_assert (DuplicateHandle (GetCurrentProcess (),
      99  			     (HANDLE) (gintptr) _get_osfhandle (reader_pipe[0]),
     100  			     GetCurrentProcess (),
     101  			     &in_handle,
     102  			     0, FALSE,
     103  			     DUPLICATE_SAME_ACCESS));
     104    close (reader_pipe[0]);
     105  
     106    in = g_win32_input_stream_new (in_handle, TRUE);
     107  
     108    do
     109      {
     110        total = 0;
     111        while (total < (gssize) sizeof (DATA))
     112  	{
     113  	  nread = g_input_stream_read (in, buf + total, sizeof (buf) - total,
     114  				       reader_cancel, &err);
     115  	  if (nread <= 0 || err != NULL)
     116  	    break;
     117  	  total += nread;
     118  	}
     119  
     120        if (err)
     121  	break;
     122  
     123        if (nread == 0)
     124  	{
     125  	  g_assert (err == NULL);
     126  	  /* pipe closed */
     127  	  g_object_unref (in);
     128  	  return NULL;
     129  	}
     130  
     131        g_assert_cmpstr (buf, ==, DATA);
     132        g_assert (!g_cancellable_is_cancelled (reader_cancel));
     133      }
     134    while (err == NULL);
     135  
     136    g_warning ("reader: %s", err->message);
     137    g_assert_not_reached ();
     138  }
     139  
     140  char main_buf[sizeof (DATA)];
     141  gssize main_len, main_offset;
     142  
     143  static void readable (GObject *source, GAsyncResult *res, gpointer user_data);
     144  static void writable (GObject *source, GAsyncResult *res, gpointer user_data);
     145  
     146  static void
     147  do_main_cancel (GOutputStream *out)
     148  {
     149    g_output_stream_close (out, NULL, NULL);
     150    g_main_loop_quit (loop);
     151  }
     152  
     153  static void
     154  readable (GObject *source, GAsyncResult *res, gpointer user_data)
     155  {
     156    GInputStream *in = G_INPUT_STREAM (source);
     157    GOutputStream *out = user_data;
     158    GError *err = NULL;
     159  
     160    main_len = g_input_stream_read_finish (in, res, &err);
     161  
     162    if (g_cancellable_is_cancelled (main_cancel))
     163      {
     164        do_main_cancel (out);
     165        return;
     166      }
     167  
     168    g_assert (err == NULL);
     169  
     170    main_offset = 0;
     171    g_output_stream_write_async (out, main_buf, main_len,
     172  			       G_PRIORITY_DEFAULT, main_cancel,
     173  			       writable, in);
     174  }
     175  
     176  static void
     177  writable (GObject *source, GAsyncResult *res, gpointer user_data)
     178  {
     179    GOutputStream *out = G_OUTPUT_STREAM (source);
     180    GInputStream *in = user_data;
     181    GError *err = NULL;
     182    gssize nwrote;
     183  
     184    nwrote = g_output_stream_write_finish (out, res, &err);
     185  
     186    if (g_cancellable_is_cancelled (main_cancel))
     187      {
     188        do_main_cancel (out);
     189        return;
     190      }
     191  
     192    g_assert (err == NULL);
     193    g_assert_cmpint (nwrote, <=, main_len - main_offset);
     194  
     195    main_offset += nwrote;
     196    if (main_offset == main_len)
     197      {
     198        g_input_stream_read_async (in, main_buf, sizeof (main_buf),
     199  				 G_PRIORITY_DEFAULT, main_cancel,
     200  				 readable, out);
     201      }
     202    else
     203      {
     204        g_output_stream_write_async (out, main_buf + main_offset,
     205  				   main_len - main_offset,
     206  				   G_PRIORITY_DEFAULT, main_cancel,
     207  				   writable, in);
     208      }
     209  }
     210  
     211  static gboolean
     212  timeout (gpointer cancellable)
     213  {
     214    g_cancellable_cancel (cancellable);
     215    return FALSE;
     216  }
     217  
     218  static void
     219  test_pipe_io (void)
     220  {
     221    GThread *writer, *reader;
     222    GInputStream *in;
     223    GOutputStream *out;
     224    HANDLE in_handle, out_handle;
     225  
     226    /* Split off two (additional) threads, a reader and a writer. From
     227     * the writer thread, write data synchronously in small chunks,
     228     * which gets read asynchronously by the main thread and then
     229     * written asynchronously to the reader thread, which reads it
     230     * synchronously. Eventually a timeout in the main thread will cause
     231     * it to cancel the writer thread, which will in turn cancel the
     232     * read op in the main thread, which will then close the pipe to
     233     * the reader thread, causing the read op to fail.
     234     */
     235  
     236    g_assert (_pipe (writer_pipe, 10, _O_BINARY) == 0 && _pipe (reader_pipe, 10, _O_BINARY) == 0);
     237  
     238    writer_cancel = g_cancellable_new ();
     239    reader_cancel = g_cancellable_new ();
     240    main_cancel = g_cancellable_new ();
     241  
     242    writer = g_thread_new ("writer", writer_thread, NULL);
     243    reader = g_thread_new ("reader", reader_thread, NULL);
     244  
     245    g_assert (DuplicateHandle (GetCurrentProcess (),
     246  			     (HANDLE) (gintptr) _get_osfhandle (writer_pipe[0]),
     247  			     GetCurrentProcess (),
     248  			     &in_handle,
     249  			     0, FALSE,
     250  			     DUPLICATE_SAME_ACCESS));
     251    close (writer_pipe[0]);
     252  
     253    g_assert (DuplicateHandle (GetCurrentProcess (),
     254  			     (HANDLE) (gintptr) _get_osfhandle (reader_pipe[1]),
     255  			     GetCurrentProcess (),
     256  			     &out_handle,
     257  			     0, FALSE,
     258  			     DUPLICATE_SAME_ACCESS));
     259    close (reader_pipe[1]);
     260  
     261    in = g_win32_input_stream_new (in_handle, TRUE);
     262    out = g_win32_output_stream_new (out_handle, TRUE);
     263  
     264    g_input_stream_read_async (in, main_buf, sizeof (main_buf),
     265  			     G_PRIORITY_DEFAULT, main_cancel,
     266  			     readable, out);
     267  
     268    g_timeout_add (500, timeout, writer_cancel);
     269  
     270    loop = g_main_loop_new (NULL, TRUE);
     271    g_main_loop_run (loop);
     272    g_main_loop_unref (loop);
     273  
     274    g_thread_join (reader);
     275    g_thread_join (writer);
     276  
     277    g_object_unref (main_cancel);
     278    g_object_unref (reader_cancel);
     279    g_object_unref (writer_cancel);
     280    g_object_unref (in);
     281    g_object_unref (out);
     282  }
     283  
     284  typedef struct _PipeIOOverlapReader
     285  {
     286    char buf[sizeof (DATA)];
     287    GInputStream *in;
     288    GThread *thread;
     289    GCancellable *cancellable;
     290    gboolean success;
     291  } PipeIOOverlapReader;
     292  
     293  #define TEST_PIPE_IO_OVERLAP (1024 * 4)
     294  
     295  static gpointer
     296  pipe_io_overlap_reader_thread (gpointer user_data)
     297  {
     298    PipeIOOverlapReader *p = user_data;
     299    GError *err = NULL;
     300    gsize read;
     301    guint i;
     302  
     303    for (i = 0; i < TEST_PIPE_IO_OVERLAP; ++i) {
     304      memset (p->buf, 0, sizeof (p->buf));
     305      g_input_stream_read_all (p->in, p->buf, sizeof (p->buf),
     306                               &read, NULL, &err);
     307  
     308      g_assert_cmpuint (read, ==, sizeof (p->buf));
     309      g_assert_no_error (err);
     310      g_assert_cmpstr (p->buf, ==, DATA);
     311    }
     312  
     313    return NULL;
     314  }
     315  
     316  static gpointer
     317  pipe_io_overlap_writer_thread (gpointer user_data)
     318  {
     319    GOutputStream *out = user_data;
     320    GError *err = NULL;
     321    gsize bytes_written;
     322    guint i;
     323  
     324    for (i = 0; i < TEST_PIPE_IO_OVERLAP; ++i) {
     325      g_output_stream_write_all (out, DATA, sizeof (DATA),
     326                                 &bytes_written, NULL, &err);
     327  
     328      g_assert_cmpuint (bytes_written, ==, sizeof (DATA));
     329      g_assert_no_error (err);
     330    }
     331  
     332    return NULL;
     333  }
     334  
     335  static void
     336  test_pipe_io_overlap (void)
     337  {
     338    GOutputStream *out_server, *out_client;
     339    GThread *writer_server, *writer_client;
     340    PipeIOOverlapReader rs, rc;
     341    HANDLE server, client;
     342    gchar name[256];
     343    wchar_t *name_utf16;
     344  
     345    g_snprintf (name, sizeof (name),
     346                "\\\\.\\pipe\\gtest-io-overlap-%u", (guint) GetCurrentProcessId ());
     347  
     348    name_utf16 = g_utf8_to_utf16 (name, -1, NULL, NULL, NULL);
     349    g_assert_nonnull (name_utf16);
     350  
     351    server = CreateNamedPipe (name_utf16,
     352                              PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
     353                              PIPE_READMODE_BYTE | PIPE_WAIT,
     354                              1, 0, 0, 0, NULL);
     355    g_assert (server != INVALID_HANDLE_VALUE);
     356  
     357    client = CreateFile (name_utf16, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
     358    g_assert (client != INVALID_HANDLE_VALUE);
     359  
     360    out_server = g_win32_output_stream_new (server, TRUE);
     361    writer_server = g_thread_new ("writer_server", pipe_io_overlap_writer_thread, out_server);
     362    rs.in = g_win32_input_stream_new (server, TRUE);
     363    rs.thread = g_thread_new ("reader_server", pipe_io_overlap_reader_thread, &rs);
     364  
     365    out_client = g_win32_output_stream_new (client, TRUE);
     366    writer_client = g_thread_new ("writer_client", pipe_io_overlap_writer_thread, out_client);
     367    rc.in = g_win32_input_stream_new (client, TRUE);
     368    rc.thread = g_thread_new ("reader_client", pipe_io_overlap_reader_thread, &rc);
     369  
     370    g_thread_join (writer_client);
     371    g_thread_join (writer_server);
     372    g_thread_join (rc.thread);
     373    g_thread_join (rs.thread);
     374  
     375    g_object_unref (rs.in);
     376    g_object_unref (rc.in);
     377    g_object_unref (out_server);
     378    g_object_unref (out_client);
     379  
     380    g_free (name_utf16);
     381  }
     382  
     383  static gpointer
     384  pipe_io_concurrent_writer_thread (gpointer user_data)
     385  {
     386    GOutputStream *out = user_data;
     387    GError *err = NULL;
     388    gsize bytes_written;
     389  
     390    g_output_stream_write_all (out, DATA, 1, &bytes_written, NULL, &err);
     391  
     392    g_assert_cmpuint (bytes_written, ==, 1);
     393    g_assert_no_error (err);
     394  
     395    return NULL;
     396  }
     397  
     398  static gpointer
     399  pipe_io_concurrent_reader_thread (gpointer user_data)
     400  {
     401    PipeIOOverlapReader *p = user_data;
     402    GError *err = NULL;
     403    gsize read;
     404  
     405    memset (p->buf, 0, sizeof (p->buf));
     406    p->success = g_input_stream_read_all (p->in, p->buf, 1, &read, p->cancellable, &err);
     407  
     408    /* only one thread will succeed, the other will be cancelled */
     409    if (p->success)
     410      {
     411        /* continue the main thread */
     412        write (writer_pipe[1], "", 1);
     413        g_assert_cmpuint (read, ==, 1);
     414        g_assert_no_error (err);
     415      }
     416  
     417    return NULL;
     418  }
     419  
     420  static void
     421  test_pipe_io_concurrent (void)
     422  {
     423    GOutputStream *out_server;
     424    GThread *writer_server;
     425    PipeIOOverlapReader rc1, rc2;
     426    HANDLE server, client;
     427    gchar name[256], c;
     428    wchar_t *name_utf16;
     429  
     430    g_snprintf (name, sizeof (name),
     431                "\\\\.\\pipe\\gtest-io-concurrent-%u", (guint) GetCurrentProcessId ());
     432  
     433    name_utf16 = g_utf8_to_utf16 (name, -1, NULL, NULL, NULL);
     434    g_assert_nonnull (name_utf16);
     435  
     436    server = CreateNamedPipe (name_utf16,
     437                              PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
     438                              PIPE_READMODE_BYTE | PIPE_WAIT,
     439                              1, 0, 0, 0, NULL);
     440    g_assert (server != INVALID_HANDLE_VALUE);
     441    g_assert (_pipe (writer_pipe, 10, _O_BINARY) == 0);
     442  
     443    client = CreateFile (name_utf16, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
     444    g_assert (client != INVALID_HANDLE_VALUE);
     445  
     446    rc1.in = g_win32_input_stream_new (client, TRUE);
     447    rc1.success = FALSE;
     448    rc1.cancellable = g_cancellable_new ();
     449    rc1.thread = g_thread_new ("reader_client", pipe_io_concurrent_reader_thread, &rc1);
     450  
     451    rc2.in = g_win32_input_stream_new (client, TRUE);
     452    rc2.success = FALSE;
     453    rc2.cancellable = g_cancellable_new ();
     454    rc2.thread = g_thread_new ("reader_client", pipe_io_concurrent_reader_thread, &rc2);
     455  
     456    /* FIXME: how to synchronize on both reader thread waiting in read,
     457       before starting the writer thread? */
     458    g_usleep (G_USEC_PER_SEC / 10);
     459  
     460    out_server = g_win32_output_stream_new (server, TRUE);
     461    writer_server = g_thread_new ("writer_server", pipe_io_concurrent_writer_thread, out_server);
     462  
     463    read (writer_pipe[0], &c, 1);
     464  
     465    g_assert (rc1.success ^ rc2.success);
     466  
     467    g_cancellable_cancel (rc1.cancellable);
     468    g_cancellable_cancel (rc2.cancellable);
     469  
     470    g_thread_join (writer_server);
     471    g_thread_join (rc1.thread);
     472    g_thread_join (rc2.thread);
     473  
     474    g_object_unref (rc1.in);
     475    g_object_unref (rc2.in);
     476    g_object_unref (out_server);
     477  
     478    close (writer_pipe[0]);
     479    close (writer_pipe[1]);
     480  
     481    g_free (name_utf16);
     482  }
     483  
     484  static void
     485  readable_cancel (GObject *source, GAsyncResult *res, gpointer user_data)
     486  {
     487    GInputStream *in = G_INPUT_STREAM (source);
     488    GError *err = NULL;
     489    gssize len;
     490  
     491    len = g_input_stream_read_finish (in, res, &err);
     492    g_assert_cmpint (len, ==, -1);
     493    g_assert_error (err, G_IO_ERROR, G_IO_ERROR_CANCELLED);
     494    g_error_free (err);
     495  
     496    g_main_loop_quit (loop);
     497  }
     498  
     499  static void
     500  test_pipe_io_cancel (void)
     501  {
     502    GInputStream *in;
     503    GOutputStream *out;
     504    HANDLE in_handle, out_handle;
     505    gchar name[256];
     506    wchar_t *name_utf16;
     507  
     508    g_snprintf (name, sizeof (name),
     509                "\\\\.\\pipe\\gtest-io-cancel-%u", (guint) GetCurrentProcessId ());
     510  
     511    name_utf16 = g_utf8_to_utf16 (name, -1, NULL, NULL, NULL);
     512    g_assert_nonnull (name_utf16);
     513  
     514    in_handle = CreateNamedPipe (name_utf16,
     515                                 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
     516                                 PIPE_READMODE_BYTE | PIPE_WAIT,
     517                                 1, 0, 0, 0, NULL);
     518    g_assert (in_handle != INVALID_HANDLE_VALUE);
     519  
     520    out_handle = CreateFile (name_utf16, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
     521    g_assert (out_handle != INVALID_HANDLE_VALUE);
     522  
     523    in = g_win32_input_stream_new (in_handle, TRUE);
     524    out = g_win32_output_stream_new (out_handle, TRUE);
     525  
     526    reader_cancel = g_cancellable_new ();
     527    g_input_stream_read_async (in, main_buf, sizeof (main_buf),
     528                               G_PRIORITY_DEFAULT, reader_cancel,
     529                               readable_cancel, out);
     530  
     531    g_timeout_add (500, timeout, reader_cancel);
     532  
     533    loop = g_main_loop_new (NULL, TRUE);
     534    g_main_loop_run (loop);
     535    g_main_loop_unref (loop);
     536  
     537    g_object_unref (reader_cancel);
     538    g_object_unref (in);
     539    g_object_unref (out);
     540  
     541    g_free (name_utf16);
     542  }
     543  
     544  int
     545  main (int   argc,
     546        char *argv[])
     547  {
     548    g_test_init (&argc, &argv, NULL);
     549  
     550    g_test_add_func ("/win32-streams/pipe-io-test", test_pipe_io);
     551    g_test_add_func ("/win32-streams/pipe-io-cancel-test", test_pipe_io_cancel);
     552    g_test_add_func ("/win32-streams/pipe-io-overlap-test", test_pipe_io_overlap);
     553    g_test_add_func ("/win32-streams/pipe-io-concurrent-test", test_pipe_io_concurrent);
     554  
     555    return g_test_run();
     556  }