1/* GLIB - Library of useful routines for C programming
2 * Copyright (C) 2000  Tor Lillqvist
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
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#undef G_DISABLE_ASSERT
25#undef G_LOG_DOMAIN
26
27#include "config.h"
28
29#include <glib.h>
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <math.h>
34#include <time.h>
35
36#ifdef G_OS_WIN32
37  #include <io.h>
38  #include <fcntl.h>
39  #include <process.h>
40  #define STRICT
41  #include <windows.h>
42  #define pipe(fds) _pipe(fds, 4096, _O_BINARY)
43#else
44  #ifdef HAVE_UNISTD_H
45    #include <unistd.h>
46  #endif
47#endif
48
49static int nrunning;
50static GMainLoop *main_loop;
51
52#define BUFSIZE 5000		/* Larger than the circular buffer in
53				 * giowin32.c on purpose.
54				 */
55
56static int nkiddies;
57
58static struct {
59  int fd;
60  int seq;
61} *seqtab;
62
63static GIOError
64read_all (int         fd,
65	  GIOChannel *channel,
66	  char       *buffer,
67	  guint       nbytes,
68	  guint      *bytes_read)
69{
70  guint left = nbytes;
71  gsize nb;
72  GIOError error = G_IO_ERROR_NONE;
73  char *bufp = buffer;
74
75  /* g_io_channel_read() doesn't necessarily return all the
76   * data we want at once.
77   */
78  *bytes_read = 0;
79  while (left)
80    {
81      error = g_io_channel_read (channel, bufp, left, &nb);
82
83      if (error != G_IO_ERROR_NONE)
84	{
85	  g_print ("gio-test: ...from %d: G_IO_ERROR_%s\n", fd,
86		   (error == G_IO_ERROR_AGAIN ? "AGAIN" :
87		    (error == G_IO_ERROR_INVAL ? "INVAL" :
88		     (error == G_IO_ERROR_UNKNOWN ? "UNKNOWN" : "???"))));
89	  if (error == G_IO_ERROR_AGAIN)
90	    continue;
91	  break;
92	}
93      if (nb == 0)
94	return error;
95      left -= nb;
96      bufp += nb;
97      *bytes_read += nb;
98    }
99  return error;
100}
101
102static void
103shutdown_source (gpointer data)
104{
105  if (g_source_remove (*(guint *) data))
106    {
107      nrunning--;
108      if (nrunning == 0)
109	g_main_loop_quit (main_loop);
110    }
111}
112
113static gboolean
114recv_message (GIOChannel  *channel,
115	      GIOCondition cond,
116	      gpointer    data)
117{
118  gint fd = g_io_channel_unix_get_fd (channel);
119  gboolean retval = TRUE;
120
121#ifdef VERBOSE
122  g_print ("gio-test: ...from %d:%s%s%s%s\n", 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#endif
128
129  if (cond & (G_IO_ERR | G_IO_HUP))
130    {
131      shutdown_source (data);
132      retval = FALSE;
133    }
134
135  if (cond & G_IO_IN)
136    {
137      char buf[BUFSIZE];
138      guint nbytes;
139      guint nb;
140      int i, j, 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#ifdef VERBOSE
149	      g_print ("gio-test: ...from %d: EOF\n", fd);
150#endif
151	      shutdown_source (data);
152	      return FALSE;
153	    }
154
155	  g_assert (nb == sizeof (nbytes));
156
157	  for (i = 0; i < nkiddies; i++)
158	    if (seqtab[i].fd == fd)
159	      {
160		if (seq != seqtab[i].seq)
161		  {
162		    g_print ("gio-test: ...from %d: invalid sequence number %d, expected %d\n",
163			     fd, seq, seqtab[i].seq);
164		    g_assert_not_reached ();
165		  }
166		seqtab[i].seq++;
167		break;
168	      }
169
170	  error = read_all (fd, channel, (gchar *) &nbytes, sizeof (nbytes), &nb);
171	}
172
173      if (error != G_IO_ERROR_NONE)
174	return FALSE;
175
176      if (nb == 0)
177	{
178#ifdef VERBOSE
179	  g_print ("gio-test: ...from %d: EOF\n", fd);
180#endif
181	  shutdown_source (data);
182	  return FALSE;
183	}
184
185      g_assert (nb == sizeof (nbytes));
186
187      if (nbytes >= BUFSIZE)
188	{
189	  g_print ("gio-test: ...from %d: nbytes = %d (%#x)!\n", fd, nbytes, nbytes);
190	  g_assert_not_reached ();
191	}
192      g_assert (nbytes >= 0 && nbytes < BUFSIZE);
193#ifdef VERBOSE
194      g_print ("gio-test: ...from %d: %d bytes\n", fd, nbytes);
195#endif
196      if (nbytes > 0)
197	{
198	  error = read_all (fd, channel, buf, nbytes, &nb);
199
200	  if (error != G_IO_ERROR_NONE)
201	    return FALSE;
202
203	  if (nb == 0)
204	    {
205#ifdef VERBOSE
206	      g_print ("gio-test: ...from %d: EOF\n", fd);
207#endif
208	      shutdown_source (data);
209	      return FALSE;
210	    }
211
212	  for (j = 0; j < nbytes; j++)
213	    if (buf[j] != ' ' + ((nbytes + j) % 95))
214	      {
215		g_print ("gio-test: ...from %d: buf[%d] == '%c', should be '%c'\n",
216			 fd, j, buf[j], 'a' + ((nbytes + j) % 32));
217		g_assert_not_reached ();
218	      }
219#ifdef VERBOSE
220	  g_print ("gio-test: ...from %d: OK\n", fd);
221#endif
222	}
223    }
224  return retval;
225}
226
227#ifdef G_OS_WIN32
228
229static gboolean
230recv_windows_message (GIOChannel  *channel,
231		      GIOCondition cond,
232		      gpointer    data)
233{
234  GIOError error;
235  MSG msg;
236  guint nb;
237
238  while (1)
239    {
240      error = g_io_channel_read (channel, &msg, sizeof (MSG), &nb);
241
242      if (error != G_IO_ERROR_NONE)
243	{
244	  g_print ("gio-test: ...reading Windows message: G_IO_ERROR_%s\n",
245		   (error == G_IO_ERROR_AGAIN ? "AGAIN" :
246		    (error == G_IO_ERROR_INVAL ? "INVAL" :
247		     (error == G_IO_ERROR_UNKNOWN ? "UNKNOWN" : "???"))));
248	  if (error == G_IO_ERROR_AGAIN)
249	    continue;
250	}
251      break;
252    }
253
254  g_print ("gio-test: ...Windows message for %#x: %d,%d,%d\n",
255	   msg.hwnd, msg.message, msg.wParam, msg.lParam);
256
257  return TRUE;
258}
259
260LRESULT CALLBACK
261window_procedure (HWND hwnd,
262		  UINT message,
263		  WPARAM wparam,
264		  LPARAM lparam)
265{
266  g_print ("gio-test: window_procedure for %#x: %d,%d,%d\n",
267	   hwnd, message, wparam, lparam);
268  return DefWindowProc (hwnd, message, wparam, lparam);
269}
270
271#endif
272
273int
274main (int    argc,
275      char **argv)
276{
277  if (argc < 3)
278    {
279      /* Parent */
280
281      GIOChannel *my_read_channel;
282      gchar *cmdline;
283      guint *id;
284      int i;
285#ifdef G_OS_WIN32
286      GTimeVal start, end;
287      GPollFD pollfd;
288      int pollresult;
289      ATOM klass;
290      static WNDCLASS wcl;
291      HWND hwnd;
292      GIOChannel *windows_messages_channel;
293#endif
294
295      nkiddies = (argc == 1 ? 1 : atoi(argv[1]));
296      seqtab = g_malloc (nkiddies * 2 * sizeof (int));
297
298#ifdef G_OS_WIN32
299      wcl.style = 0;
300      wcl.lpfnWndProc = window_procedure;
301      wcl.cbClsExtra = 0;
302      wcl.cbWndExtra = 0;
303      wcl.hInstance = GetModuleHandle (NULL);
304      wcl.hIcon = NULL;
305      wcl.hCursor = NULL;
306      wcl.hbrBackground = NULL;
307      wcl.lpszMenuName = NULL;
308      wcl.lpszClassName = "gio-test";
309
310      klass = RegisterClass (&wcl);
311
312      if (!klass)
313	{
314	  g_print ("gio-test: RegisterClass failed\n");
315	  exit (1);
316	}
317
318      hwnd = CreateWindow (MAKEINTATOM(klass), "gio-test", 0, 0, 0, 10, 10,
319			   NULL, NULL, wcl.hInstance, NULL);
320      if (!hwnd)
321	{
322	  g_print ("gio-test: CreateWindow failed\n");
323	  exit (1);
324	}
325
326      windows_messages_channel = g_io_channel_win32_new_messages ((guint)hwnd);
327      g_io_add_watch (windows_messages_channel, G_IO_IN, recv_windows_message, 0);
328#endif
329
330      for (i = 0; i < nkiddies; i++)
331	{
332	  int pipe_to_sub[2], pipe_from_sub[2];
333
334	  if (pipe (pipe_to_sub) == -1 ||
335	      pipe (pipe_from_sub) == -1)
336	    perror ("pipe"), exit (1);
337
338	  seqtab[i].fd = pipe_from_sub[0];
339	  seqtab[i].seq = 0;
340
341	  my_read_channel = g_io_channel_unix_new (pipe_from_sub[0]);
342
343	  id = g_new (guint, 1);
344	  *id =
345	    g_io_add_watch (my_read_channel,
346			    G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
347			    recv_message,
348			    id);
349
350	  nrunning++;
351
352#ifdef G_OS_WIN32
353	  cmdline = g_strdup_printf ("%d:%d:%d",
354				     pipe_to_sub[0],
355				     pipe_from_sub[1],
356				     hwnd);
357	  _spawnl (_P_NOWAIT, argv[0], argv[0], "--child", cmdline, NULL);
358#else
359	  cmdline = g_strdup_printf ("%s --child %d:%d &", argv[0],
360				     pipe_to_sub[0], pipe_from_sub[1]);
361
362	  system (cmdline);
363#endif
364	  close (pipe_to_sub[0]);
365	  close (pipe_from_sub [1]);
366
367#ifdef G_OS_WIN32
368	  g_get_current_time (&start);
369	  g_io_channel_win32_make_pollfd (my_read_channel, G_IO_IN, &pollfd);
370	  pollresult = g_io_channel_win32_poll (&pollfd, 1, 100);
371	  g_get_current_time (&end);
372	  if (end.tv_usec < start.tv_usec)
373	    end.tv_sec--, end.tv_usec += 1000000;
374	  g_print ("gio-test: had to wait %ld.%03ld s, result:%d\n",
375		   end.tv_sec - start.tv_sec,
376		   (end.tv_usec - start.tv_usec) / 1000,
377		   pollresult);
378#endif
379	}
380
381      main_loop = g_main_loop_new (NULL, FALSE);
382
383      g_main_loop_run (main_loop);
384    }
385  else if (argc == 3)
386    {
387      /* Child */
388
389      int readfd, writefd;
390#ifdef G_OS_WIN32
391      HWND hwnd;
392#endif
393      int i, j;
394      char buf[BUFSIZE];
395      int buflen;
396      GTimeVal tv;
397      int n;
398
399      g_get_current_time (&tv);
400
401      sscanf (argv[2], "%d:%d%n", &readfd, &writefd, &n);
402
403#ifdef G_OS_WIN32
404      sscanf (argv[2] + n, ":%d", &hwnd);
405#endif
406
407      srand (tv.tv_sec ^ (tv.tv_usec / 1000) ^ readfd ^ (writefd << 4));
408
409      for (i = 0; i < 20 + rand() % 20; i++)
410	{
411	  g_usleep (100 + (rand() % 10) * 5000);
412	  buflen = rand() % BUFSIZE;
413	  for (j = 0; j < buflen; j++)
414	    buf[j] = ' ' + ((buflen + j) % 95);
415#ifdef VERBOSE
416	  g_print ("gio-test: child writing %d+%d bytes to %d\n",
417		   (int)(sizeof(i) + sizeof(buflen)), buflen, writefd);
418#endif
419	  write (writefd, &i, sizeof (i));
420	  write (writefd, &buflen, sizeof (buflen));
421	  write (writefd, buf, buflen);
422
423#ifdef G_OS_WIN32
424	  if (rand() % 100 < 5)
425	    {
426	      int msg = WM_USER + (rand() % 100);
427	      WPARAM wparam = rand ();
428	      LPARAM lparam = rand ();
429	      g_print ("gio-test: child posting message %d,%d,%d to %#x\n",
430		       msg, wparam, lparam, hwnd);
431	      PostMessage (hwnd, msg, wparam, lparam);
432	    }
433#endif
434	}
435#ifdef VERBOSE
436      g_print ("gio-test: child exiting, closing %d\n", writefd);
437#endif
438      close (writefd);
439    }
440  else
441    g_print ("Huh?\n");
442
443  return 0;
444}
445
446