Test proggie for GLib main loop and IO channels



I and Craig Setera have been working on an improved implementation of
the IO channel stuff for Win32. I needed a test program. GIMP is a bit
too large, I wanted something smaller that I had written in one
session and understood completely...

I ended up with the below proggie. It seems to work OK on Solaris (and
on Win32 with the new implementation, whee!).

Writing the test program some (to me) unclear issues came up. I think
I have resolved them, but please correct me if I have understood
wrong:

- It is normal for a watch function to occasionally be called with
  both G_IO_IN and G_IO_HUP set. If so, you should read the final data 
  from the channel.

- Is it allowed for g_io_channel_read to return G_IO_ERROR_NONE, but
  the number of bytes being returned still being 0 (presumably when
  this would happen also G_IO_HUP would be set).

- When the watch function is called with G_IO_HUP set, it should call
  g_source_remove? (But not g_io_channel_close() and/or _unref()?)

Hmm, have to hurry to the bus, will write more later perhaps.

Here is the program. Usage: testgio [ nkids ]. 

/* GLIB - Library of useful routines for C programming
 * Copyright (C) 2000  Tor Lillqvist
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <glib.h>

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#ifdef G_OS_WIN32
  #include <io.h>
  #include <fcntl.h>
  #include <process.h>
#else
  #include <unistd.h>
#endif

static int nrunning;
static GMainLoop *main_loop;

#define BUFSIZE 5000		/* Larger than the circular buffer in
				 * giowin32.c on purpose.
				 */

static int nkiddies;

static struct {
  int fd;
  int seq;
} *seqtab;

static gboolean
recv_message (GIOChannel  *channel,
	      GIOCondition cond,
	      gpointer    data)
{
  gint fd = g_io_channel_unix_get_fd (channel);

  g_print ("testgio: ...from %d:%s%s%s%s\n", fd,
	   (cond & G_IO_ERR) ? " ERR" : "",
	   (cond & G_IO_HUP) ? " HUP" : "",
	   (cond & G_IO_IN)  ? " IN"  : "",
	   (cond & G_IO_PRI) ? " PRI" : "");

  if (cond & (G_IO_ERR | G_IO_HUP))
    {
#if 0
      g_io_channel_close (channel);
      g_io_channel_unref (channel);
#endif
      g_source_remove (*(guint *) data);
      nrunning--;
      if (nrunning == 0)
	g_main_quit (main_loop);
    }

  if (cond & G_IO_IN)
    {
      char buf[BUFSIZE];
      guint nbytes;
      guint nb;
      int i, j, seq;
      GIOError error;
      
      error = g_io_channel_read (channel, (gchar *) &seq, sizeof (seq), &nb);
      if (error == G_IO_ERROR_NONE)
	{
	  if (nb == 0)
	    {
	      g_print ("testgio: ...from %d: EOF\n", fd);
	      return FALSE;
	    }
	  
	  g_assert (nb == sizeof (nbytes));

	  for (i = 0; i < nkiddies; i++)
	    if (seqtab[i].fd == fd)
	      {
		if (seq != seqtab[i].seq)
		  {
		    g_print ("testgio: ...from &d: invalid sequence number %d, expected %d\n",
			     seq, seqtab[i].seq);
		    g_assert_not_reached ();
		  }
		seqtab[i].seq++;
		break;
	      }

	  error = g_io_channel_read (channel, (gchar *) &nbytes, sizeof (nbytes), &nb);
	}

      if (error != G_IO_ERROR_NONE)
	{
	  g_print ("testgio: ...from %d: G_IO_ERROR_%s\n", fd,
		   (error == G_IO_ERROR_AGAIN ? "AGAIN" :
		    (error == G_IO_ERROR_INVAL ? "INVAL" :
		     (error == G_IO_ERROR_UNKNOWN ? "UNKNOWN" : "???"))));

	  return FALSE;
	}
      
      if (nb == 0)
	{
	  g_print ("testgio: ...from %d: EOF\n", fd);
	  return FALSE;
	}
      
      g_assert (nb == sizeof (nbytes));

      if (nbytes >= BUFSIZE)
	{
	  g_print ("testgio: ...from %d: nbytes = %d (%#x)!\n", fd, nbytes, nbytes);
	  g_assert_not_reached ();
	}
      g_assert (nbytes >= 0 && nbytes < BUFSIZE);
      
      g_print ("testgio: ...from %d: %d bytes\n", fd, nbytes);
      
      if (nbytes > 0)
	{
	  error = g_io_channel_read (channel, buf, nbytes, &nb);
	  
	  if (error != G_IO_ERROR_NONE)
	    {
	      g_print ("testgio: ...from %d: G_IO_ERROR_%s\n", fd,
		       (error == G_IO_ERROR_AGAIN ? "AGAIN" :
			(error == G_IO_ERROR_INVAL ? "INVAL" :
			 (error == G_IO_ERROR_UNKNOWN ? "UNKNOWN" : "???"))));
	      
	      return FALSE;
	    }

	  if (nb != nbytes)
	    {
	      g_print ("testgio: ...from %d: nb=%d != nbytes=%d\n",
		       fd, nb, nbytes);
	      g_assert_not_reached ();
	    }
	  
	  for (j = 0; j < nbytes; j++)
	    if (buf[j] != ' ' + ((nbytes + j) % 95))
	      {
		g_print ("testgio: ...from %d: buf[%d] == '%c', should be '%c'\n",
			 fd, j, buf[j], 'a' + ((nbytes + j) % 32));
		g_assert_not_reached ();
	      }
	}
    }
  return TRUE;
}

