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 49 static int nrunning; 50 static GMainLoop *main_loop; 51 52 #define BUFSIZE 5000 /* Larger than the circular buffer in 53 * giowin32.c on purpose. 54 */ 55 56 static int nkiddies; 57 58 static struct { 59 int fd; 60 int seq; 61 } *seqtab; 62 63 static GIOError 64 read_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 102 static void 103 shutdown_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 113 static gboolean 114 recv_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 229 static gboolean 230 recv_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 260 LRESULT CALLBACK 261 window_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 273 int 274 main (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