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

source code of gtk/subprojects/glib/tests/gio-test.c