mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 15:36:14 +01:00
8a0df0a71c
2001-01-25 Tor Lillqvist <tml@iki.fi> * giowin32.c: Socket support rewritten. It was utterly broken, and untested in fact. We still do use a thread for each socket being watched, but instead of blocking in recv() (which of course was plain stupid for sockets being liste()ed on), we block in select(). The read method for sockets calls recv(). It is now possible for the application to call accept(), recv() or send() in the callback, just like on Unix. Tested with code kindly provided by Andrew Lanoix. Rename g_io_channel_win32_new_stream_socket() to g_io_channel_win32_new_socket() as it isn't restricted to stream sockets. * gmain.c (g_poll): Related changes in the Win32 version of g_poll(). When polling for messages, always do a PeekMessage() first. We used to miss messages if several were posted between calls to g_poll(). * giochannel.h: Improve Win32-related comments. * gutf8.c: (Win32) Include <stdio.h> for sprintf. * tests/gio-test.c: (Win32) Add tests for polling for Windows messages. * tests/makefile.mingw.in: Remove superfluous compilation command line.
428 lines
9.7 KiB
C
428 lines
9.7 KiB
C
/* 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.
|
|
*/
|
|
|
|
/* A test program for the main loop and IO channel code.
|
|
* Just run it. Optional parameter is number of sub-processes.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#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>
|
|
#define STRICT
|
|
#include <windows.h>
|
|
#else
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#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 GIOError
|
|
read_all (int fd,
|
|
GIOChannel *channel,
|
|
char *buffer,
|
|
guint nbytes,
|
|
guint *bytes_read)
|
|
{
|
|
guint left = nbytes;
|
|
guint nb;
|
|
GIOError error = G_IO_ERROR_NONE;
|
|
char *bufp = buffer;
|
|
|
|
/* g_io_channel_read() doesn't necessarily return all the
|
|
* data we want at once.
|
|
*/
|
|
*bytes_read = 0;
|
|
while (left)
|
|
{
|
|
error = g_io_channel_read (channel, bufp, left, &nb);
|
|
|
|
if (error != G_IO_ERROR_NONE)
|
|
{
|
|
g_print ("gio-test: ...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" : "???"))));
|
|
if (error == G_IO_ERROR_AGAIN)
|
|
continue;
|
|
break;
|
|
}
|
|
if (nb == 0)
|
|
return error;
|
|
left -= nb;
|
|
bufp += nb;
|
|
*bytes_read += nb;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
shutdown_source (gpointer data)
|
|
{
|
|
if (g_source_remove (*(guint *) data))
|
|
{
|
|
nrunning--;
|
|
if (nrunning == 0)
|
|
g_main_quit (main_loop);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
recv_message (GIOChannel *channel,
|
|
GIOCondition cond,
|
|
gpointer data)
|
|
{
|
|
gint fd = g_io_channel_unix_get_fd (channel);
|
|
gboolean retval = TRUE;
|
|
|
|
g_print ("gio-test: ...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))
|
|
{
|
|
shutdown_source (data);
|
|
retval = FALSE;
|
|
}
|
|
|
|
if (cond & G_IO_IN)
|
|
{
|
|
char buf[BUFSIZE];
|
|
guint nbytes;
|
|
guint nb;
|
|
int i, j, seq;
|
|
GIOError error;
|
|
|
|
error = read_all (fd, channel, (gchar *) &seq, sizeof (seq), &nb);
|
|
if (error == G_IO_ERROR_NONE)
|
|
{
|
|
if (nb == 0)
|
|
{
|
|
g_print ("gio-test: ...from %d: EOF\n", fd);
|
|
shutdown_source (data);
|
|
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 ("gio-test: ...from %d: invalid sequence number %d, expected %d\n",
|
|
fd, seq, seqtab[i].seq);
|
|
g_assert_not_reached ();
|
|
}
|
|
seqtab[i].seq++;
|
|
break;
|
|
}
|
|
|
|
error = read_all (fd, channel, (gchar *) &nbytes, sizeof (nbytes), &nb);
|
|
}
|
|
|
|
if (error != G_IO_ERROR_NONE)
|
|
return FALSE;
|
|
|
|
if (nb == 0)
|
|
{
|
|
g_print ("gio-test: ...from %d: EOF\n", fd);
|
|
shutdown_source (data);
|
|
return FALSE;
|
|
}
|
|
|
|
g_assert (nb == sizeof (nbytes));
|
|
|
|
if (nbytes >= BUFSIZE)
|
|
{
|
|
g_print ("gio-test: ...from %d: nbytes = %d (%#x)!\n", fd, nbytes, nbytes);
|
|
g_assert_not_reached ();
|
|
}
|
|
g_assert (nbytes >= 0 && nbytes < BUFSIZE);
|
|
|
|
g_print ("gio-test: ...from %d: %d bytes\n", fd, nbytes);
|
|
|
|
if (nbytes > 0)
|
|
{
|
|
error = read_all (fd, channel, buf, nbytes, &nb);
|
|
|
|
if (error != G_IO_ERROR_NONE)
|
|
return FALSE;
|
|
|
|
if (nb == 0)
|
|
{
|
|
g_print ("gio-test: ...from %d: EOF\n", fd);
|
|
shutdown_source (data);
|
|
return FALSE;
|
|
}
|
|
|
|
for (j = 0; j < nbytes; j++)
|
|
if (buf[j] != ' ' + ((nbytes + j) % 95))
|
|
{
|
|
g_print ("gio-test: ...from %d: buf[%d] == '%c', should be '%c'\n",
|
|
fd, j, buf[j], 'a' + ((nbytes + j) % 32));
|
|
g_assert_not_reached ();
|
|
}
|
|
g_print ("gio-test: ...from %d: OK\n", fd);
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
static gboolean
|
|
recv_windows_message (GIOChannel *channel,
|
|
GIOCondition cond,
|
|
gpointer data)
|
|
{
|
|
GIOError error;
|
|
MSG msg;
|
|
guint nb;
|
|
|
|
while (1)
|
|
{
|
|
error = g_io_channel_read (channel, &msg, sizeof (MSG), &nb);
|
|
|
|
if (error != G_IO_ERROR_NONE)
|
|
{
|
|
g_print ("gio-test: ...reading Windows message: G_IO_ERROR_%s\n",
|
|
(error == G_IO_ERROR_AGAIN ? "AGAIN" :
|
|
(error == G_IO_ERROR_INVAL ? "INVAL" :
|
|
(error == G_IO_ERROR_UNKNOWN ? "UNKNOWN" : "???"))));
|
|
if (error == G_IO_ERROR_AGAIN)
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
g_print ("gio-test: ...Windows message for %#x: %d,%d,%d\n",
|
|
msg.hwnd, msg.message, msg.wParam, msg.lParam);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT CALLBACK
|
|
window_procedure (HWND hwnd,
|
|
UINT message,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
g_print ("gio-test: window_procedure for %#x: %d,%d,%d\n",
|
|
hwnd, message, wparam, lparam);
|
|
return DefWindowProc (hwnd, message, wparam, lparam);
|
|
}
|
|
|
|
#endif
|
|
|
|
int
|
|
main (int argc,
|
|
char **argv)
|
|
{
|
|
if (argc < 3)
|
|
{
|
|
/* Parent */
|
|
|
|
GIOChannel *my_read_channel;
|
|
gchar *cmdline;
|
|
guint *id;
|
|
int i;
|
|
#ifdef G_OS_WIN32
|
|
GTimeVal start, end;
|
|
GPollFD pollfd;
|
|
int pollresult;
|
|
ATOM klass;
|
|
static WNDCLASS wcl;
|
|
HWND hwnd;
|
|
GIOChannel *windows_messages_channel;
|
|
#endif
|
|
|
|
nkiddies = (argc == 1 ? 1 : atoi(argv[1]));
|
|
seqtab = g_malloc (nkiddies * 2 * sizeof (int));
|
|
|
|
#ifdef G_OS_WIN32
|
|
wcl.style = 0;
|
|
wcl.lpfnWndProc = window_procedure;
|
|
wcl.cbClsExtra = 0;
|
|
wcl.cbWndExtra = 0;
|
|
wcl.hInstance = GetModuleHandle (NULL);
|
|
wcl.hIcon = NULL;
|
|
wcl.hCursor = NULL;
|
|
wcl.hbrBackground = NULL;
|
|
wcl.lpszMenuName = NULL;
|
|
wcl.lpszClassName = "gio-test";
|
|
|
|
klass = RegisterClass (&wcl);
|
|
|
|
if (!klass)
|
|
{
|
|
g_print ("gio-test: RegisterClass failed\n");
|
|
exit (1);
|
|
}
|
|
|
|
hwnd = CreateWindow (klass, "gio-test", 0, 0, 0, 10, 10,
|
|
NULL, NULL, wcl.hInstance, NULL);
|
|
if (!hwnd)
|
|
{
|
|
g_print ("gio-test: CreateWindow failed\n");
|
|
exit (1);
|
|
}
|
|
|
|
windows_messages_channel = g_io_channel_win32_new_messages (hwnd);
|
|
g_io_add_watch (windows_messages_channel, G_IO_IN, recv_windows_message, 0);
|
|
#endif
|
|
|
|
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);
|
|
|
|
nrunning++;
|
|
|
|
#ifdef G_OS_WIN32
|
|
cmdline = g_strdup_printf ("%d:%d:%d",
|
|
pipe_to_sub[0],
|
|
pipe_from_sub[1],
|
|
hwnd);
|
|
_spawnl (_P_NOWAIT, argv[0], argv[0], "--child", cmdline, NULL);
|
|
#else
|
|
cmdline = g_strdup_printf ("%s --child %d:%d &", argv[0],
|
|
pipe_to_sub[0], pipe_from_sub[1]);
|
|
|
|
system (cmdline);
|
|
#endif
|
|
close (pipe_to_sub[0]);
|
|
close (pipe_from_sub [1]);
|
|
|
|
#ifdef G_OS_WIN32
|
|
g_get_current_time (&start);
|
|
g_io_channel_win32_make_pollfd (my_read_channel, G_IO_IN, &pollfd);
|
|
pollresult = g_io_channel_win32_poll (&pollfd, 1, 100);
|
|
g_get_current_time (&end);
|
|
if (end.tv_usec < start.tv_usec)
|
|
end.tv_sec--, end.tv_usec += 1000000;
|
|
g_print ("gio-test: had to wait %ld.%03ld s, result:%d\n",
|
|
end.tv_sec - start.tv_sec,
|
|
(end.tv_usec - start.tv_usec) / 1000,
|
|
pollresult);
|
|
#endif
|
|
}
|
|
|
|
main_loop = g_main_new (FALSE);
|
|
|
|
g_main_run (main_loop);
|
|
}
|
|
else if (argc == 3)
|
|
{
|
|
/* Child */
|
|
|
|
int readfd, writefd;
|
|
#ifdef G_OS_WIN32
|
|
HWND hwnd;
|
|
#endif
|
|
int i, j;
|
|
char buf[BUFSIZE];
|
|
int buflen;
|
|
GTimeVal tv;
|
|
int n;
|
|
|
|
g_get_current_time (&tv);
|
|
|
|
sscanf (argv[2], "%d:%d%n", &readfd, &writefd, &n);
|
|
|
|
#ifdef G_OS_WIN32
|
|
sscanf (argv[2] + n, ":%d", &hwnd);
|
|
#endif
|
|
|
|
srand (tv.tv_sec ^ (tv.tv_usec / 1000) ^ readfd ^ (writefd << 4));
|
|
|
|
for (i = 0; i < 20 + rand() % 20; i++)
|
|
{
|
|
g_usleep (100 + (rand() % 10) * 5000);
|
|
buflen = rand() % BUFSIZE;
|
|
for (j = 0; j < buflen; j++)
|
|
buf[j] = ' ' + ((buflen + j) % 95);
|
|
g_print ("gio-test: child writing %d+%d bytes to %d\n",
|
|
sizeof(i) + sizeof(buflen), buflen, writefd);
|
|
write (writefd, &i, sizeof (i));
|
|
write (writefd, &buflen, sizeof (buflen));
|
|
write (writefd, buf, buflen);
|
|
|
|
#ifdef G_OS_WIN32
|
|
if (rand() % 100 < 5)
|
|
{
|
|
int msg = WM_USER + (rand() % 100);
|
|
WPARAM wparam = rand ();
|
|
LPARAM lparam = rand ();
|
|
g_print ("gio-test: child posting message %d,%d,%d to %#x\n",
|
|
msg, wparam, lparam, hwnd);
|
|
PostMessage (hwnd, msg, wparam, lparam);
|
|
}
|
|
#endif
|
|
}
|
|
g_print ("gio-test: child exiting, closing %d\n", writefd);
|
|
close (writefd);
|
|
}
|
|
else
|
|
g_print ("Huh?\n");
|
|
|
|
return 0;
|
|
}
|
|
|