Branch data Line data Source code
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 : 399 : read_all (int fd,
64 : : GIOChannel *channel,
65 : : char *buffer,
66 : : guint nbytes,
67 : : guint *bytes_read)
68 : : {
69 : 399 : guint left = nbytes;
70 : : gsize nb;
71 : 399 : GIOError error = G_IO_ERROR_NONE;
72 : 399 : char *bufp = buffer;
73 : :
74 : : /* g_io_channel_read() doesn't necessarily return all the
75 : : * data we want at once.
76 : : */
77 : 399 : *bytes_read = 0;
78 [ + + ]: 798 : while (left)
79 : : {
80 : 399 : error = g_io_channel_read (channel, bufp, left, &nb);
81 : :
82 [ - + ]: 399 : if (error != G_IO_ERROR_NONE)
83 : : {
84 : 0 : g_test_message ("io-channel-basic: ...from %d: %d", fd, error);
85 [ # # ]: 0 : if (error == G_IO_ERROR_AGAIN)
86 : 0 : continue;
87 : 0 : break;
88 : : }
89 [ - + ]: 399 : if (nb == 0)
90 : 0 : return error;
91 : 399 : left -= nb;
92 : 399 : bufp += nb;
93 : 399 : *bytes_read += nb;
94 : : }
95 : 399 : return error;
96 : : }
97 : :
98 : : static void
99 : 6 : shutdown_source (gpointer data)
100 : : {
101 : 6 : guint *fd_ptr = data;
102 : :
103 [ + - ]: 6 : if (*fd_ptr != 0)
104 : : {
105 : 6 : g_source_remove (*fd_ptr);
106 : 6 : *fd_ptr = 0;
107 : :
108 : 6 : nrunning--;
109 [ + + ]: 6 : if (nrunning == 0)
110 : 2 : g_main_loop_quit (main_loop);
111 : : }
112 : 6 : }
113 : :
114 : : static gboolean
115 : 137 : recv_message (GIOChannel *channel,
116 : : GIOCondition cond,
117 : : gpointer data)
118 : : {
119 : 137 : gint fd = g_io_channel_unix_get_fd (channel);
120 : 137 : gboolean retval = TRUE;
121 : :
122 [ - + + + : 137 : 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 [ + + ]: 137 : if (cond & (G_IO_ERR | G_IO_HUP))
129 : : {
130 : 6 : shutdown_source (data);
131 : 6 : retval = FALSE;
132 : : }
133 : :
134 [ + + ]: 137 : if (cond & G_IO_IN)
135 : : {
136 : : char buf[BUFSIZE];
137 : 133 : guint nbytes = 0;
138 : : guint nb;
139 : : guint j;
140 : : int i, seq;
141 : : GIOError error;
142 : :
143 : 133 : error = read_all (fd, channel, (gchar *) &seq, sizeof (seq), &nb);
144 [ + - ]: 133 : if (error == G_IO_ERROR_NONE)
145 : : {
146 [ - + ]: 133 : if (nb == 0)
147 : : {
148 : 0 : g_debug ("io-channel-basic: ...from %d: EOF", fd);
149 : 0 : shutdown_source (data);
150 : 0 : return FALSE;
151 : : }
152 : 133 : g_assert_cmpuint (nb, ==, sizeof (nbytes));
153 : :
154 [ + - ]: 362 : for (i = 0; i < nkiddies; i++)
155 [ + + ]: 362 : if (seqtab[i].fd == fd)
156 : : {
157 : 133 : g_assert_cmpint (seq, ==, seqtab[i].seq);
158 : 133 : seqtab[i].seq++;
159 : 133 : break;
160 : : }
161 : :
162 : : error =
163 : 133 : read_all (fd, channel, (gchar *) &nbytes, sizeof (nbytes), &nb);
164 : : }
165 : :
166 [ - + ]: 133 : if (error != G_IO_ERROR_NONE)
167 : 0 : return FALSE;
168 : :
169 [ - + ]: 133 : if (nb == 0)
170 : : {
171 : 0 : g_debug ("io-channel-basic: ...from %d: EOF", fd);
172 : 0 : shutdown_source (data);
173 : 0 : return FALSE;
174 : : }
175 : 133 : g_assert_cmpuint (nb, ==, sizeof (nbytes));
176 : :
177 : 133 : g_assert_cmpuint (nbytes, <, BUFSIZE);
178 : 133 : g_debug ("io-channel-basic: ...from %d: %d bytes", fd, nbytes);
179 [ + - ]: 133 : if (nbytes > 0)
180 : : {
181 : 133 : error = read_all (fd, channel, buf, nbytes, &nb);
182 : :
183 [ - + ]: 133 : if (error != G_IO_ERROR_NONE)
184 : 0 : return FALSE;
185 : :
186 [ - + ]: 133 : if (nb == 0)
187 : : {
188 : 0 : g_debug ("io-channel-basic: ...from %d: EOF", fd);
189 : 0 : shutdown_source (data);
190 : 0 : return FALSE;
191 : : }
192 : :
193 [ + + ]: 316915 : for (j = 0; j < nbytes; j++)
194 : 316782 : g_assert_cmpint (buf[j], ==, ' ' + (char) ((nbytes + j) % 95));
195 : 133 : g_debug ("io-channel-basic: ...from %d: OK", fd);
196 : : }
197 : : }
198 : 137 : 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 : 2 : 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 : 2 : nkiddies = (children_nb > 0 ? children_nb : 1);
288 : 2 : seqtab = g_malloc (nkiddies * 2 * sizeof (int));
289 : :
290 [ + + ]: 8 : for (i = 0; i < nkiddies; i++)
291 : : {
292 : : guint *id;
293 : : int pipe_to_sub[2], pipe_from_sub[2];
294 : :
295 [ + - - + ]: 6 : if (pipe (pipe_to_sub) == -1 || pipe (pipe_from_sub) == -1)
296 : : {
297 : 0 : perror ("pipe");
298 : 0 : exit (1);
299 : : }
300 : :
301 : 6 : seqtab[i].fd = pipe_from_sub[0];
302 : 6 : seqtab[i].seq = 0;
303 : :
304 : 6 : my_read_channel = g_io_channel_unix_new (pipe_from_sub[0]);
305 : :
306 : 6 : id = g_new (guint, 1);
307 : 6 : *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 : 6 : 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 : 6 : cmdline = g_strdup_printf ("%s --child %d:%d &",
322 : : exec_name, pipe_to_sub[0], pipe_from_sub[1]);
323 : 6 : g_assert_no_errno (system (cmdline));
324 : : #endif
325 : 6 : g_free (cmdline);
326 : :
327 : : /* Closing pipes */
328 : 6 : close (pipe_to_sub[0]);
329 : 6 : 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 : 6 : g_io_channel_unref (my_read_channel);
341 : : }
342 : :
343 : 2 : main_loop = g_main_loop_new (NULL, FALSE);
344 : 2 : g_main_loop_run (main_loop);
345 : :
346 : 2 : g_main_loop_unref (main_loop);
347 : 2 : g_free (seqtab);
348 : 2 : }
349 : :
350 : : static void
351 : 6 : 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 : 6 : 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 : 6 : dt = g_get_monotonic_time();
368 : 6 : srand (dt ^ (dt / 1000) ^ readfd ^ (writefd << 4));
369 : :
370 [ + + ]: 139 : for (i = 0; i < 20 + rand () % 10; i++)
371 : : {
372 : 133 : g_usleep ((100 + rand () % 10) * 2500);
373 : 133 : buflen = rand () % BUFSIZE;
374 [ + + ]: 316915 : for (j = 0; j < buflen; j++)
375 : 316782 : buf[j] = ' ' + ((buflen + j) % 95);
376 : 133 : g_debug ("io-channel-basic: child writing %d+%d bytes to %d",
377 : : (int) (sizeof (i) + sizeof (buflen)), buflen, writefd);
378 : 133 : g_assert_cmpint (write (writefd, &i, sizeof (i)), ==, sizeof (i));
379 : 133 : g_assert_cmpint (write (writefd, &buflen, sizeof (buflen)), ==, sizeof (buflen));
380 : 133 : 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 : 6 : g_debug ("io-channel-basic: child exiting, closing %d", writefd);
395 : 6 : close (writefd);
396 : 6 : }
397 : :
398 : : static void
399 : 1 : test_io_basics (void)
400 : : {
401 : 1 : spawn_process (1);
402 : : #ifndef G_OS_WIN32
403 : 1 : spawn_process (5);
404 : : #endif
405 : 1 : }
406 : :
407 : : int
408 : 7 : main (int argc, char *argv[])
409 : : {
410 : : /* Get executable name */
411 : 7 : exec_name = argv[0];
412 : :
413 : : /* Run the tests */
414 : 7 : g_test_init (&argc, &argv, NULL);
415 : :
416 : : /* Run subprocess, if it is the case */
417 [ + + ]: 7 : if (argc > 2)
418 : : {
419 : 6 : run_process (argc, argv);
420 : 6 : return 0;
421 : : }
422 : :
423 : 1 : g_test_add_func ("/gio/io-basics", test_io_basics);
424 : :
425 : 1 : return g_test_run ();
426 : : }
|