(root)/
glib-2.79.0/
glib/
tests/
io-channel-basic.c
       1  /* GLIB - Library of useful routines for C programming
       2   * Copyright (C) 2000  Tor Lillqvist
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       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.1 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 Public
      17   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18   */
      19  
      20  /* A test program for the main loop and IO channel code.
      21   * Just run it. Optional parameter is number of sub-processes.
      22   */
      23  
      24  /* We are using g_io_channel_read() which is deprecated */
      25  #ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
      26  #define GLIB_DISABLE_DEPRECATION_WARNINGS
      27  #endif
      28  
      29  #include "config.h"
      30  
      31  #include <glib.h>
      32  
      33  #include <stdio.h>
      34  
      35  #ifdef G_OS_WIN32
      36    #include <io.h>
      37    #include <fcntl.h>
      38    #include <process.h>
      39    #define STRICT
      40    #include <windows.h>
      41    #define pipe(fds) _pipe(fds, 4096, _O_BINARY)
      42  #endif
      43  
      44  #ifdef G_OS_UNIX
      45    #include <unistd.h>
      46  #endif
      47  
      48  static int nrunning;
      49  static GMainLoop *main_loop;
      50  
      51  /* Larger than the circular buffer in giowin32.c on purpose */
      52  #define BUFSIZE 5000
      53  
      54  static int nkiddies;
      55  static char *exec_name;
      56  
      57  static struct {
      58    int fd;
      59    int seq;
      60  } *seqtab;
      61  
      62  static GIOError
      63  read_all (int         fd,
      64            GIOChannel *channel,
      65            char       *buffer,
      66            guint       nbytes,
      67            guint      *bytes_read)
      68  {
      69    guint left = nbytes;
      70    gsize nb;
      71    GIOError error = G_IO_ERROR_NONE;
      72    char *bufp = buffer;
      73  
      74    /* g_io_channel_read() doesn't necessarily return all the
      75     * data we want at once.
      76     */
      77    *bytes_read = 0;
      78    while (left)
      79      {
      80        error = g_io_channel_read (channel, bufp, left, &nb);
      81  
      82        if (error != G_IO_ERROR_NONE)
      83          {
      84            g_test_message ("io-channel-basic: ...from %d: %d", fd, error);
      85            if (error == G_IO_ERROR_AGAIN)
      86              continue;
      87            break;
      88          }
      89        if (nb == 0)
      90          return error;
      91        left -= nb;
      92        bufp += nb;
      93        *bytes_read += nb;
      94      }
      95    return error;
      96  }
      97  
      98  static void
      99  shutdown_source (gpointer data)
     100  {
     101    guint *fd_ptr = data;
     102  
     103    if (*fd_ptr != 0)
     104      {
     105        g_source_remove (*fd_ptr);
     106        *fd_ptr = 0;
     107  
     108        nrunning--;
     109        if (nrunning == 0)
     110          g_main_loop_quit (main_loop);
     111      }
     112  }
     113  
     114  static gboolean
     115  recv_message (GIOChannel  *channel,
     116                GIOCondition cond,
     117                gpointer    data)
     118  {
     119    gint fd = g_io_channel_unix_get_fd (channel);
     120    gboolean retval = TRUE;
     121  
     122    g_debug ("io-channel-basic: ...from %d:%s%s%s%s", fd,
     123             (cond & G_IO_ERR) ? " ERR" : "",
     124             (cond & G_IO_HUP) ? " HUP" : "",
     125             (cond & G_IO_IN)  ? " IN"  : "",
     126             (cond & G_IO_PRI) ? " PRI" : "");
     127  
     128    if (cond & (G_IO_ERR | G_IO_HUP))
     129      {
     130        shutdown_source (data);
     131        retval = FALSE;
     132      }
     133  
     134    if (cond & G_IO_IN)
     135      {
     136        char buf[BUFSIZE];
     137        guint nbytes = 0;
     138        guint nb;
     139        guint j;
     140        int i, seq;
     141        GIOError error;
     142  
     143        error = read_all (fd, channel, (gchar *) &seq, sizeof (seq), &nb);
     144        if (error == G_IO_ERROR_NONE)
     145          {
     146            if (nb == 0)
     147              {
     148                g_debug ("io-channel-basic: ...from %d: EOF", fd);
     149                shutdown_source (data);
     150                return FALSE;
     151              }
     152            g_assert_cmpuint (nb, ==, sizeof (nbytes));
     153  
     154            for (i = 0; i < nkiddies; i++)
     155              if (seqtab[i].fd == fd)
     156                {
     157                  g_assert_cmpint (seq, ==, seqtab[i].seq);
     158                  seqtab[i].seq++;
     159                  break;
     160                }
     161  
     162            error =
     163              read_all (fd, channel, (gchar *) &nbytes, sizeof (nbytes), &nb);
     164          }
     165  
     166        if (error != G_IO_ERROR_NONE)
     167          return FALSE;
     168  
     169        if (nb == 0)
     170          {
     171            g_debug ("io-channel-basic: ...from %d: EOF", fd);
     172            shutdown_source (data);
     173            return FALSE;
     174          }
     175        g_assert_cmpuint (nb, ==, sizeof (nbytes));
     176  
     177        g_assert_cmpuint (nbytes, <, BUFSIZE);
     178        g_debug ("io-channel-basic: ...from %d: %d bytes", fd, nbytes);
     179        if (nbytes > 0)
     180          {
     181            error = read_all (fd, channel, buf, nbytes, &nb);
     182  
     183            if (error != G_IO_ERROR_NONE)
     184              return FALSE;
     185  
     186            if (nb == 0)
     187              {
     188                g_debug ("io-channel-basic: ...from %d: EOF", fd);
     189                shutdown_source (data);
     190                return FALSE;
     191              }
     192  
     193            for (j = 0; j < nbytes; j++)
     194              g_assert_cmpint (buf[j], ==, ' ' + (char) ((nbytes + j) % 95));
     195            g_debug ("io-channel-basic: ...from %d: OK", fd);
     196          }
     197      }
     198    return retval;
     199  }
     200  
     201  #ifdef G_OS_WIN32
     202  static gboolean
     203  recv_windows_message (GIOChannel  *channel,
     204                        GIOCondition cond,
     205                        gpointer    data)
     206  {
     207    GIOError error;
     208    MSG msg;
     209    gsize nb;
     210  
     211    while (1)
     212      {
     213        error = g_io_channel_read (channel, (gchar *) &msg, sizeof (MSG), &nb);
     214  
     215        if (error != G_IO_ERROR_NONE)
     216          {
     217            g_test_message ("io-channel-basic: ...reading Windows message: G_IO_ERROR_%s",
     218                            (error == G_IO_ERROR_AGAIN ? "AGAIN" : (error == G_IO_ERROR_INVAL ? "INVAL" : (error == G_IO_ERROR_UNKNOWN ? "UNKNOWN" : "???"))));
     219            if (error == G_IO_ERROR_AGAIN)
     220              continue;
     221          }
     222        break;
     223      }
     224  
     225    g_test_message ("io-channel-basic: ...Windows message for 0x%p: %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT,
     226                    msg.hwnd, msg.message, msg.wParam, (gintptr) msg.lParam);
     227  
     228    return TRUE;
     229  }
     230  
     231  LRESULT CALLBACK window_procedure (HWND   hwnd,
     232                                     UINT   message,
     233                                     WPARAM wparam,
     234                                     LPARAM lparam);
     235  
     236  LRESULT CALLBACK
     237  window_procedure (HWND hwnd,
     238                    UINT message,
     239                    WPARAM wparam,
     240                    LPARAM lparam)
     241  {
     242    g_test_message ("io-channel-basic: window_procedure for 0x%p: %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT,
     243                    hwnd, message, wparam, (gintptr) lparam);
     244    return DefWindowProc (hwnd, message, wparam, lparam);
     245  }
     246  #endif
     247  
     248  static void
     249  spawn_process (int children_nb)
     250  {
     251    GIOChannel *my_read_channel;
     252    gchar *cmdline;
     253    int i;
     254  
     255  #ifdef G_OS_WIN32
     256    gint64 start, end;
     257    GPollFD pollfd;
     258    int pollresult;
     259    ATOM klass;
     260    static WNDCLASS wcl;
     261    HWND hwnd;
     262    GIOChannel *windows_messages_channel;
     263  
     264    wcl.style = 0;
     265    wcl.lpfnWndProc = window_procedure;
     266    wcl.cbClsExtra = 0;
     267    wcl.cbWndExtra = 0;
     268    wcl.hInstance = GetModuleHandle (NULL);
     269    wcl.hIcon = NULL;
     270    wcl.hCursor = NULL;
     271    wcl.hbrBackground = NULL;
     272    wcl.lpszMenuName = NULL;
     273    wcl.lpszClassName = L"io-channel-basic";
     274  
     275    klass = RegisterClass (&wcl);
     276    g_assert_cmpint (klass, !=, 0);
     277  
     278    hwnd = CreateWindow (MAKEINTATOM (klass), L"io-channel-basic", 0, 0, 0, 10, 10,
     279                         NULL, NULL, wcl.hInstance, NULL);
     280    g_assert_nonnull (hwnd);
     281  
     282    windows_messages_channel =
     283      g_io_channel_win32_new_messages ((guint) (guintptr) hwnd);
     284    g_io_add_watch (windows_messages_channel, G_IO_IN, recv_windows_message, 0);
     285  #endif
     286  
     287    nkiddies = (children_nb > 0 ? children_nb : 1);
     288    seqtab = g_malloc (nkiddies * 2 * sizeof (int));
     289  
     290    for (i = 0; i < nkiddies; i++)
     291      {
     292        guint *id;
     293        int pipe_to_sub[2], pipe_from_sub[2];
     294  
     295        if (pipe (pipe_to_sub) == -1 || pipe (pipe_from_sub) == -1)
     296          {
     297            perror ("pipe");
     298            exit (1);
     299          }
     300  
     301        seqtab[i].fd = pipe_from_sub[0];
     302        seqtab[i].seq = 0;
     303  
     304        my_read_channel = g_io_channel_unix_new (pipe_from_sub[0]);
     305  
     306        id = g_new (guint, 1);
     307        *id = g_io_add_watch_full (my_read_channel,
     308                                   G_PRIORITY_DEFAULT,
     309                                   G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
     310                                   recv_message,
     311                                   id, g_free);
     312        nrunning++;
     313  
     314  #ifdef G_OS_WIN32
     315        /* Spawn new Win32 process */
     316        cmdline =
     317          g_strdup_printf ("%d:%d:0x%p", pipe_to_sub[0], pipe_from_sub[1], hwnd);
     318        _spawnl (_P_NOWAIT, exec_name, exec_name, "--child", cmdline, NULL);
     319  #else
     320        /* Spawn new Unix process */
     321        cmdline = g_strdup_printf ("%s --child %d:%d &",
     322                                   exec_name, pipe_to_sub[0], pipe_from_sub[1]);
     323        g_assert_no_errno (system (cmdline));
     324  #endif
     325        g_free (cmdline);
     326  
     327        /* Closing pipes */
     328        close (pipe_to_sub[0]);
     329        close (pipe_from_sub[1]);
     330  
     331  #ifdef G_OS_WIN32
     332        start = g_get_monotonic_time();
     333        g_io_channel_win32_make_pollfd (my_read_channel, G_IO_IN, &pollfd);
     334        pollresult = g_io_channel_win32_poll (&pollfd, 1, 100);
     335        end = g_get_monotonic_time();
     336  
     337        g_test_message ("io-channel-basic: had to wait %" G_GINT64_FORMAT "s, result:%d",
     338                        (end - start) / 1000000, pollresult);
     339  #endif
     340        g_io_channel_unref (my_read_channel);
     341      }
     342  
     343    main_loop = g_main_loop_new (NULL, FALSE);
     344    g_main_loop_run (main_loop);
     345  
     346    g_main_loop_unref (main_loop);
     347    g_free (seqtab);
     348  }
     349  
     350  static void
     351  run_process (int argc, char *argv[])
     352  {
     353    int readfd, writefd;
     354    gint64 dt;
     355    char buf[BUFSIZE];
     356    int buflen, i, j, n;
     357  #ifdef G_OS_WIN32
     358    HWND hwnd;
     359  #endif
     360  
     361    /* Extract parameters */
     362    sscanf (argv[2], "%d:%d%n", &readfd, &writefd, &n);
     363  #ifdef G_OS_WIN32
     364    sscanf (argv[2] + n, ":0x%p", &hwnd);
     365  #endif
     366  
     367    dt = g_get_monotonic_time();
     368    srand (dt ^ (dt / 1000) ^ readfd ^ (writefd << 4));
     369  
     370    for (i = 0; i < 20 + rand () % 10; i++)
     371      {
     372        g_usleep ((100 + rand () % 10) * 2500);
     373        buflen = rand () % BUFSIZE;
     374        for (j = 0; j < buflen; j++)
     375          buf[j] = ' ' + ((buflen + j) % 95);
     376        g_debug ("io-channel-basic: child writing %d+%d bytes to %d",
     377                 (int) (sizeof (i) + sizeof (buflen)), buflen, writefd);
     378        g_assert_cmpint (write (writefd, &i, sizeof (i)), ==, sizeof (i));
     379        g_assert_cmpint (write (writefd, &buflen, sizeof (buflen)), ==, sizeof (buflen));
     380        g_assert_cmpint (write (writefd, buf, buflen), ==, buflen);
     381  
     382  #ifdef G_OS_WIN32
     383        if (i % 10 == 0)
     384          {
     385            int msg = WM_USER + (rand () % 100);
     386            WPARAM wparam = rand ();
     387            LPARAM lparam = rand ();
     388            g_test_message ("io-channel-basic: child posting message %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT " to 0x%p",
     389                            msg, wparam, (gintptr) lparam, hwnd);
     390            PostMessage (hwnd, msg, wparam, lparam);
     391          }
     392  #endif
     393      }
     394    g_debug ("io-channel-basic: child exiting, closing %d", writefd);
     395    close (writefd);
     396  }
     397  
     398  static void
     399  test_io_basics (void)
     400  {
     401    spawn_process (1);
     402  #ifndef G_OS_WIN32
     403    spawn_process (5);
     404  #endif
     405  }
     406  
     407  int
     408  main (int argc, char *argv[])
     409  {
     410    /* Get executable name */
     411    exec_name = argv[0];
     412  
     413    /* Run the tests */
     414    g_test_init (&argc, &argv, NULL);
     415  
     416    /* Run subprocess, if it is the case */
     417    if (argc > 2)
     418      {
     419        run_process (argc, argv);
     420        return 0;
     421      }
     422  
     423    g_test_add_func ("/gio/io-basics", test_io_basics);
     424  
     425    return g_test_run ();
     426  }