int
main (int    argc,
      char **argv)
{
  srand (time (NULL));
  
  if (argc < 3)
    {
      /* Parent */
      
      GIOChannel *my_read_channel;
      gchar *cmdline;
      guint *id;
      int i;
      
      nkiddies = (argc == 1 ? 1 : atoi(argv[1]));
      seqtab = g_malloc (nkiddies * 2 * sizeof (int));

      for (i = 0; i < nkiddies; i++)
	{
	  int pipe_to_sub[2], pipe_from_sub[2];
	  
	  if (pipe (pipe_to_sub) == -1 ||
	      pipe (pipe_from_sub) == -1)
	    perror ("pipe"), exit (1);
	  
	  
	  seqtab[i].fd = pipe_from_sub[0];
	  seqtab[i].seq = 0;

	  my_read_channel = g_io_channel_unix_new (pipe_from_sub[0]);
	  
	  id = g_new (guint, 1);
	  *id =
	    g_io_add_watch (my_read_channel,
			    G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
			    recv_message,
			    id);
	  
	  cmdline = g_strdup_printf ("%s %d %d &", argv[0],
				     pipe_to_sub[0], pipe_from_sub[1]);
	  
	  nrunning++;
	  
#ifdef G_OS_WIN32
	  {
	    gchar *readfd = g_strdup_printf ("%d", pipe_to_sub[0]);
	    gchar *writefd = g_strdup_printf ("%d", pipe_from_sub[1]);
	    _spawnl (_P_NOWAIT, argv[0], argv[0], readfd, writefd, NULL);
	  }
#else
	  system (cmdline);
#endif
	  close (pipe_to_sub[0]);
	  close (pipe_from_sub [1]);
	}
      
      main_loop = g_main_new (FALSE);
      
      g_main_run (main_loop);
    }
  else if (argc == 3)
    {
      /* Child */
      
      int readfd, writefd;
      int i, j;
      char buf[BUFSIZE];
      int buflen;
      
      readfd = atoi (argv[1]);
      writefd = atoi (argv[2]);
      
      for (i = 0; i < 5 + rand() % 20; i++)
	{
	  g_usleep (100 + (rand() % 20) * 10000);
	  buflen = rand() % BUFSIZE;
	  for (j = 0; j < buflen; j++)
	    buf[j] = ' ' + ((buflen + j) % 95);
	  g_print ("testgio: child writing %d bytes to %d\n", buflen, writefd);
	  write (writefd, &i, sizeof (i));
	  write (writefd, &buflen, sizeof (buflen));
	  write (writefd, buf, buflen);
	}
      g_print ("testgio: child exiting, closing %d\n", writefd);
      close (writefd);
    }
  else
    g_print ("Huh?\n");
  
  return 0;
}


Cheers,
--tml




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]