Finally, a new and improved IO Channel and condition watch implementation
authorTor Lillqvist <tml@iki.fi>
Sat, 29 Jul 2000 20:59:07 +0000 (20:59 +0000)
committerTor Lillqvist <tml@src.gnome.org>
Sat, 29 Jul 2000 20:59:07 +0000 (20:59 +0000)
2000-07-30  Tor Lillqvist  <tml@iki.fi>

Finally, a new and improved IO Channel and condition watch
implementation for Win32. Based on code provided by Craig Setera.

When watching file descriptors, for which there is no select()
like functionality on Win32 that would work on all Win32 platforms
for all types of file descriptors (including anonymous pipes), we
start a new thread that blocks while trying to read from the file
descriptor. When the read returns, a Win32 Event is signalled that
the polling routine eventually notices. Meanwhile, the data being
read is stored in a circular buffer, from where the IO channel's
read() method picks it up.

If the buffer fills up the reading thread has to wait for space
becoming available. For this another Win32 Event is used. The IO
Channel's read() method signals this when it has read some data
out of the buffer.

The separate reader thread(s), and the circular buffer(s) with
associated events mean lots of possibilities for fun parallellism
errors. But it seems to work OK, i.e. GIMP runs.

* gmain.c: Small changes to the Win32 polling function.
(g_main_win32_get_poll_func): New function. Perhaps it would be a
good idea to provide this on all platforms.

* giowin32.c: The bulk of the new implementation.
(g_io_channel_win32_wait_for_condition): New function. To be used
where on Unix one does a select() on the channel's fd, like
libgimp's gimp_extension_process(). Could be provided on all
platforms.

* glib.h: Update documentation for IO Channels on Win32. Remove
the declarations for the as of now obsolete old functions related
to IO Channels for pipes with "wakeup" messages.

* glib.def: Some new functions.

* tests/gio-test.c: New file, to test GIOChannel and main loop.

* tests/Makefile.am
* tests/makefile.mingw.in: Add it.

19 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
giowin32.c
glib.def
glib.h
glib/giowin32.c
glib/glib.def
glib/glib.h
glib/gmain.c
gmain.c
tests/Makefile.am
tests/gio-test.c [new file with mode: 0644]
tests/makefile.mingw.in

index 1f3fea9..aac6ab2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,47 @@
+2000-07-30  Tor Lillqvist  <tml@iki.fi>
+
+       Finally, a new and improved IO Channel and condition watch
+       implementation for Win32. Based on code provided by Craig Setera.
+
+       When watching file descriptors, for which there is no select()
+       like functionality on Win32 that would work on all Win32 platforms
+       for all types of file descriptors (including anonymous pipes), we
+       start a new thread that blocks while trying to read from the file
+       descriptor. When the read returns, a Win32 Event is signalled that
+       the polling routine eventually notices. Meanwhile, the data being
+       read is stored in a circular buffer, from where the IO channel's
+       read() method picks it up.
+       If the buffer fills up the reading thread has to wait for space
+       becoming available. For this another Win32 Event is used. The IO
+       Channel's read() method signals this when it has read some data
+       out of the buffer.
+
+       The separate reader thread(s), and the circular buffer(s) with
+       associated events mean lots of possibilities for fun parallellism
+       errors. But it seems to work OK, i.e. GIMP runs.
+
+       * gmain.c: Small changes to the Win32 polling function.
+       (g_main_win32_get_poll_func): New function. Perhaps it would be a
+       good idea to provide this on all platforms.
+
+       * giowin32.c: The bulk of the new implementation.
+       (g_io_channel_win32_wait_for_condition): New function. To be used
+       where on Unix one does a select() on the channel's fd, like
+       libgimp's gimp_extension_process(). Could be provided on all
+       platforms.
+
+       * glib.h: Update documentation for IO Channels on Win32. Remove
+       the declarations for the as of now obsolete old functions related
+       to IO Channels for pipes with "wakeup" messages.
+       
+       * glib.def: Some new functions.
+       
+       * tests/gio-test.c: New file, to test GIOChannel and main loop.
+
+       * tests/Makefile.am
+       * tests/makefile.mingw.in: Add it.
+
 Thu Jul 27 05:15:11 2000  Tim Janik  <timj@gtk.org>
 
        * gstrfuncs.c (g_strlcpy, g_strlcat): completed tor's fix
index 1f3fea9..aac6ab2 100644 (file)
@@ -1,3 +1,47 @@
+2000-07-30  Tor Lillqvist  <tml@iki.fi>
+
+       Finally, a new and improved IO Channel and condition watch
+       implementation for Win32. Based on code provided by Craig Setera.
+
+       When watching file descriptors, for which there is no select()
+       like functionality on Win32 that would work on all Win32 platforms
+       for all types of file descriptors (including anonymous pipes), we
+       start a new thread that blocks while trying to read from the file
+       descriptor. When the read returns, a Win32 Event is signalled that
+       the polling routine eventually notices. Meanwhile, the data being
+       read is stored in a circular buffer, from where the IO channel's
+       read() method picks it up.
+       If the buffer fills up the reading thread has to wait for space
+       becoming available. For this another Win32 Event is used. The IO
+       Channel's read() method signals this when it has read some data
+       out of the buffer.
+
+       The separate reader thread(s), and the circular buffer(s) with
+       associated events mean lots of possibilities for fun parallellism
+       errors. But it seems to work OK, i.e. GIMP runs.
+
+       * gmain.c: Small changes to the Win32 polling function.
+       (g_main_win32_get_poll_func): New function. Perhaps it would be a
+       good idea to provide this on all platforms.
+
+       * giowin32.c: The bulk of the new implementation.
+       (g_io_channel_win32_wait_for_condition): New function. To be used
+       where on Unix one does a select() on the channel's fd, like
+       libgimp's gimp_extension_process(). Could be provided on all
+       platforms.
+
+       * glib.h: Update documentation for IO Channels on Win32. Remove
+       the declarations for the as of now obsolete old functions related
+       to IO Channels for pipes with "wakeup" messages.
+       
+       * glib.def: Some new functions.
+       
+       * tests/gio-test.c: New file, to test GIOChannel and main loop.
+
+       * tests/Makefile.am
+       * tests/makefile.mingw.in: Add it.
+
 Thu Jul 27 05:15:11 2000  Tim Janik  <timj@gtk.org>
 
        * gstrfuncs.c (g_strlcpy, g_strlcat): completed tor's fix
index 1f3fea9..aac6ab2 100644 (file)
@@ -1,3 +1,47 @@
+2000-07-30  Tor Lillqvist  <tml@iki.fi>
+
+       Finally, a new and improved IO Channel and condition watch
+       implementation for Win32. Based on code provided by Craig Setera.
+
+       When watching file descriptors, for which there is no select()
+       like functionality on Win32 that would work on all Win32 platforms
+       for all types of file descriptors (including anonymous pipes), we
+       start a new thread that blocks while trying to read from the file
+       descriptor. When the read returns, a Win32 Event is signalled that
+       the polling routine eventually notices. Meanwhile, the data being
+       read is stored in a circular buffer, from where the IO channel's
+       read() method picks it up.
+       If the buffer fills up the reading thread has to wait for space
+       becoming available. For this another Win32 Event is used. The IO
+       Channel's read() method signals this when it has read some data
+       out of the buffer.
+
+       The separate reader thread(s), and the circular buffer(s) with
+       associated events mean lots of possibilities for fun parallellism
+       errors. But it seems to work OK, i.e. GIMP runs.
+
+       * gmain.c: Small changes to the Win32 polling function.
+       (g_main_win32_get_poll_func): New function. Perhaps it would be a
+       good idea to provide this on all platforms.
+
+       * giowin32.c: The bulk of the new implementation.
+       (g_io_channel_win32_wait_for_condition): New function. To be used
+       where on Unix one does a select() on the channel's fd, like
+       libgimp's gimp_extension_process(). Could be provided on all
+       platforms.
+
+       * glib.h: Update documentation for IO Channels on Win32. Remove
+       the declarations for the as of now obsolete old functions related
+       to IO Channels for pipes with "wakeup" messages.
+       
+       * glib.def: Some new functions.
+       
+       * tests/gio-test.c: New file, to test GIOChannel and main loop.
+
+       * tests/Makefile.am
+       * tests/makefile.mingw.in: Add it.
+
 Thu Jul 27 05:15:11 2000  Tim Janik  <timj@gtk.org>
 
        * gstrfuncs.c (g_strlcpy, g_strlcat): completed tor's fix
index 1f3fea9..aac6ab2 100644 (file)
@@ -1,3 +1,47 @@
+2000-07-30  Tor Lillqvist  <tml@iki.fi>
+
+       Finally, a new and improved IO Channel and condition watch
+       implementation for Win32. Based on code provided by Craig Setera.
+
+       When watching file descriptors, for which there is no select()
+       like functionality on Win32 that would work on all Win32 platforms
+       for all types of file descriptors (including anonymous pipes), we
+       start a new thread that blocks while trying to read from the file
+       descriptor. When the read returns, a Win32 Event is signalled that
+       the polling routine eventually notices. Meanwhile, the data being
+       read is stored in a circular buffer, from where the IO channel's
+       read() method picks it up.
+       If the buffer fills up the reading thread has to wait for space
+       becoming available. For this another Win32 Event is used. The IO
+       Channel's read() method signals this when it has read some data
+       out of the buffer.
+
+       The separate reader thread(s), and the circular buffer(s) with
+       associated events mean lots of possibilities for fun parallellism
+       errors. But it seems to work OK, i.e. GIMP runs.
+
+       * gmain.c: Small changes to the Win32 polling function.
+       (g_main_win32_get_poll_func): New function. Perhaps it would be a
+       good idea to provide this on all platforms.
+
+       * giowin32.c: The bulk of the new implementation.
+       (g_io_channel_win32_wait_for_condition): New function. To be used
+       where on Unix one does a select() on the channel's fd, like
+       libgimp's gimp_extension_process(). Could be provided on all
+       platforms.
+
+       * glib.h: Update documentation for IO Channels on Win32. Remove
+       the declarations for the as of now obsolete old functions related
+       to IO Channels for pipes with "wakeup" messages.
+       
+       * glib.def: Some new functions.
+       
+       * tests/gio-test.c: New file, to test GIOChannel and main loop.
+
+       * tests/Makefile.am
+       * tests/makefile.mingw.in: Add it.
+
 Thu Jul 27 05:15:11 2000  Tim Janik  <timj@gtk.org>
 
        * gstrfuncs.c (g_strlcpy, g_strlcat): completed tor's fix
index 1f3fea9..aac6ab2 100644 (file)
@@ -1,3 +1,47 @@
+2000-07-30  Tor Lillqvist  <tml@iki.fi>
+
+       Finally, a new and improved IO Channel and condition watch
+       implementation for Win32. Based on code provided by Craig Setera.
+
+       When watching file descriptors, for which there is no select()
+       like functionality on Win32 that would work on all Win32 platforms
+       for all types of file descriptors (including anonymous pipes), we
+       start a new thread that blocks while trying to read from the file
+       descriptor. When the read returns, a Win32 Event is signalled that
+       the polling routine eventually notices. Meanwhile, the data being
+       read is stored in a circular buffer, from where the IO channel's
+       read() method picks it up.
+       If the buffer fills up the reading thread has to wait for space
+       becoming available. For this another Win32 Event is used. The IO
+       Channel's read() method signals this when it has read some data
+       out of the buffer.
+
+       The separate reader thread(s), and the circular buffer(s) with
+       associated events mean lots of possibilities for fun parallellism
+       errors. But it seems to work OK, i.e. GIMP runs.
+
+       * gmain.c: Small changes to the Win32 polling function.
+       (g_main_win32_get_poll_func): New function. Perhaps it would be a
+       good idea to provide this on all platforms.
+
+       * giowin32.c: The bulk of the new implementation.
+       (g_io_channel_win32_wait_for_condition): New function. To be used
+       where on Unix one does a select() on the channel's fd, like
+       libgimp's gimp_extension_process(). Could be provided on all
+       platforms.
+
+       * glib.h: Update documentation for IO Channels on Win32. Remove
+       the declarations for the as of now obsolete old functions related
+       to IO Channels for pipes with "wakeup" messages.
+       
+       * glib.def: Some new functions.
+       
+       * tests/gio-test.c: New file, to test GIOChannel and main loop.
+
+       * tests/Makefile.am
+       * tests/makefile.mingw.in: Add it.
+
 Thu Jul 27 05:15:11 2000  Tim Janik  <timj@gtk.org>
 
        * gstrfuncs.c (g_strlcpy, g_strlcat): completed tor's fix
index 1f3fea9..aac6ab2 100644 (file)
@@ -1,3 +1,47 @@
+2000-07-30  Tor Lillqvist  <tml@iki.fi>
+
+       Finally, a new and improved IO Channel and condition watch
+       implementation for Win32. Based on code provided by Craig Setera.
+
+       When watching file descriptors, for which there is no select()
+       like functionality on Win32 that would work on all Win32 platforms
+       for all types of file descriptors (including anonymous pipes), we
+       start a new thread that blocks while trying to read from the file
+       descriptor. When the read returns, a Win32 Event is signalled that
+       the polling routine eventually notices. Meanwhile, the data being
+       read is stored in a circular buffer, from where the IO channel's
+       read() method picks it up.
+       If the buffer fills up the reading thread has to wait for space
+       becoming available. For this another Win32 Event is used. The IO
+       Channel's read() method signals this when it has read some data
+       out of the buffer.
+
+       The separate reader thread(s), and the circular buffer(s) with
+       associated events mean lots of possibilities for fun parallellism
+       errors. But it seems to work OK, i.e. GIMP runs.
+
+       * gmain.c: Small changes to the Win32 polling function.
+       (g_main_win32_get_poll_func): New function. Perhaps it would be a
+       good idea to provide this on all platforms.
+
+       * giowin32.c: The bulk of the new implementation.
+       (g_io_channel_win32_wait_for_condition): New function. To be used
+       where on Unix one does a select() on the channel's fd, like
+       libgimp's gimp_extension_process(). Could be provided on all
+       platforms.
+
+       * glib.h: Update documentation for IO Channels on Win32. Remove
+       the declarations for the as of now obsolete old functions related
+       to IO Channels for pipes with "wakeup" messages.
+       
+       * glib.def: Some new functions.
+       
+       * tests/gio-test.c: New file, to test GIOChannel and main loop.
+
+       * tests/Makefile.am
+       * tests/makefile.mingw.in: Add it.
+
 Thu Jul 27 05:15:11 2000  Tim Janik  <timj@gtk.org>
 
        * gstrfuncs.c (g_strlcpy, g_strlcat): completed tor's fix
index 1f3fea9..aac6ab2 100644 (file)
@@ -1,3 +1,47 @@
+2000-07-30  Tor Lillqvist  <tml@iki.fi>
+
+       Finally, a new and improved IO Channel and condition watch
+       implementation for Win32. Based on code provided by Craig Setera.
+
+       When watching file descriptors, for which there is no select()
+       like functionality on Win32 that would work on all Win32 platforms
+       for all types of file descriptors (including anonymous pipes), we
+       start a new thread that blocks while trying to read from the file
+       descriptor. When the read returns, a Win32 Event is signalled that
+       the polling routine eventually notices. Meanwhile, the data being
+       read is stored in a circular buffer, from where the IO channel's
+       read() method picks it up.
+       If the buffer fills up the reading thread has to wait for space
+       becoming available. For this another Win32 Event is used. The IO
+       Channel's read() method signals this when it has read some data
+       out of the buffer.
+
+       The separate reader thread(s), and the circular buffer(s) with
+       associated events mean lots of possibilities for fun parallellism
+       errors. But it seems to work OK, i.e. GIMP runs.
+
+       * gmain.c: Small changes to the Win32 polling function.
+       (g_main_win32_get_poll_func): New function. Perhaps it would be a
+       good idea to provide this on all platforms.
+
+       * giowin32.c: The bulk of the new implementation.
+       (g_io_channel_win32_wait_for_condition): New function. To be used
+       where on Unix one does a select() on the channel's fd, like
+       libgimp's gimp_extension_process(). Could be provided on all
+       platforms.
+
+       * glib.h: Update documentation for IO Channels on Win32. Remove
+       the declarations for the as of now obsolete old functions related
+       to IO Channels for pipes with "wakeup" messages.
+       
+       * glib.def: Some new functions.
+       
+       * tests/gio-test.c: New file, to test GIOChannel and main loop.
+
+       * tests/Makefile.am
+       * tests/makefile.mingw.in: Add it.
+
 Thu Jul 27 05:15:11 2000  Tim Janik  <timj@gtk.org>
 
        * gstrfuncs.c (g_strlcpy, g_strlcat): completed tor's fix
index 1f3fea9..aac6ab2 100644 (file)
@@ -1,3 +1,47 @@
+2000-07-30  Tor Lillqvist  <tml@iki.fi>
+
+       Finally, a new and improved IO Channel and condition watch
+       implementation for Win32. Based on code provided by Craig Setera.
+
+       When watching file descriptors, for which there is no select()
+       like functionality on Win32 that would work on all Win32 platforms
+       for all types of file descriptors (including anonymous pipes), we
+       start a new thread that blocks while trying to read from the file
+       descriptor. When the read returns, a Win32 Event is signalled that
+       the polling routine eventually notices. Meanwhile, the data being
+       read is stored in a circular buffer, from where the IO channel's
+       read() method picks it up.
+       If the buffer fills up the reading thread has to wait for space
+       becoming available. For this another Win32 Event is used. The IO
+       Channel's read() method signals this when it has read some data
+       out of the buffer.
+
+       The separate reader thread(s), and the circular buffer(s) with
+       associated events mean lots of possibilities for fun parallellism
+       errors. But it seems to work OK, i.e. GIMP runs.
+
+       * gmain.c: Small changes to the Win32 polling function.
+       (g_main_win32_get_poll_func): New function. Perhaps it would be a
+       good idea to provide this on all platforms.
+
+       * giowin32.c: The bulk of the new implementation.
+       (g_io_channel_win32_wait_for_condition): New function. To be used
+       where on Unix one does a select() on the channel's fd, like
+       libgimp's gimp_extension_process(). Could be provided on all
+       platforms.
+
+       * glib.h: Update documentation for IO Channels on Win32. Remove
+       the declarations for the as of now obsolete old functions related
+       to IO Channels for pipes with "wakeup" messages.
+       
+       * glib.def: Some new functions.
+       
+       * tests/gio-test.c: New file, to test GIOChannel and main loop.
+
+       * tests/Makefile.am
+       * tests/makefile.mingw.in: Add it.
+
 Thu Jul 27 05:15:11 2000  Tim Janik  <timj@gtk.org>
 
        * gstrfuncs.c (g_strlcpy, g_strlcat): completed tor's fix
index 36fa82c..c06df4a 100644 (file)
@@ -3,6 +3,7 @@
  *
  * giowin32.c: IO Channels for Win32.
  * Copyright 1998 Owen Taylor and Tor Lillqvist
+ * Copyright 1999-2000 Tor Lillqvist and Craig Setera
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -11,7 +12,7 @@
  *
  * 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
+ * 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
  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
  * file for a list of people on the GLib Team.  See the ChangeLog
  * files for a list of changes.  These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
  */
 
+/* Define this to get (very) verbose logging */
+/* #define G_IO_WIN32_DEBUG */
 
-#include "config.h"
 #include "glib.h"
+
+#include <stdlib.h>
 #include <windows.h>
-#include <winsock.h>           /* Not everybody has winsock2 */
+#include <winsock.h>          /* Not everybody has winsock2 */
 #include <fcntl.h>
 #include <io.h>
+#include <process.h>
 #include <errno.h>
-#include <sys/types.h>
-
-#include <stdio.h>
+#include <sys/stat.h>
 
 typedef struct _GIOWin32Channel GIOWin32Channel;
 typedef struct _GIOWin32Watch GIOWin32Watch;
 
-guint g_pipe_readable_msg;
+#define BUFFER_SIZE 4096
 
 typedef enum {
   G_IO_WINDOWS_MESSAGES,       /* Windows messages */
-  G_IO_FILE_DESC,              /* Unix-like file descriptors from _open*/
-  G_IO_PIPE,                   /* pipe, with windows messages for signalling */
-  G_IO_STREAM_SOCKET           /* Stream sockets */
+  G_IO_FILE_DESC,              /* Unix-like file descriptors from
+                                * _open() or _pipe(). Read with read().
+                                * Have to create separate thread to read.
+                                */
+  G_IO_STREAM_SOCKET           /* Stream sockets. Similar as fds, but
+                                * read with recv().
+                                */
 } GIOWin32ChannelType;
 
 struct _GIOWin32Channel {
@@ -58,19 +65,49 @@ struct _GIOWin32Channel {
                                 * as provided by WinSock.
                                 */
   GIOWin32ChannelType type;
-
+  
   /* This is used by G_IO_WINDOWS_MESSAGES channels */
   HWND hwnd;                   /* handle of window, or NULL */
-
-  /* This is used by G_IO_PIPE channels */
-  guint peer;                  /* thread id of reader */
-  guint peer_fd;               /* fd in the reader */
-  guint offset;                        /* counter of accumulated bytes */
-  guint need_wakeups;          /* in output channels whether the
-                                * reader needs wakeups
+  
+  /* Following fields used by fd and socket channels for input */
+  
+  /* Data is kept in a circular buffer. To be able to distinguish between
+   * empty and full buffer, we cannot fill it completely, but have to
+   * leave a one character gap.
+   *
+   * Data available is between indexes rdp and wrp-1 (modulo BUFFER_SIZE).
+   *
+   * Empty:    wrp == rdp
+   * Full:     (wrp + 1) % BUFFER_SIZE == rdp
+   * Partial:  otherwise
+   */
+  guchar *buffer;              /* (Circular) buffer */
+  gint wrp, rdp;               /* Buffer indices for writing and reading */
+  gboolean running;            /* Is reader thread running. FALSE if
+                                * EOF has been reached.
                                 */
+  guint thread_id;             /* If non-NULL has a reader thread, or has
+                                * had.*/
+  HANDLE data_avail_event;
+  HANDLE space_avail_event;
+  CRITICAL_SECTION mutex;
+  
+  /* Function that actually reads from fd */
+  int (*reader) (int fd, guchar *buf, int len);
 };
 
+#define LOCK(mutex) EnterCriticalSection (&mutex)
+#define UNLOCK(mutex) LeaveCriticalSection (&mutex)
+
+/* Temporarilyu change a PRINT to PRINT_ to get just *that* message */
+#define PRINT_(x) g_print x
+
+#ifdef G_IO_WIN32_DEBUG
+#define PRINT(x) PRINT_(x)
+#else
+#define PRINT(x)
+#endif
+
 struct _GIOWin32Watch {
   GPollFD       pollfd;
   GIOChannel   *channel;
@@ -78,346 +115,314 @@ struct _GIOWin32Watch {
   GIOFunc       callback;
 };
 
-static gboolean g_io_win32_msg_prepare  (gpointer  source_data, 
-                                        GTimeVal *current_time,
-                                        gint     *timeout);
-static gboolean g_io_win32_msg_check    (gpointer  source_data,
-                                        GTimeVal *current_time);
-static gboolean g_io_win32_msg_dispatch (gpointer  source_data,
-                                        GTimeVal *current_time,
-                                        gpointer  user_data);
-
-static gboolean g_io_win32_fd_prepare  (gpointer  source_data, 
-                                       GTimeVal *current_time,
-                                       gint     *timeout);
-static gboolean g_io_win32_fd_check    (gpointer  source_data,
-                                       GTimeVal *current_time);
-static gboolean g_io_win32_fd_dispatch (gpointer  source_data,
-                                       GTimeVal *current_time,
-                                       gpointer  user_data);
-
-static gboolean g_io_win32_pipe_prepare  (gpointer  source_data, 
-                                         GTimeVal *current_time,
-                                         gint     *timeout);
-static gboolean g_io_win32_pipe_check    (gpointer  source_data,
-                                         GTimeVal *current_time);
-static gboolean g_io_win32_pipe_dispatch (gpointer  source_data,
-                                         GTimeVal *current_time,
-                                         gpointer  user_data);
-static void g_io_win32_pipe_destroy     (gpointer source_data);
-
-static gboolean g_io_win32_sock_prepare  (gpointer  source_data, 
-                                         GTimeVal *current_time,
-                                         gint     *timeout);
-static gboolean g_io_win32_sock_check    (gpointer  source_data,
-                                         GTimeVal *current_time);
-static gboolean g_io_win32_sock_dispatch (gpointer  source_data,
-                                         GTimeVal *current_time,
-                                         gpointer  user_data);
-
-static void g_io_win32_destroy (gpointer source_data);
-
-static GIOError g_io_win32_msg_read (GIOChannel *channel, 
-                                    gchar      *buf, 
-                                    guint       count,
-                                    guint      *bytes_written);
-
-static GIOError g_io_win32_msg_write(GIOChannel *channel, 
-                                    gchar      *buf, 
-                                    guint       count,
-                                    guint      *bytes_written);
-static GIOError g_io_win32_msg_seek (GIOChannel *channel,
-                                    gint        offset, 
-                                    GSeekType   type);
-static void g_io_win32_msg_close    (GIOChannel *channel);
-static guint g_io_win32_msg_add_watch (GIOChannel      *channel,
-                                      gint             priority,
-                                      GIOCondition     condition,
-                                      GIOFunc          func,
-                                      gpointer         user_data,
-                                      GDestroyNotify   notify);
-
-static GIOError g_io_win32_fd_read (GIOChannel *channel, 
-                                   gchar      *buf, 
-                                   guint       count,
-                                   guint      *bytes_written);
-static GIOError g_io_win32_fd_write(GIOChannel *channel, 
-                                   gchar      *buf, 
-                                   guint       count,
-                                   guint      *bytes_written);
-static GIOError g_io_win32_fd_seek (GIOChannel *channel,
-                                   gint        offset, 
-                                   GSeekType   type);
-static void g_io_win32_fd_close (GIOChannel *channel);
-
-static void g_io_win32_free (GIOChannel *channel);
-
-static guint g_io_win32_fd_add_watch (GIOChannel      *channel,
-                                     gint             priority,
-                                     GIOCondition     condition,
-                                     GIOFunc          func,
-                                     gpointer         user_data,
-                                     GDestroyNotify   notify);
-
-static GIOError g_io_win32_no_seek (GIOChannel *channel,
-                                   gint        offset, 
-                                   GSeekType   type);
-
-static GIOError g_io_win32_pipe_read (GIOChannel *channel, 
-                                     gchar      *buf, 
-                                     guint       count,
-                                     guint      *bytes_written);
-static GIOError g_io_win32_pipe_write (GIOChannel *channel, 
-                                      gchar      *buf, 
-                                      guint       count,
-                                      guint      *bytes_written);
-static void g_io_win32_pipe_close    (GIOChannel *channel);
-static guint g_io_win32_pipe_add_watch (GIOChannel      *channel,
-                                       gint             priority,
-                                       GIOCondition     condition,
-                                       GIOFunc          func,
-                                       gpointer         user_data,
-                                       GDestroyNotify   notify);
-static void g_io_win32_pipe_free (GIOChannel *channel);
-
-static GIOError g_io_win32_sock_read (GIOChannel *channel, 
-                                     gchar      *buf, 
-                                     guint       count,
-                                     guint      *bytes_written);
-static GIOError g_io_win32_sock_write(GIOChannel *channel, 
-                                     gchar      *buf, 
-                                     guint       count,
-                                     guint      *bytes_written);
-static void g_io_win32_sock_close    (GIOChannel *channel);
-static guint g_io_win32_sock_add_watch (GIOChannel      *channel,
-                                       gint             priority,
-                                       GIOCondition     condition,
-                                       GIOFunc          func,
-                                       gpointer         user_data,
-                                       GDestroyNotify   notify);
-
-GSourceFuncs win32_watch_msg_funcs = {
-  g_io_win32_msg_prepare,
-  g_io_win32_msg_check,
-  g_io_win32_msg_dispatch,
-  g_io_win32_destroy
-};
+static void
+create_events (GIOWin32Channel *channel)
+{
+  SECURITY_ATTRIBUTES sec_attrs;
+  
+  sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+  sec_attrs.lpSecurityDescriptor = NULL;
+  sec_attrs.bInheritHandle = FALSE;
 
-GSourceFuncs win32_watch_fd_funcs = {
-  g_io_win32_fd_prepare,
-  g_io_win32_fd_check,
-  g_io_win32_fd_dispatch,
-  g_io_win32_destroy
-};
+  channel->data_avail_event = NULL;
+  channel->space_avail_event = NULL;
+  
+  /* The data available event is manual reset, the space available event
+   * is automatic reset.
+   */
+  if (!(channel->data_avail_event = CreateEvent (&sec_attrs, TRUE, FALSE, NULL))
+      || !(channel->space_avail_event = CreateEvent (&sec_attrs, FALSE, FALSE, NULL)))
+    {
+      gchar *msg = g_win32_error_message (GetLastError ());
+      g_error ("Error creating event: %s", msg);
+    }
+  InitializeCriticalSection (&channel->mutex);
+}
 
-GSourceFuncs win32_watch_pipe_funcs = {
-  g_io_win32_pipe_prepare,
-  g_io_win32_pipe_check,
-  g_io_win32_pipe_dispatch,
-  g_io_win32_pipe_destroy
-};
+static unsigned __stdcall
+reader_thread (void *parameter)
+{
+  GIOWin32Channel *channel = parameter;
+  guchar *buffer;
+  guint nbytes;
 
-GSourceFuncs win32_watch_sock_funcs = {
-  g_io_win32_sock_prepare,
-  g_io_win32_sock_check,
-  g_io_win32_sock_dispatch,
-  g_io_win32_destroy
-};
+  g_io_channel_ref ((GIOChannel *) channel);
 
-GIOFuncs win32_channel_msg_funcs = {
-  g_io_win32_msg_read,
-  g_io_win32_msg_write,
-  g_io_win32_no_seek,
-  g_io_win32_msg_close,
-  g_io_win32_msg_add_watch,
-  g_io_win32_free
-};
+  PRINT (("thread %#x: starting. pid:%#x, fd:%d, data_avail:%#x, space_avail:%#x\n",
+         channel->thread_id,
+         (guint) GetCurrentProcessId (),
+         channel->fd,
+         (guint) channel->data_avail_event,
+         (guint) channel->space_avail_event));
+  
+  channel->buffer = g_malloc (BUFFER_SIZE);
+  channel->rdp = channel->wrp = 0;
+  channel->running = TRUE;
 
-GIOFuncs win32_channel_fd_funcs = {
-  g_io_win32_fd_read,
-  g_io_win32_fd_write,
-  g_io_win32_fd_seek,
-  g_io_win32_fd_close,
-  g_io_win32_fd_add_watch,
-  g_io_win32_free
-};
+  SetEvent (channel->space_avail_event);
+  
+  while (channel->running)
+    {
+      LOCK (channel->mutex);
+      PRINT (("thread %#x: rdp=%d, wrp=%d\n",
+             channel->thread_id, channel->rdp, channel->wrp));
+      if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
+       {
+         /* Buffer is full */
+         PRINT (("thread %#x: resetting space_available\n",
+                 channel->thread_id));
+         ResetEvent (channel->space_avail_event);
+         PRINT (("thread %#x: waiting for space\n", channel->thread_id));
+         UNLOCK (channel->mutex);
+         WaitForSingleObject (channel->space_avail_event, INFINITE);
+         LOCK (channel->mutex);
+         PRINT (("thread %#x: rdp=%d, wrp=%d\n",
+                 channel->thread_id, channel->rdp, channel->wrp));
+       }
+      
+      buffer = channel->buffer + channel->wrp;
+      
+      /* Always leave at least one byte unused gap to be able to
+       * distinguish between the full and empty condition...
+       */
+      nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
+                   BUFFER_SIZE - channel->wrp);
 
-GIOFuncs win32_channel_pipe_funcs = {
-  g_io_win32_pipe_read,
-  g_io_win32_pipe_write,
-  g_io_win32_no_seek,
-  g_io_win32_pipe_close,
-  g_io_win32_pipe_add_watch,
-  g_io_win32_pipe_free
-};
+      UNLOCK (channel->mutex);
 
-GIOFuncs win32_channel_sock_funcs = {
-  g_io_win32_sock_read,
-  g_io_win32_sock_write,
-  g_io_win32_no_seek,
-  g_io_win32_sock_close,
-  g_io_win32_sock_add_watch,
-  g_io_win32_free
-};
+      nbytes = (*channel->reader) (channel->fd, buffer, nbytes);
+      
+      if (nbytes <= 0)
+       break;
 
-#define N_WATCHED_PIPES 4
+      LOCK (channel->mutex);
+      PRINT (("thread %#x: got %d bytes, rdp=%d, wrp=%d\n",
+             channel->thread_id, nbytes, channel->rdp, channel->wrp));
+      channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE;
+      PRINT (("thread %#x: rdp=%d, wrp=%d, setting data available\n",
+             channel->thread_id, channel->rdp, channel->wrp));
+      SetEvent (channel->data_avail_event);
+      UNLOCK (channel->mutex);
+    }
+  
+  LOCK (channel->mutex);
+  channel->running = FALSE;
+  PRINT (("thread %#x: got EOF, rdp=%d, wrp=%d, setting data available\n",
+         channel->thread_id, channel->rdp, channel->wrp));
+  SetEvent (channel->data_avail_event);
+  UNLOCK (channel->mutex);
+  
+  g_io_channel_unref((GIOChannel *) channel);
+  
+  /* All of the Microsoft docs say we should explicitly
+   * end the thread...
+   */
+  _endthreadex(1);
+  
+  return 0;
+}
 
-static struct {
-  gint fd;
-  GIOWin32Watch *watch;
-  GIOWin32Channel *channel;
-  gpointer user_data;
-} watched_pipes[N_WATCHED_PIPES];
+static void
+create_reader_thread (GIOWin32Channel *channel,
+                     gpointer         reader)
+{
+  channel->reader = reader;
 
-static gint n_watched_pipes = 0;
+  if (_beginthreadex (NULL, 0, reader_thread, channel, 0,
+                     &channel->thread_id) == 0)
+    g_warning ("Error creating reader thread: %s", strerror (errno));
+  WaitForSingleObject (channel->space_avail_event, INFINITE);
+}
 
-static gboolean
-g_io_win32_msg_prepare  (gpointer source_data, 
-                        GTimeVal *current_time,
-                        gint    *timeout)
+static int
+buffer_read (GIOWin32Channel *channel,
+            guchar          *dest,
+            guint            count,
+            GIOError        *error)
 {
-  GIOWin32Watch *data = source_data;
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) data->channel;
-  MSG msg;
+  guint nbytes;
+  guint left = count;
+  
+  LOCK (channel->mutex);
+  PRINT (("reading from thread %#x %d bytes, rdp=%d, wrp=%d\n",
+         channel->thread_id, count, channel->rdp, channel->wrp));
+  
+  while (left)
+    {
+      if (channel->rdp == channel->wrp)
+       {
+         UNLOCK (channel->mutex);
+         PRINT (("waiting for data from thread %#x\n", channel->thread_id));
+         WaitForSingleObject (channel->data_avail_event, INFINITE);
+         LOCK (channel->mutex);
+         if (channel->rdp == channel->wrp && !channel->running)
+           break;
+       }
+      
+      if (channel->rdp < channel->wrp)
+       nbytes = channel->wrp - channel->rdp;
+      else
+       nbytes = BUFFER_SIZE - channel->rdp;
+      UNLOCK (channel->mutex);
+      nbytes = MIN (left, nbytes);
+      PRINT (("moving %d bytes from thread %#x\n",
+             nbytes, channel->thread_id));
+      memcpy (dest, channel->buffer + channel->rdp, nbytes);
+      dest += nbytes;
+      left -= nbytes;
+      LOCK (channel->mutex);
+      channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE;
+      PRINT (("setting space available for thread %#x\n", channel->thread_id));
+      SetEvent (channel->space_avail_event);
+      PRINT (("for thread %#x: rdp=%d, wrp=%d\n",
+             channel->thread_id, channel->rdp, channel->wrp));
+      if (channel->running && channel->rdp == channel->wrp)
+       {
+         PRINT (("resetting data_available of thread %#x\n",
+                 channel->thread_id));
+         ResetEvent (channel->data_avail_event);
+       };
+    }
+  UNLOCK (channel->mutex);
+  
+  return count - left;
+}
 
+static gboolean
+g_io_win32_prepare (gpointer  source_data,
+                   GTimeVal *current_time,
+                   gint     *timeout,
+                   gpointer  user_data)
+{
   *timeout = -1;
-
-  return PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_NOREMOVE) == TRUE;
+  
+  return FALSE;
 }
 
-static gboolean 
-g_io_win32_msg_check    (gpointer source_data,
-                        GTimeVal *current_time)
+static gboolean
+g_io_win32_check (gpointer  source_data,
+                 GTimeVal *current_time,
+                 gpointer  user_data)
 {
   GIOWin32Watch *data = source_data;
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) data->channel;
-  MSG msg;
-
-  return PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_NOREMOVE) == TRUE;
+  GIOWin32Channel *channel = (GIOWin32Channel *) data->channel;
+  
+  /* If the thread has died, we have encountered EOF. If the buffer
+   * also is emtpty set the HUP bit.
+   */
+  if (!channel->running && channel->rdp == channel->wrp)
+    {
+      PRINT (("g_io_win32_check: setting G_IO_HUP thread %#x rdp=%d wrp=%d\n", channel->thread_id, channel->rdp, channel->wrp));
+      data->pollfd.revents |= G_IO_HUP;
+      return TRUE;
+    }
+  
+  return (data->pollfd.revents & data->condition);
 }
 
 static gboolean
-g_io_win32_msg_dispatch (gpointer source_data, 
-                        GTimeVal *current_time,
-                        gpointer user_data)
-
+g_io_win32_dispatch (gpointer  source_data,
+                    GTimeVal *current_time,
+                    gpointer  user_data)
+     
 {
   GIOWin32Watch *data = source_data;
-
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
+  
+  return (*data->callback) (data->channel,
+                           data->pollfd.revents & data->condition,
+                           user_data);
 }
 
 static void
 g_io_win32_destroy (gpointer source_data)
 {
   GIOWin32Watch *data = source_data;
-
+  
   g_main_remove_poll (&data->pollfd);
   g_io_channel_unref (data->channel);
   g_free (data);
 }
 
-static gboolean
-g_io_win32_fd_prepare  (gpointer source_data, 
-                       GTimeVal *current_time,
-                       gint    *timeout)
-{
-  *timeout = -1;
-
-  return FALSE;
-}
-
-static gboolean 
-g_io_win32_fd_check    (gpointer source_data,
-                       GTimeVal *current_time)
-{
-  GIOWin32Watch *data = source_data;
-
-  return (data->pollfd.revents & data->condition);
-}
-
-static gboolean
-g_io_win32_fd_dispatch (gpointer source_data, 
-                       GTimeVal *current_time,
-                       gpointer user_data)
-
-{
-  GIOWin32Watch *data = source_data;
-
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
-}
+static GSourceFuncs win32_watch_funcs = {
+  g_io_win32_prepare,
+  g_io_win32_check,
+  g_io_win32_dispatch,
+  g_io_win32_destroy
+};
 
 static GIOError
-g_io_win32_msg_read (GIOChannel *channel, 
-                    gchar     *buf, 
-                    guint      count,
-                    guint     *bytes_read)
+g_io_win32_msg_read (GIOChannel *channel,
+                    gchar      *buf,
+                    guint       count,
+                    guint      *bytes_read)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  MSG msg;                     /* In case of alignment problems */
-
+  MSG msg;               /* In case of alignment problems */
+  
   if (count < sizeof (MSG))
     return G_IO_ERROR_INVAL;
   
   if (!PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_REMOVE))
     return G_IO_ERROR_AGAIN;
-
+  
   memmove (buf, &msg, sizeof (MSG));
   *bytes_read = sizeof (MSG);
   return G_IO_ERROR_NONE;
 }
-                      
-static GIOError 
-g_io_win32_msg_write(GIOChannel *channel, 
-                    gchar     *buf, 
-                    guint      count,
-                    guint     *bytes_written)
+
+static GIOError
+g_io_win32_msg_write (GIOChannel *channel,
+                     gchar      *buf,
+                     guint       count,
+                     guint      *bytes_written)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   MSG msg;
-  gint result;
-
+  
   if (count != sizeof (MSG))
     return G_IO_ERROR_INVAL;
-
+  
   /* In case of alignment problems */
   memmove (&msg, buf, sizeof (MSG));
   if (!PostMessage (win32_channel->hwnd, msg.message, msg.wParam, msg.lParam))
     return G_IO_ERROR_UNKNOWN;
-
+  
   *bytes_written = sizeof (MSG);
-  return G_IO_ERROR_NONE; 
+  return G_IO_ERROR_NONE;
 }
 
-static GIOError 
+static GIOError
 g_io_win32_no_seek (GIOChannel *channel,
-                   gint      offset, 
-                   GSeekType type)
+                   gint        offset,
+                   GSeekType   type)
 {
-  g_warning ("g_io_win32_no_seek: unseekable IO channel type");
   return G_IO_ERROR_UNKNOWN;
 }
 
 
-static void 
+static void
 g_io_win32_msg_close (GIOChannel *channel)
 {
   /* Nothing to be done. Or should we set hwnd to some invalid value? */
 }
 
-static void 
+static void
 g_io_win32_free (GIOChannel *channel)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+  
+  if (win32_channel->buffer)
+    {
+      CloseHandle (win32_channel->data_avail_event);
+      CloseHandle (win32_channel->space_avail_event);
+      DeleteCriticalSection (&win32_channel->mutex);
+    }
 
+  g_free (win32_channel->buffer);
   g_free (win32_channel);
 }
 
-static guint 
+static guint
 g_io_win32_msg_add_watch (GIOChannel    *channel,
                          gint           priority,
                          GIOCondition   condition,
@@ -426,117 +431,56 @@ g_io_win32_msg_add_watch (GIOChannel    *channel,
                          GDestroyNotify notify)
 {
   GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   
   watch->channel = channel;
   g_io_channel_ref (channel);
-
+  
   watch->callback = func;
   watch->condition = condition;
-
+  
   watch->pollfd.fd = G_WIN32_MSG_HANDLE;
   watch->pollfd.events = condition;
-
+  
   g_main_add_poll (&watch->pollfd, priority);
-
-  return g_source_add (priority, TRUE, &win32_watch_msg_funcs,
+  
+  return g_source_add (priority, TRUE, &win32_watch_funcs,
                       watch, user_data, notify);
 }
 
-static gboolean
-g_io_win32_pipe_prepare  (gpointer source_data, 
-                         GTimeVal *current_time,
-                         gint    *timeout)
-{
-  *timeout = -1;
-
-  return FALSE;
-}
-
-static gboolean 
-g_io_win32_pipe_check    (gpointer source_data,
-                         GTimeVal *current_time)
-{
-  GIOWin32Watch *data = source_data;
-  return FALSE;
-}
-
-static gboolean
-g_io_win32_pipe_dispatch (gpointer source_data, 
-                         GTimeVal *current_time,
-                         gpointer user_data)
-
-{
-  GIOWin32Watch *data = source_data;
-
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
-}
-
-static void
-g_io_win32_pipe_destroy (gpointer source_data)
-{
-  GIOWin32Watch *data = source_data;
-
-  g_io_channel_unref (data->channel);
-  g_free (data);
-}
-
-static gboolean
-g_io_win32_sock_prepare  (gpointer source_data, 
-                       GTimeVal *current_time,
-                       gint    *timeout)
-{
-  *timeout = -1;
-
-  return FALSE;
-}
-
-static gboolean 
-g_io_win32_sock_check    (gpointer source_data,
-                       GTimeVal *current_time)
-{
-  GIOWin32Watch *data = source_data;
-
-  return (data->pollfd.revents & data->condition);
-}
-
-static gboolean
-g_io_win32_sock_dispatch (gpointer source_data, 
-                       GTimeVal *current_time,
-                       gpointer user_data)
-
-{
-  GIOWin32Watch *data = source_data;
-
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
-}
-
 static GIOError
-g_io_win32_fd_read (GIOChannel *channel, 
-                   gchar     *buf, 
+g_io_win32_fd_read (GIOChannel *channel,
+                   gchar     *buf,
                    guint      count,
                    guint     *bytes_read)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   gint result;
+  GIOError error;
+  
+  if (win32_channel->thread_id)
+    {
+      result = buffer_read (win32_channel, buf, count, &error);
+      if (result < 0)
+       {
+         *bytes_read = 0;
+         return error;
+       }
+      else
+       {
+         *bytes_read = result;
+         return G_IO_ERROR_NONE;
+       }
+    }
 
   result = read (win32_channel->fd, buf, count);
+
   if (result < 0)
     {
       *bytes_read = 0;
-      switch (errno)
-       {
-       case EINVAL:
-         return G_IO_ERROR_INVAL;
-       case EAGAIN:
-         return G_IO_ERROR_AGAIN;
-       default:
-         return G_IO_ERROR_UNKNOWN;
-       }
+      if (errno == EINVAL)
+       return G_IO_ERROR_INVAL;
+      else
+       return G_IO_ERROR_UNKNOWN;
     }
   else
     {
@@ -544,18 +488,19 @@ g_io_win32_fd_read (GIOChannel *channel,
       return G_IO_ERROR_NONE;
     }
 }
-                      
-static GIOError 
-g_io_win32_fd_write(GIOChannel *channel, 
-                   gchar     *buf, 
+
+static GIOError
+g_io_win32_fd_write(GIOChannel *channel,
+                   gchar     *buf,
                    guint      count,
                    guint     *bytes_written)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   gint result;
-
+  
   result = write (win32_channel->fd, buf, count);
-      
+  PRINT (("g_io_win32_fd_write: fd:%d count:%d = %d\n", win32_channel->fd, count, result));
+  
   if (result < 0)
     {
       *bytes_written = 0;
@@ -576,15 +521,15 @@ g_io_win32_fd_write(GIOChannel *channel,
     }
 }
 
-static GIOError 
+static GIOError
 g_io_win32_fd_seek (GIOChannel *channel,
-                   gint      offset, 
+                   gint      offset,
                    GSeekType type)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   int whence;
   off_t result;
-
+  
   switch (type)
     {
     case G_SEEK_SET:
@@ -617,16 +562,30 @@ g_io_win32_fd_seek (GIOChannel *channel,
     return G_IO_ERROR_NONE;
 }
 
-static void 
+static void
 g_io_win32_fd_close (GIOChannel *channel)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-
+  
   close (win32_channel->fd);
   return;
 }
 
-static guint 
+static int
+fd_reader (int     fd,
+          guchar *buf,
+          int     len)
+{
+  int value;
+  
+  value = read (fd, buf, len);
+  
+  PRINT (("fd_reader (%d,%p,%d) = %d\n", fd, buf, len, value));
+  
+  return value;
+}
+
+static guint
 g_io_win32_fd_add_watch (GIOChannel    *channel,
                         gint           priority,
                         GIOCondition   condition,
@@ -636,206 +595,65 @@ g_io_win32_fd_add_watch (GIOChannel    *channel,
 {
   GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+  DWORD nbytes;
+  char dummy[1];
   
   watch->channel = channel;
   g_io_channel_ref (channel);
-
+  
   watch->callback = func;
   watch->condition = condition;
+  
+  create_events (win32_channel);
 
-  /* This probably does not work, except for CONIN$. */
-  watch->pollfd.fd = _get_osfhandle (win32_channel->fd);
+  watch->pollfd.fd = (gint) win32_channel->data_avail_event;
   watch->pollfd.events = condition;
+  
+  PRINT (("g_io_win32_fd_add_watch: fd:%d handle:%#x\n",
+         win32_channel->fd, watch->pollfd.fd));
+  
+  /* Is it readable? (Would be strange to watch it otherwise, but... */
+  if (ReadFile ((HANDLE) _get_osfhandle (win32_channel->fd),
+               dummy, 0, &nbytes, NULL))
+    create_reader_thread (win32_channel, fd_reader);
 
   g_main_add_poll (&watch->pollfd, priority);
-
-  return g_source_add (priority, TRUE, &win32_watch_fd_funcs,
+  
+  return g_source_add (priority, TRUE, &win32_watch_funcs,
                       watch, user_data, notify);
 }
 
 static GIOError
-g_io_win32_pipe_read (GIOChannel *channel, 
-                     gchar     *buf, 
-                     guint      count,
-                     guint     *bytes_read)
+g_io_win32_sock_read (GIOChannel *channel,
+                     gchar      *buf,
+                     guint       count,
+                     guint      *bytes_read)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  HANDLE handle;
-  DWORD avail;
   gint result;
-
-  handle = (HANDLE) _get_osfhandle (win32_channel->fd);
-  if (!PeekNamedPipe (handle, NULL, 0, NULL, &avail, NULL))
-    {
-      return G_IO_ERROR_UNKNOWN;
-    }
-
-  count = MIN (count, avail);
-
-  count = MAX (count, 1);      /* Must read at least one byte, or
-                                * caller will think it's EOF.
-                                */
-  /* g_print ("g_io_win32_pipe_read: %d %d\n", win32_channel->fd, count); */
-  if (count == 0)
-    result = 0;
-  else
-    result = read (win32_channel->fd, buf, count);
-  if (result < 0)
+  GIOError error;
+  
+  if (win32_channel->thread_id)
     {
-      *bytes_read = 0;
-      switch (errno)
+      result = buffer_read (win32_channel, buf, count, &error);
+      if (result < 0)
        {
-       case EINVAL:
-         return G_IO_ERROR_INVAL;
-       case EAGAIN:
-         return G_IO_ERROR_AGAIN;
-       default:
-         return G_IO_ERROR_UNKNOWN;
+         *bytes_read = 0;
+         return error;
        }
-    }
-  else
-    {
-      *bytes_read = result;
-      win32_channel->offset += result;
-      /* g_print ("=%d (%d)\n", result, win32_channel->offset); */
-      return G_IO_ERROR_NONE;
-    }
-}
-                      
-static GIOError 
-g_io_win32_pipe_write(GIOChannel *channel, 
-                     gchar     *buf, 
-                     guint      count,
-                     guint     *bytes_written)
-{
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  LONG prevcnt;
-  gint result;
-
-  /* g_print ("g_io_win32_pipe_write: %d %d\n", win32_channel->fd, count); */
-  result = write (win32_channel->fd, buf, count);
-  if (result < 0)
-    {
-      *bytes_written = 0;
-      switch (errno)
+      else
        {
-       case EINVAL:
-         return G_IO_ERROR_INVAL;
-       case EAGAIN:
-         return G_IO_ERROR_AGAIN;
-       default:
-         return G_IO_ERROR_UNKNOWN;
-       }
-    }
-  else
-    {
-      if (g_pipe_readable_msg == 0)
-       g_pipe_readable_msg = RegisterWindowMessage ("g-pipe-readable");
-
-      win32_channel->offset += result;
-      /* g_print ("=%d (%d)\n", result, win32_channel->offset); */
-      if (win32_channel->need_wakeups)
-       {
-         PostThreadMessage (win32_channel->peer,
-                            g_pipe_readable_msg,
-                            win32_channel->peer_fd,
-                            win32_channel->offset);
+         *bytes_read = result;
+         return G_IO_ERROR_NONE;
        }
-      *bytes_written = result;
-      return G_IO_ERROR_NONE;
-    }
-}
-
-static void 
-g_io_win32_pipe_close (GIOChannel *channel)
-{
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-
-  /* g_print ("g_io_win32_pipe_close: %#x %d\n", channel, win32_channel->fd); */
-
-  close (win32_channel->fd);
-  return;
-}
-
-static guint 
-g_io_win32_pipe_add_watch (GIOChannel    *channel,
-                          gint           priority,
-                          GIOCondition   condition,
-                          GIOFunc        func,
-                          gpointer       user_data,
-                          GDestroyNotify notify)
-{
-  GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  gint i;
-  
-  /* g_print ("g_io_win32_pipe_add_watch: %d\n", win32_channel->fd); */
-
-  watch->channel = channel;
-  g_io_channel_ref (channel);
-
-  watch->callback = func;
-  watch->condition = condition;
-
-  watch->pollfd.fd = win32_channel->fd;
-  watch->pollfd.events = condition;
-
-  for (i = 0; i < n_watched_pipes; i++)
-    if (watched_pipes[i].fd == -1)
-      break;
-  if (i == N_WATCHED_PIPES)
-    g_error ("Too many watched pipes");
-  else
-    {
-      watched_pipes[i].fd = win32_channel->fd;
-      watched_pipes[i].watch = watch;
-      watched_pipes[i].channel = win32_channel;
-      watched_pipes[i].user_data = user_data;
-      n_watched_pipes = MAX (i + 1, n_watched_pipes);
     }
-  return g_source_add (priority, FALSE, &win32_watch_pipe_funcs, watch, user_data, notify);
-}
-
-static void
-g_io_win32_pipe_free (GIOChannel *channel)
-{
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  gint i;
-
-  /* g_print ("g_io_win32_pipe_free: %#x %#x\n", channel, channel->channel_data); */
-
-  for (i = 0; i < n_watched_pipes; i++)
-    if (watched_pipes[i].fd == win32_channel->fd)
-      {
-       watched_pipes[i].fd = -1;
-       break;
-      }
-  g_io_win32_free (channel);
-}
-
-static GIOError 
-g_io_win32_sock_read (GIOChannel *channel, 
-                     gchar      *buf, 
-                     guint       count,
-                     guint      *bytes_read)
-{
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  gint result;
 
   result = recv (win32_channel->fd, buf, count, 0);
-  if (result == SOCKET_ERROR)
+
+  if (result < 0)
     {
       *bytes_read = 0;
-      switch (WSAGetLastError ())
-       {
-       case WSAEINVAL:
-         return G_IO_ERROR_INVAL;
-       case WSAEWOULDBLOCK:
-       case WSAEINTR:
-         return G_IO_ERROR_AGAIN;
-       default:
-         return G_IO_ERROR_UNKNOWN;
-       }
+      return G_IO_ERROR_UNKNOWN;
     }
   else
     {
@@ -843,18 +661,18 @@ g_io_win32_sock_read (GIOChannel *channel,
       return G_IO_ERROR_NONE;
     }
 }
-                      
-static GIOError 
-g_io_win32_sock_write(GIOChannel *channel, 
-                     gchar      *buf, 
+
+static GIOError
+g_io_win32_sock_write(GIOChannel *channel,
+                     gchar      *buf,
                      guint       count,
                      guint      *bytes_written)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   gint result;
-
+  
   result = send (win32_channel->fd, buf, count, 0);
-      
+  
   if (result == SOCKET_ERROR)
     {
       *bytes_written = 0;
@@ -876,16 +694,23 @@ g_io_win32_sock_write(GIOChannel *channel,
     }
 }
 
-static void 
+static void
 g_io_win32_sock_close (GIOChannel *channel)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
 
   closesocket (win32_channel->fd);
-  return;
 }
 
-static guint 
+static int
+sock_reader (int     fd,
+            guchar *buf,
+            int     len)
+{
+  return recv (fd, buf, len, 0);
+}
+
+static guint
 g_io_win32_sock_add_watch (GIOChannel    *channel,
                           gint           priority,
                           GIOCondition   condition,
@@ -895,21 +720,54 @@ g_io_win32_sock_add_watch (GIOChannel    *channel,
 {
   GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  
+
   watch->channel = channel;
   g_io_channel_ref (channel);
 
   watch->callback = func;
   watch->condition = condition;
 
-  watch->pollfd.fd = win32_channel->fd;
+  create_events (win32_channel);
+
+  watch->pollfd.fd = (gint) win32_channel->data_avail_event;
   watch->pollfd.events = condition;
 
+  /* Sockets are always readable, aren't they? */
+  create_reader_thread (win32_channel, sock_reader);
+
   g_main_add_poll (&watch->pollfd, priority);
 
-  return g_source_add (priority, TRUE, &win32_watch_sock_funcs, watch, user_data, notify);
+  return g_source_add (priority, TRUE, &win32_watch_funcs, watch,
+                      user_data, notify);
 }
 
+static GIOFuncs win32_channel_msg_funcs = {
+  g_io_win32_msg_read,
+  g_io_win32_msg_write,
+  g_io_win32_no_seek,
+  g_io_win32_msg_close,
+  g_io_win32_msg_add_watch,
+  g_io_win32_free
+};
+
+static GIOFuncs win32_channel_fd_funcs = {
+  g_io_win32_fd_read,
+  g_io_win32_fd_write,
+  g_io_win32_fd_seek,
+  g_io_win32_fd_close,
+  g_io_win32_fd_add_watch,
+  g_io_win32_free
+};
+
+static GIOFuncs win32_channel_sock_funcs = {
+  g_io_win32_sock_read,
+  g_io_win32_sock_write,
+  g_io_win32_no_seek,
+  g_io_win32_sock_close,
+  g_io_win32_sock_add_watch,
+  g_io_win32_free
+};
+
 GIOChannel *
 g_io_channel_win32_new_messages (guint hwnd)
 {
@@ -918,7 +776,6 @@ g_io_channel_win32_new_messages (guint hwnd)
 
   g_io_channel_init (channel);
   channel->funcs = &win32_channel_msg_funcs;
-  win32_channel->fd = -1;
   win32_channel->type = G_IO_WINDOWS_MESSAGES;
   win32_channel->hwnd = (HWND) hwnd;
 
@@ -926,21 +783,37 @@ g_io_channel_win32_new_messages (guint hwnd)
 }
 
 GIOChannel *
-g_io_channel_unix_new (gint fd)
+g_io_channel_win32_new_fd (gint fd)
 {
-  GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
-  GIOChannel *channel = (GIOChannel *) win32_channel;
+  GIOWin32Channel *win32_channel;
+  GIOChannel *channel;
+  struct stat st;
+
+  if (fstat (fd, &st) == -1)
+    {
+      g_warning ("%d isn't a (emulated) file descriptor", fd);
+      return NULL;
+    }
+
+  PRINT (("g_io_channel_win32_new_fd: %d\n", fd));
+
+  win32_channel = g_new (GIOWin32Channel, 1);
+  channel = (GIOChannel *) win32_channel;
 
   g_io_channel_init (channel);
+
   channel->funcs = &win32_channel_fd_funcs;
   win32_channel->fd = fd;
   win32_channel->type = G_IO_FILE_DESC;
+  win32_channel->buffer = NULL;
+  win32_channel->running = FALSE;
+  win32_channel->thread_id = 0;
 
   return channel;
 }
 
 gint
-g_io_channel_unix_get_fd (GIOChannel *channel)
+g_io_channel_win32_get_fd (GIOChannel *channel)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
 
@@ -948,88 +821,92 @@ g_io_channel_unix_get_fd (GIOChannel *channel)
 }
 
 GIOChannel *
-g_io_channel_win32_new_pipe_with_wakeups (int   fd,
-                                         guint peer,
-                                         int   peer_fd)
+g_io_channel_win32_new_stream_socket (int socket)
 {
   GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
   GIOChannel *channel = (GIOChannel *) win32_channel;
 
-  /* g_print ("g_io_channel_win32_new_pipe_with_wakeups %d %#x %d\n", fd, peer, peer_fd); */
-
   g_io_channel_init (channel);
-  channel->funcs = &win32_channel_pipe_funcs;
-  win32_channel->fd = fd;
-  win32_channel->type = G_IO_PIPE;
-  win32_channel->peer = peer;
-  win32_channel->peer_fd = peer_fd;
-  win32_channel->offset = 0;
-  win32_channel->need_wakeups = TRUE;
+  channel->funcs = &win32_channel_sock_funcs;
+  win32_channel->fd = socket;
+  win32_channel->type = G_IO_STREAM_SOCKET;
+  win32_channel->buffer = NULL;
+  win32_channel->running = FALSE;
+  win32_channel->thread_id = 0;
 
   return channel;
 }
 
 GIOChannel *
-g_io_channel_win32_new_pipe (int fd)
+g_io_channel_unix_new (gint fd)
 {
-  GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
-  GIOChannel *channel = (GIOChannel *) win32_channel;
-
-  g_io_channel_init (channel);
-  channel->funcs = &win32_channel_pipe_funcs;
-  win32_channel->fd = fd;
-  win32_channel->type = G_IO_PIPE;
-  win32_channel->offset = 0;
-  win32_channel->need_wakeups = FALSE;
+  return g_io_channel_win32_new_fd (fd);
+}
 
-  return channel;
+gint
+g_io_channel_unix_get_fd (GIOChannel *channel)
+{
+  return g_io_channel_win32_get_fd (channel);
 }
 
-GIOChannel *
-g_io_channel_win32_new_stream_socket (int socket)
+gint
+g_io_channel_win32_wait_for_condition (GIOChannel  *channel,
+                                      GIOCondition condition,
+                                      gint         timeout)
 {
-  GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
-  GIOChannel *channel = (GIOChannel *) win32_channel;
+  GPollFD pollfd;
+  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+  int result;
 
-  g_io_channel_init (channel);
-  channel->funcs = &win32_channel_sock_funcs;
-  win32_channel->fd = socket;
-  win32_channel->type = G_IO_STREAM_SOCKET;
+  pollfd.fd = (gint) win32_channel->data_avail_event;
+  pollfd.events = condition;
 
-  return channel;
+  PRINT (("g_io_channel_win32_wait_for_condition: fd:%d event:%#x timeout:%d\n",
+         win32_channel->fd, pollfd.fd, timeout));
+
+  result = (*g_main_win32_get_poll_func ()) (&pollfd, 1, timeout);
+
+  PRINT (("g_io_channel_win32_wait_for_condition: done:%d\n", result));
+
+  return result;
 }
 
-gint
-g_io_channel_win32_get_fd (GIOChannel *channel)
+
+/* This variable and the functions below are present just to be 
+ * binary compatible with old clients... But note that in GIMP, the
+ * libgimp/gimp.c:gimp_extension_process() function will have to be modified
+ * anyhow for this new approach.
+ *
+ * These will be removed after some weeks.
+ */
+guint g_pipe_readable_msg = 0;
+
+GIOChannel *
+g_io_channel_win32_new_pipe (int fd)
+{
+  return g_io_channel_win32_new_fd (fd);
+}
+
+GIOChannel *
+g_io_channel_win32_new_pipe_with_wakeups (int   fd,
+                           guint peer,
+                           int   peer_fd)
 {
-  return g_io_channel_unix_get_fd (channel);
+  return g_io_channel_win32_new_fd (fd);
 }
 
 void
 g_io_channel_win32_pipe_request_wakeups (GIOChannel *channel,
-                                        guint       peer,
-                                        int         peer_fd)
+                          guint       peer,
+                          int         peer_fd)
 {
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-
-  win32_channel->peer = peer;
-  win32_channel->peer_fd = peer_fd;
-  win32_channel->need_wakeups = TRUE;
+  /* Nothing needed now */
 }
 
 void
 g_io_channel_win32_pipe_readable (gint  fd,
-                                 guint offset)
+                      guint offset)
 {
-  gint i;
-
-  for (i = 0; i < n_watched_pipes; i++)
-    if (watched_pipes[i].fd == fd)
-      {
-       if ((watched_pipes[i].watch->condition & G_IO_IN)
-           && watched_pipes[i].channel->offset < offset)
-         (*watched_pipes[i].watch->callback) (watched_pipes[i].watch->channel,
-                                              G_IO_IN,
-                                              watched_pipes[i].user_data);
-      }
+  /* Nothing needed now */
 }
+
index e7fdb71..9e1b56c 100644 (file)
--- a/glib.def
+++ b/glib.def
@@ -160,11 +160,13 @@ EXPORTS
        g_io_channel_unref
        g_io_channel_win32_get_fd
        g_io_channel_win32_new_messages
+       g_io_channel_win32_new_fd
        g_io_channel_win32_new_pipe
        g_io_channel_win32_new_pipe_with_wakeups
        g_io_channel_win32_new_stream_socket
        g_io_channel_win32_pipe_readable
        g_io_channel_win32_pipe_request_wakeups
+       g_io_channel_win32_wait_for_condition
        g_io_channel_write
        g_list_alloc
        g_list_append
@@ -207,6 +209,7 @@ EXPORTS
        g_main_remove_poll
        g_main_run
        g_main_set_poll_func
+       g_main_win32_get_poll_func
        g_malloc
        g_malloc0
        g_mem_check
diff --git a/glib.h b/glib.h
index 3100f52..4ebe662 100644 (file)
--- a/glib.h
+++ b/glib.h
@@ -2881,29 +2881,43 @@ void        g_main_add_poll          (GPollFD    *fd,
 void        g_main_remove_poll       (GPollFD    *fd);
 void        g_main_set_poll_func     (GPollFunc   func);
 
+#ifdef G_OS_WIN32
+
+/* Useful on other platforms, too? */
+
+GPollFunc   g_main_win32_get_poll_func (void);
+
+#endif
+
 /* On Unix, IO channels created with this function for any file
  * descriptor or socket.
  *
- * On Win32, use this only for plain files opened with the MSVCRT (the
- * Microsoft run-time C library) _open(), including file descriptors
- * 0, 1 and 2 (corresponding to stdin, stdout and stderr).
- * Actually, don't do even that, this code isn't done yet.
+ * On Win32, use this only for files opened with the MSVCRT (the
+ * Microsoft run-time C library) _open() or _pipe, including file
+ * descriptors 0, 1 and 2 (corresponding to stdin, stdout and stderr).
  *
  * The term file descriptor as used in the context of Win32 refers to
- * the emulated Unix-like file descriptors MSVCRT provides.
+ * the emulated Unix-like file descriptors MSVCRT provides. The native
+ * corresponding concept is file HANDLE. There isn't as of yet a way to
+ * get GIOChannels for file HANDLEs.
  */
 GIOChannel* g_io_channel_unix_new    (int         fd);
 gint        g_io_channel_unix_get_fd (GIOChannel *channel);
 
 #ifdef G_OS_WIN32
 
-GLIB_VAR guint g_pipe_readable_msg;
-
 #define G_WIN32_MSG_HANDLE 19981206
 
+/* This can be used to wait until a channel is readable.  On Unix you
+ * would do a select() on the fd of the channel. This should probably
+ * be replaced by something for all platforms?
+ */
+gint        g_io_channel_win32_wait_for_condition (GIOChannel  *channel,
+                                                  GIOCondition condition,
+                                                  gint         timeout);
+
 /* This is used to add polling for Windows messages. GDK (GTk+) programs
- * should *not* use this. (In fact, I can't think of any program that
- * would want to use this, but it's here just for completeness's sake.
+ * should *not* use this.
  */
 void        g_main_poll_win32_msg_add(gint        priority,
                                      GPollFD    *fd,
@@ -2912,40 +2926,16 @@ void        g_main_poll_win32_msg_add(gint        priority,
 /* An IO channel for Windows messages for window handle hwnd. */
 GIOChannel *g_io_channel_win32_new_messages (guint hwnd);
 
-/* An IO channel for an anonymous pipe as returned from the MSVCRT
- * _pipe(), with no mechanism for the writer to tell the reader when
- * there is data in the pipe.
- *
- * This is not really implemented yet.
- */
-GIOChannel *g_io_channel_win32_new_pipe (int fd);
-
-/* An IO channel for a pipe as returned from the MSVCRT _pipe(), with
- * Windows user messages used to signal data in the pipe for the
- * reader.
- *
- * fd is the file descriptor. For the write end, peer is the thread id
- * of the reader, and peer_fd is his file descriptor for the read end
- * of the pipe.
- *
- * This is used by the GIMP, and works.
+/* An IO channel for C runtime (emulated Unix-like) file
+ * descriptors. Identical to g_io_channel_unix_new above.
  */
-GIOChannel *g_io_channel_win32_new_pipe_with_wakeups (int   fd,
-                                                     guint peer,
-                                                     int   peer_fd);
-
-void        g_io_channel_win32_pipe_request_wakeups (GIOChannel *channel,
-                                                    guint       peer,
-                                                    int         peer_fd);
-
-void        g_io_channel_win32_pipe_readable (int   fd,
-                                             guint offset);
+GIOChannel* g_io_channel_win32_new_fd (int         fd);
 
 /* Get the C runtime file descriptor of a channel. */
 gint        g_io_channel_win32_get_fd (GIOChannel *channel);
 
-/* An IO channel for a SOCK_STREAM winsock socket. The parameter is
- * actually a SOCKET.
+/* An IO channel for a SOCK_STREAM winsock socket. The parameter should
+ * be a SOCKET.
  */
 GIOChannel *g_io_channel_win32_new_stream_socket (int socket);
 
index 36fa82c..c06df4a 100644 (file)
@@ -3,6 +3,7 @@
  *
  * giowin32.c: IO Channels for Win32.
  * Copyright 1998 Owen Taylor and Tor Lillqvist
+ * Copyright 1999-2000 Tor Lillqvist and Craig Setera
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -11,7 +12,7 @@
  *
  * 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
+ * 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
  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
  * file for a list of people on the GLib Team.  See the ChangeLog
  * files for a list of changes.  These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
  */
 
+/* Define this to get (very) verbose logging */
+/* #define G_IO_WIN32_DEBUG */
 
-#include "config.h"
 #include "glib.h"
+
+#include <stdlib.h>
 #include <windows.h>
-#include <winsock.h>           /* Not everybody has winsock2 */
+#include <winsock.h>          /* Not everybody has winsock2 */
 #include <fcntl.h>
 #include <io.h>
+#include <process.h>
 #include <errno.h>
-#include <sys/types.h>
-
-#include <stdio.h>
+#include <sys/stat.h>
 
 typedef struct _GIOWin32Channel GIOWin32Channel;
 typedef struct _GIOWin32Watch GIOWin32Watch;
 
-guint g_pipe_readable_msg;
+#define BUFFER_SIZE 4096
 
 typedef enum {
   G_IO_WINDOWS_MESSAGES,       /* Windows messages */
-  G_IO_FILE_DESC,              /* Unix-like file descriptors from _open*/
-  G_IO_PIPE,                   /* pipe, with windows messages for signalling */
-  G_IO_STREAM_SOCKET           /* Stream sockets */
+  G_IO_FILE_DESC,              /* Unix-like file descriptors from
+                                * _open() or _pipe(). Read with read().
+                                * Have to create separate thread to read.
+                                */
+  G_IO_STREAM_SOCKET           /* Stream sockets. Similar as fds, but
+                                * read with recv().
+                                */
 } GIOWin32ChannelType;
 
 struct _GIOWin32Channel {
@@ -58,19 +65,49 @@ struct _GIOWin32Channel {
                                 * as provided by WinSock.
                                 */
   GIOWin32ChannelType type;
-
+  
   /* This is used by G_IO_WINDOWS_MESSAGES channels */
   HWND hwnd;                   /* handle of window, or NULL */
-
-  /* This is used by G_IO_PIPE channels */
-  guint peer;                  /* thread id of reader */
-  guint peer_fd;               /* fd in the reader */
-  guint offset;                        /* counter of accumulated bytes */
-  guint need_wakeups;          /* in output channels whether the
-                                * reader needs wakeups
+  
+  /* Following fields used by fd and socket channels for input */
+  
+  /* Data is kept in a circular buffer. To be able to distinguish between
+   * empty and full buffer, we cannot fill it completely, but have to
+   * leave a one character gap.
+   *
+   * Data available is between indexes rdp and wrp-1 (modulo BUFFER_SIZE).
+   *
+   * Empty:    wrp == rdp
+   * Full:     (wrp + 1) % BUFFER_SIZE == rdp
+   * Partial:  otherwise
+   */
+  guchar *buffer;              /* (Circular) buffer */
+  gint wrp, rdp;               /* Buffer indices for writing and reading */
+  gboolean running;            /* Is reader thread running. FALSE if
+                                * EOF has been reached.
                                 */
+  guint thread_id;             /* If non-NULL has a reader thread, or has
+                                * had.*/
+  HANDLE data_avail_event;
+  HANDLE space_avail_event;
+  CRITICAL_SECTION mutex;
+  
+  /* Function that actually reads from fd */
+  int (*reader) (int fd, guchar *buf, int len);
 };
 
+#define LOCK(mutex) EnterCriticalSection (&mutex)
+#define UNLOCK(mutex) LeaveCriticalSection (&mutex)
+
+/* Temporarilyu change a PRINT to PRINT_ to get just *that* message */
+#define PRINT_(x) g_print x
+
+#ifdef G_IO_WIN32_DEBUG
+#define PRINT(x) PRINT_(x)
+#else
+#define PRINT(x)
+#endif
+
 struct _GIOWin32Watch {
   GPollFD       pollfd;
   GIOChannel   *channel;
@@ -78,346 +115,314 @@ struct _GIOWin32Watch {
   GIOFunc       callback;
 };
 
-static gboolean g_io_win32_msg_prepare  (gpointer  source_data, 
-                                        GTimeVal *current_time,
-                                        gint     *timeout);
-static gboolean g_io_win32_msg_check    (gpointer  source_data,
-                                        GTimeVal *current_time);
-static gboolean g_io_win32_msg_dispatch (gpointer  source_data,
-                                        GTimeVal *current_time,
-                                        gpointer  user_data);
-
-static gboolean g_io_win32_fd_prepare  (gpointer  source_data, 
-                                       GTimeVal *current_time,
-                                       gint     *timeout);
-static gboolean g_io_win32_fd_check    (gpointer  source_data,
-                                       GTimeVal *current_time);
-static gboolean g_io_win32_fd_dispatch (gpointer  source_data,
-                                       GTimeVal *current_time,
-                                       gpointer  user_data);
-
-static gboolean g_io_win32_pipe_prepare  (gpointer  source_data, 
-                                         GTimeVal *current_time,
-                                         gint     *timeout);
-static gboolean g_io_win32_pipe_check    (gpointer  source_data,
-                                         GTimeVal *current_time);
-static gboolean g_io_win32_pipe_dispatch (gpointer  source_data,
-                                         GTimeVal *current_time,
-                                         gpointer  user_data);
-static void g_io_win32_pipe_destroy     (gpointer source_data);
-
-static gboolean g_io_win32_sock_prepare  (gpointer  source_data, 
-                                         GTimeVal *current_time,
-                                         gint     *timeout);
-static gboolean g_io_win32_sock_check    (gpointer  source_data,
-                                         GTimeVal *current_time);
-static gboolean g_io_win32_sock_dispatch (gpointer  source_data,
-                                         GTimeVal *current_time,
-                                         gpointer  user_data);
-
-static void g_io_win32_destroy (gpointer source_data);
-
-static GIOError g_io_win32_msg_read (GIOChannel *channel, 
-                                    gchar      *buf, 
-                                    guint       count,
-                                    guint      *bytes_written);
-
-static GIOError g_io_win32_msg_write(GIOChannel *channel, 
-                                    gchar      *buf, 
-                                    guint       count,
-                                    guint      *bytes_written);
-static GIOError g_io_win32_msg_seek (GIOChannel *channel,
-                                    gint        offset, 
-                                    GSeekType   type);
-static void g_io_win32_msg_close    (GIOChannel *channel);
-static guint g_io_win32_msg_add_watch (GIOChannel      *channel,
-                                      gint             priority,
-                                      GIOCondition     condition,
-                                      GIOFunc          func,
-                                      gpointer         user_data,
-                                      GDestroyNotify   notify);
-
-static GIOError g_io_win32_fd_read (GIOChannel *channel, 
-                                   gchar      *buf, 
-                                   guint       count,
-                                   guint      *bytes_written);
-static GIOError g_io_win32_fd_write(GIOChannel *channel, 
-                                   gchar      *buf, 
-                                   guint       count,
-                                   guint      *bytes_written);
-static GIOError g_io_win32_fd_seek (GIOChannel *channel,
-                                   gint        offset, 
-                                   GSeekType   type);
-static void g_io_win32_fd_close (GIOChannel *channel);
-
-static void g_io_win32_free (GIOChannel *channel);
-
-static guint g_io_win32_fd_add_watch (GIOChannel      *channel,
-                                     gint             priority,
-                                     GIOCondition     condition,
-                                     GIOFunc          func,
-                                     gpointer         user_data,
-                                     GDestroyNotify   notify);
-
-static GIOError g_io_win32_no_seek (GIOChannel *channel,
-                                   gint        offset, 
-                                   GSeekType   type);
-
-static GIOError g_io_win32_pipe_read (GIOChannel *channel, 
-                                     gchar      *buf, 
-                                     guint       count,
-                                     guint      *bytes_written);
-static GIOError g_io_win32_pipe_write (GIOChannel *channel, 
-                                      gchar      *buf, 
-                                      guint       count,
-                                      guint      *bytes_written);
-static void g_io_win32_pipe_close    (GIOChannel *channel);
-static guint g_io_win32_pipe_add_watch (GIOChannel      *channel,
-                                       gint             priority,
-                                       GIOCondition     condition,
-                                       GIOFunc          func,
-                                       gpointer         user_data,
-                                       GDestroyNotify   notify);
-static void g_io_win32_pipe_free (GIOChannel *channel);
-
-static GIOError g_io_win32_sock_read (GIOChannel *channel, 
-                                     gchar      *buf, 
-                                     guint       count,
-                                     guint      *bytes_written);
-static GIOError g_io_win32_sock_write(GIOChannel *channel, 
-                                     gchar      *buf, 
-                                     guint       count,
-                                     guint      *bytes_written);
-static void g_io_win32_sock_close    (GIOChannel *channel);
-static guint g_io_win32_sock_add_watch (GIOChannel      *channel,
-                                       gint             priority,
-                                       GIOCondition     condition,
-                                       GIOFunc          func,
-                                       gpointer         user_data,
-                                       GDestroyNotify   notify);
-
-GSourceFuncs win32_watch_msg_funcs = {
-  g_io_win32_msg_prepare,
-  g_io_win32_msg_check,
-  g_io_win32_msg_dispatch,
-  g_io_win32_destroy
-};
+static void
+create_events (GIOWin32Channel *channel)
+{
+  SECURITY_ATTRIBUTES sec_attrs;
+  
+  sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+  sec_attrs.lpSecurityDescriptor = NULL;
+  sec_attrs.bInheritHandle = FALSE;
 
-GSourceFuncs win32_watch_fd_funcs = {
-  g_io_win32_fd_prepare,
-  g_io_win32_fd_check,
-  g_io_win32_fd_dispatch,
-  g_io_win32_destroy
-};
+  channel->data_avail_event = NULL;
+  channel->space_avail_event = NULL;
+  
+  /* The data available event is manual reset, the space available event
+   * is automatic reset.
+   */
+  if (!(channel->data_avail_event = CreateEvent (&sec_attrs, TRUE, FALSE, NULL))
+      || !(channel->space_avail_event = CreateEvent (&sec_attrs, FALSE, FALSE, NULL)))
+    {
+      gchar *msg = g_win32_error_message (GetLastError ());
+      g_error ("Error creating event: %s", msg);
+    }
+  InitializeCriticalSection (&channel->mutex);
+}
 
-GSourceFuncs win32_watch_pipe_funcs = {
-  g_io_win32_pipe_prepare,
-  g_io_win32_pipe_check,
-  g_io_win32_pipe_dispatch,
-  g_io_win32_pipe_destroy
-};
+static unsigned __stdcall
+reader_thread (void *parameter)
+{
+  GIOWin32Channel *channel = parameter;
+  guchar *buffer;
+  guint nbytes;
 
-GSourceFuncs win32_watch_sock_funcs = {
-  g_io_win32_sock_prepare,
-  g_io_win32_sock_check,
-  g_io_win32_sock_dispatch,
-  g_io_win32_destroy
-};
+  g_io_channel_ref ((GIOChannel *) channel);
 
-GIOFuncs win32_channel_msg_funcs = {
-  g_io_win32_msg_read,
-  g_io_win32_msg_write,
-  g_io_win32_no_seek,
-  g_io_win32_msg_close,
-  g_io_win32_msg_add_watch,
-  g_io_win32_free
-};
+  PRINT (("thread %#x: starting. pid:%#x, fd:%d, data_avail:%#x, space_avail:%#x\n",
+         channel->thread_id,
+         (guint) GetCurrentProcessId (),
+         channel->fd,
+         (guint) channel->data_avail_event,
+         (guint) channel->space_avail_event));
+  
+  channel->buffer = g_malloc (BUFFER_SIZE);
+  channel->rdp = channel->wrp = 0;
+  channel->running = TRUE;
 
-GIOFuncs win32_channel_fd_funcs = {
-  g_io_win32_fd_read,
-  g_io_win32_fd_write,
-  g_io_win32_fd_seek,
-  g_io_win32_fd_close,
-  g_io_win32_fd_add_watch,
-  g_io_win32_free
-};
+  SetEvent (channel->space_avail_event);
+  
+  while (channel->running)
+    {
+      LOCK (channel->mutex);
+      PRINT (("thread %#x: rdp=%d, wrp=%d\n",
+             channel->thread_id, channel->rdp, channel->wrp));
+      if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
+       {
+         /* Buffer is full */
+         PRINT (("thread %#x: resetting space_available\n",
+                 channel->thread_id));
+         ResetEvent (channel->space_avail_event);
+         PRINT (("thread %#x: waiting for space\n", channel->thread_id));
+         UNLOCK (channel->mutex);
+         WaitForSingleObject (channel->space_avail_event, INFINITE);
+         LOCK (channel->mutex);
+         PRINT (("thread %#x: rdp=%d, wrp=%d\n",
+                 channel->thread_id, channel->rdp, channel->wrp));
+       }
+      
+      buffer = channel->buffer + channel->wrp;
+      
+      /* Always leave at least one byte unused gap to be able to
+       * distinguish between the full and empty condition...
+       */
+      nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
+                   BUFFER_SIZE - channel->wrp);
 
-GIOFuncs win32_channel_pipe_funcs = {
-  g_io_win32_pipe_read,
-  g_io_win32_pipe_write,
-  g_io_win32_no_seek,
-  g_io_win32_pipe_close,
-  g_io_win32_pipe_add_watch,
-  g_io_win32_pipe_free
-};
+      UNLOCK (channel->mutex);
 
-GIOFuncs win32_channel_sock_funcs = {
-  g_io_win32_sock_read,
-  g_io_win32_sock_write,
-  g_io_win32_no_seek,
-  g_io_win32_sock_close,
-  g_io_win32_sock_add_watch,
-  g_io_win32_free
-};
+      nbytes = (*channel->reader) (channel->fd, buffer, nbytes);
+      
+      if (nbytes <= 0)
+       break;
 
-#define N_WATCHED_PIPES 4
+      LOCK (channel->mutex);
+      PRINT (("thread %#x: got %d bytes, rdp=%d, wrp=%d\n",
+             channel->thread_id, nbytes, channel->rdp, channel->wrp));
+      channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE;
+      PRINT (("thread %#x: rdp=%d, wrp=%d, setting data available\n",
+             channel->thread_id, channel->rdp, channel->wrp));
+      SetEvent (channel->data_avail_event);
+      UNLOCK (channel->mutex);
+    }
+  
+  LOCK (channel->mutex);
+  channel->running = FALSE;
+  PRINT (("thread %#x: got EOF, rdp=%d, wrp=%d, setting data available\n",
+         channel->thread_id, channel->rdp, channel->wrp));
+  SetEvent (channel->data_avail_event);
+  UNLOCK (channel->mutex);
+  
+  g_io_channel_unref((GIOChannel *) channel);
+  
+  /* All of the Microsoft docs say we should explicitly
+   * end the thread...
+   */
+  _endthreadex(1);
+  
+  return 0;
+}
 
-static struct {
-  gint fd;
-  GIOWin32Watch *watch;
-  GIOWin32Channel *channel;
-  gpointer user_data;
-} watched_pipes[N_WATCHED_PIPES];
+static void
+create_reader_thread (GIOWin32Channel *channel,
+                     gpointer         reader)
+{
+  channel->reader = reader;
 
-static gint n_watched_pipes = 0;
+  if (_beginthreadex (NULL, 0, reader_thread, channel, 0,
+                     &channel->thread_id) == 0)
+    g_warning ("Error creating reader thread: %s", strerror (errno));
+  WaitForSingleObject (channel->space_avail_event, INFINITE);
+}
 
-static gboolean
-g_io_win32_msg_prepare  (gpointer source_data, 
-                        GTimeVal *current_time,
-                        gint    *timeout)
+static int
+buffer_read (GIOWin32Channel *channel,
+            guchar          *dest,
+            guint            count,
+            GIOError        *error)
 {
-  GIOWin32Watch *data = source_data;
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) data->channel;
-  MSG msg;
+  guint nbytes;
+  guint left = count;
+  
+  LOCK (channel->mutex);
+  PRINT (("reading from thread %#x %d bytes, rdp=%d, wrp=%d\n",
+         channel->thread_id, count, channel->rdp, channel->wrp));
+  
+  while (left)
+    {
+      if (channel->rdp == channel->wrp)
+       {
+         UNLOCK (channel->mutex);
+         PRINT (("waiting for data from thread %#x\n", channel->thread_id));
+         WaitForSingleObject (channel->data_avail_event, INFINITE);
+         LOCK (channel->mutex);
+         if (channel->rdp == channel->wrp && !channel->running)
+           break;
+       }
+      
+      if (channel->rdp < channel->wrp)
+       nbytes = channel->wrp - channel->rdp;
+      else
+       nbytes = BUFFER_SIZE - channel->rdp;
+      UNLOCK (channel->mutex);
+      nbytes = MIN (left, nbytes);
+      PRINT (("moving %d bytes from thread %#x\n",
+             nbytes, channel->thread_id));
+      memcpy (dest, channel->buffer + channel->rdp, nbytes);
+      dest += nbytes;
+      left -= nbytes;
+      LOCK (channel->mutex);
+      channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE;
+      PRINT (("setting space available for thread %#x\n", channel->thread_id));
+      SetEvent (channel->space_avail_event);
+      PRINT (("for thread %#x: rdp=%d, wrp=%d\n",
+             channel->thread_id, channel->rdp, channel->wrp));
+      if (channel->running && channel->rdp == channel->wrp)
+       {
+         PRINT (("resetting data_available of thread %#x\n",
+                 channel->thread_id));
+         ResetEvent (channel->data_avail_event);
+       };
+    }
+  UNLOCK (channel->mutex);
+  
+  return count - left;
+}
 
+static gboolean
+g_io_win32_prepare (gpointer  source_data,
+                   GTimeVal *current_time,
+                   gint     *timeout,
+                   gpointer  user_data)
+{
   *timeout = -1;
-
-  return PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_NOREMOVE) == TRUE;
+  
+  return FALSE;
 }
 
-static gboolean 
-g_io_win32_msg_check    (gpointer source_data,
-                        GTimeVal *current_time)
+static gboolean
+g_io_win32_check (gpointer  source_data,
+                 GTimeVal *current_time,
+                 gpointer  user_data)
 {
   GIOWin32Watch *data = source_data;
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) data->channel;
-  MSG msg;
-
-  return PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_NOREMOVE) == TRUE;
+  GIOWin32Channel *channel = (GIOWin32Channel *) data->channel;
+  
+  /* If the thread has died, we have encountered EOF. If the buffer
+   * also is emtpty set the HUP bit.
+   */
+  if (!channel->running && channel->rdp == channel->wrp)
+    {
+      PRINT (("g_io_win32_check: setting G_IO_HUP thread %#x rdp=%d wrp=%d\n", channel->thread_id, channel->rdp, channel->wrp));
+      data->pollfd.revents |= G_IO_HUP;
+      return TRUE;
+    }
+  
+  return (data->pollfd.revents & data->condition);
 }
 
 static gboolean
-g_io_win32_msg_dispatch (gpointer source_data, 
-                        GTimeVal *current_time,
-                        gpointer user_data)
-
+g_io_win32_dispatch (gpointer  source_data,
+                    GTimeVal *current_time,
+                    gpointer  user_data)
+     
 {
   GIOWin32Watch *data = source_data;
-
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
+  
+  return (*data->callback) (data->channel,
+                           data->pollfd.revents & data->condition,
+                           user_data);
 }
 
 static void
 g_io_win32_destroy (gpointer source_data)
 {
   GIOWin32Watch *data = source_data;
-
+  
   g_main_remove_poll (&data->pollfd);
   g_io_channel_unref (data->channel);
   g_free (data);
 }
 
-static gboolean
-g_io_win32_fd_prepare  (gpointer source_data, 
-                       GTimeVal *current_time,
-                       gint    *timeout)
-{
-  *timeout = -1;
-
-  return FALSE;
-}
-
-static gboolean 
-g_io_win32_fd_check    (gpointer source_data,
-                       GTimeVal *current_time)
-{
-  GIOWin32Watch *data = source_data;
-
-  return (data->pollfd.revents & data->condition);
-}
-
-static gboolean
-g_io_win32_fd_dispatch (gpointer source_data, 
-                       GTimeVal *current_time,
-                       gpointer user_data)
-
-{
-  GIOWin32Watch *data = source_data;
-
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
-}
+static GSourceFuncs win32_watch_funcs = {
+  g_io_win32_prepare,
+  g_io_win32_check,
+  g_io_win32_dispatch,
+  g_io_win32_destroy
+};
 
 static GIOError
-g_io_win32_msg_read (GIOChannel *channel, 
-                    gchar     *buf, 
-                    guint      count,
-                    guint     *bytes_read)
+g_io_win32_msg_read (GIOChannel *channel,
+                    gchar      *buf,
+                    guint       count,
+                    guint      *bytes_read)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  MSG msg;                     /* In case of alignment problems */
-
+  MSG msg;               /* In case of alignment problems */
+  
   if (count < sizeof (MSG))
     return G_IO_ERROR_INVAL;
   
   if (!PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_REMOVE))
     return G_IO_ERROR_AGAIN;
-
+  
   memmove (buf, &msg, sizeof (MSG));
   *bytes_read = sizeof (MSG);
   return G_IO_ERROR_NONE;
 }
-                      
-static GIOError 
-g_io_win32_msg_write(GIOChannel *channel, 
-                    gchar     *buf, 
-                    guint      count,
-                    guint     *bytes_written)
+
+static GIOError
+g_io_win32_msg_write (GIOChannel *channel,
+                     gchar      *buf,
+                     guint       count,
+                     guint      *bytes_written)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   MSG msg;
-  gint result;
-
+  
   if (count != sizeof (MSG))
     return G_IO_ERROR_INVAL;
-
+  
   /* In case of alignment problems */
   memmove (&msg, buf, sizeof (MSG));
   if (!PostMessage (win32_channel->hwnd, msg.message, msg.wParam, msg.lParam))
     return G_IO_ERROR_UNKNOWN;
-
+  
   *bytes_written = sizeof (MSG);
-  return G_IO_ERROR_NONE; 
+  return G_IO_ERROR_NONE;
 }
 
-static GIOError 
+static GIOError
 g_io_win32_no_seek (GIOChannel *channel,
-                   gint      offset, 
-                   GSeekType type)
+                   gint        offset,
+                   GSeekType   type)
 {
-  g_warning ("g_io_win32_no_seek: unseekable IO channel type");
   return G_IO_ERROR_UNKNOWN;
 }
 
 
-static void 
+static void
 g_io_win32_msg_close (GIOChannel *channel)
 {
   /* Nothing to be done. Or should we set hwnd to some invalid value? */
 }
 
-static void 
+static void
 g_io_win32_free (GIOChannel *channel)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+  
+  if (win32_channel->buffer)
+    {
+      CloseHandle (win32_channel->data_avail_event);
+      CloseHandle (win32_channel->space_avail_event);
+      DeleteCriticalSection (&win32_channel->mutex);
+    }
 
+  g_free (win32_channel->buffer);
   g_free (win32_channel);
 }
 
-static guint 
+static guint
 g_io_win32_msg_add_watch (GIOChannel    *channel,
                          gint           priority,
                          GIOCondition   condition,
@@ -426,117 +431,56 @@ g_io_win32_msg_add_watch (GIOChannel    *channel,
                          GDestroyNotify notify)
 {
   GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   
   watch->channel = channel;
   g_io_channel_ref (channel);
-
+  
   watch->callback = func;
   watch->condition = condition;
-
+  
   watch->pollfd.fd = G_WIN32_MSG_HANDLE;
   watch->pollfd.events = condition;
-
+  
   g_main_add_poll (&watch->pollfd, priority);
-
-  return g_source_add (priority, TRUE, &win32_watch_msg_funcs,
+  
+  return g_source_add (priority, TRUE, &win32_watch_funcs,
                       watch, user_data, notify);
 }
 
-static gboolean
-g_io_win32_pipe_prepare  (gpointer source_data, 
-                         GTimeVal *current_time,
-                         gint    *timeout)
-{
-  *timeout = -1;
-
-  return FALSE;
-}
-
-static gboolean 
-g_io_win32_pipe_check    (gpointer source_data,
-                         GTimeVal *current_time)
-{
-  GIOWin32Watch *data = source_data;
-  return FALSE;
-}
-
-static gboolean
-g_io_win32_pipe_dispatch (gpointer source_data, 
-                         GTimeVal *current_time,
-                         gpointer user_data)
-
-{
-  GIOWin32Watch *data = source_data;
-
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
-}
-
-static void
-g_io_win32_pipe_destroy (gpointer source_data)
-{
-  GIOWin32Watch *data = source_data;
-
-  g_io_channel_unref (data->channel);
-  g_free (data);
-}
-
-static gboolean
-g_io_win32_sock_prepare  (gpointer source_data, 
-                       GTimeVal *current_time,
-                       gint    *timeout)
-{
-  *timeout = -1;
-
-  return FALSE;
-}
-
-static gboolean 
-g_io_win32_sock_check    (gpointer source_data,
-                       GTimeVal *current_time)
-{
-  GIOWin32Watch *data = source_data;
-
-  return (data->pollfd.revents & data->condition);
-}
-
-static gboolean
-g_io_win32_sock_dispatch (gpointer source_data, 
-                       GTimeVal *current_time,
-                       gpointer user_data)
-
-{
-  GIOWin32Watch *data = source_data;
-
-  return (*data->callback)(data->channel,
-                          data->pollfd.revents & data->condition,
-                          user_data);
-}
-
 static GIOError
-g_io_win32_fd_read (GIOChannel *channel, 
-                   gchar     *buf, 
+g_io_win32_fd_read (GIOChannel *channel,
+                   gchar     *buf,
                    guint      count,
                    guint     *bytes_read)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   gint result;
+  GIOError error;
+  
+  if (win32_channel->thread_id)
+    {
+      result = buffer_read (win32_channel, buf, count, &error);
+      if (result < 0)
+       {
+         *bytes_read = 0;
+         return error;
+       }
+      else
+       {
+         *bytes_read = result;
+         return G_IO_ERROR_NONE;
+       }
+    }
 
   result = read (win32_channel->fd, buf, count);
+
   if (result < 0)
     {
       *bytes_read = 0;
-      switch (errno)
-       {
-       case EINVAL:
-         return G_IO_ERROR_INVAL;
-       case EAGAIN:
-         return G_IO_ERROR_AGAIN;
-       default:
-         return G_IO_ERROR_UNKNOWN;
-       }
+      if (errno == EINVAL)
+       return G_IO_ERROR_INVAL;
+      else
+       return G_IO_ERROR_UNKNOWN;
     }
   else
     {
@@ -544,18 +488,19 @@ g_io_win32_fd_read (GIOChannel *channel,
       return G_IO_ERROR_NONE;
     }
 }
-                      
-static GIOError 
-g_io_win32_fd_write(GIOChannel *channel, 
-                   gchar     *buf, 
+
+static GIOError
+g_io_win32_fd_write(GIOChannel *channel,
+                   gchar     *buf,
                    guint      count,
                    guint     *bytes_written)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   gint result;
-
+  
   result = write (win32_channel->fd, buf, count);
-      
+  PRINT (("g_io_win32_fd_write: fd:%d count:%d = %d\n", win32_channel->fd, count, result));
+  
   if (result < 0)
     {
       *bytes_written = 0;
@@ -576,15 +521,15 @@ g_io_win32_fd_write(GIOChannel *channel,
     }
 }
 
-static GIOError 
+static GIOError
 g_io_win32_fd_seek (GIOChannel *channel,
-                   gint      offset, 
+                   gint      offset,
                    GSeekType type)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   int whence;
   off_t result;
-
+  
   switch (type)
     {
     case G_SEEK_SET:
@@ -617,16 +562,30 @@ g_io_win32_fd_seek (GIOChannel *channel,
     return G_IO_ERROR_NONE;
 }
 
-static void 
+static void
 g_io_win32_fd_close (GIOChannel *channel)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-
+  
   close (win32_channel->fd);
   return;
 }
 
-static guint 
+static int
+fd_reader (int     fd,
+          guchar *buf,
+          int     len)
+{
+  int value;
+  
+  value = read (fd, buf, len);
+  
+  PRINT (("fd_reader (%d,%p,%d) = %d\n", fd, buf, len, value));
+  
+  return value;
+}
+
+static guint
 g_io_win32_fd_add_watch (GIOChannel    *channel,
                         gint           priority,
                         GIOCondition   condition,
@@ -636,206 +595,65 @@ g_io_win32_fd_add_watch (GIOChannel    *channel,
 {
   GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+  DWORD nbytes;
+  char dummy[1];
   
   watch->channel = channel;
   g_io_channel_ref (channel);
-
+  
   watch->callback = func;
   watch->condition = condition;
+  
+  create_events (win32_channel);
 
-  /* This probably does not work, except for CONIN$. */
-  watch->pollfd.fd = _get_osfhandle (win32_channel->fd);
+  watch->pollfd.fd = (gint) win32_channel->data_avail_event;
   watch->pollfd.events = condition;
+  
+  PRINT (("g_io_win32_fd_add_watch: fd:%d handle:%#x\n",
+         win32_channel->fd, watch->pollfd.fd));
+  
+  /* Is it readable? (Would be strange to watch it otherwise, but... */
+  if (ReadFile ((HANDLE) _get_osfhandle (win32_channel->fd),
+               dummy, 0, &nbytes, NULL))
+    create_reader_thread (win32_channel, fd_reader);
 
   g_main_add_poll (&watch->pollfd, priority);
-
-  return g_source_add (priority, TRUE, &win32_watch_fd_funcs,
+  
+  return g_source_add (priority, TRUE, &win32_watch_funcs,
                       watch, user_data, notify);
 }
 
 static GIOError
-g_io_win32_pipe_read (GIOChannel *channel, 
-                     gchar     *buf, 
-                     guint      count,
-                     guint     *bytes_read)
+g_io_win32_sock_read (GIOChannel *channel,
+                     gchar      *buf,
+                     guint       count,
+                     guint      *bytes_read)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  HANDLE handle;
-  DWORD avail;
   gint result;
-
-  handle = (HANDLE) _get_osfhandle (win32_channel->fd);
-  if (!PeekNamedPipe (handle, NULL, 0, NULL, &avail, NULL))
-    {
-      return G_IO_ERROR_UNKNOWN;
-    }
-
-  count = MIN (count, avail);
-
-  count = MAX (count, 1);      /* Must read at least one byte, or
-                                * caller will think it's EOF.
-                                */
-  /* g_print ("g_io_win32_pipe_read: %d %d\n", win32_channel->fd, count); */
-  if (count == 0)
-    result = 0;
-  else
-    result = read (win32_channel->fd, buf, count);
-  if (result < 0)
+  GIOError error;
+  
+  if (win32_channel->thread_id)
     {
-      *bytes_read = 0;
-      switch (errno)
+      result = buffer_read (win32_channel, buf, count, &error);
+      if (result < 0)
        {
-       case EINVAL:
-         return G_IO_ERROR_INVAL;
-       case EAGAIN:
-         return G_IO_ERROR_AGAIN;
-       default:
-         return G_IO_ERROR_UNKNOWN;
+         *bytes_read = 0;
+         return error;
        }
-    }
-  else
-    {
-      *bytes_read = result;
-      win32_channel->offset += result;
-      /* g_print ("=%d (%d)\n", result, win32_channel->offset); */
-      return G_IO_ERROR_NONE;
-    }
-}
-                      
-static GIOError 
-g_io_win32_pipe_write(GIOChannel *channel, 
-                     gchar     *buf, 
-                     guint      count,
-                     guint     *bytes_written)
-{
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  LONG prevcnt;
-  gint result;
-
-  /* g_print ("g_io_win32_pipe_write: %d %d\n", win32_channel->fd, count); */
-  result = write (win32_channel->fd, buf, count);
-  if (result < 0)
-    {
-      *bytes_written = 0;
-      switch (errno)
+      else
        {
-       case EINVAL:
-         return G_IO_ERROR_INVAL;
-       case EAGAIN:
-         return G_IO_ERROR_AGAIN;
-       default:
-         return G_IO_ERROR_UNKNOWN;
-       }
-    }
-  else
-    {
-      if (g_pipe_readable_msg == 0)
-       g_pipe_readable_msg = RegisterWindowMessage ("g-pipe-readable");
-
-      win32_channel->offset += result;
-      /* g_print ("=%d (%d)\n", result, win32_channel->offset); */
-      if (win32_channel->need_wakeups)
-       {
-         PostThreadMessage (win32_channel->peer,
-                            g_pipe_readable_msg,
-                            win32_channel->peer_fd,
-                            win32_channel->offset);
+         *bytes_read = result;
+         return G_IO_ERROR_NONE;
        }
-      *bytes_written = result;
-      return G_IO_ERROR_NONE;
-    }
-}
-
-static void 
-g_io_win32_pipe_close (GIOChannel *channel)
-{
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-
-  /* g_print ("g_io_win32_pipe_close: %#x %d\n", channel, win32_channel->fd); */
-
-  close (win32_channel->fd);
-  return;
-}
-
-static guint 
-g_io_win32_pipe_add_watch (GIOChannel    *channel,
-                          gint           priority,
-                          GIOCondition   condition,
-                          GIOFunc        func,
-                          gpointer       user_data,
-                          GDestroyNotify notify)
-{
-  GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  gint i;
-  
-  /* g_print ("g_io_win32_pipe_add_watch: %d\n", win32_channel->fd); */
-
-  watch->channel = channel;
-  g_io_channel_ref (channel);
-
-  watch->callback = func;
-  watch->condition = condition;
-
-  watch->pollfd.fd = win32_channel->fd;
-  watch->pollfd.events = condition;
-
-  for (i = 0; i < n_watched_pipes; i++)
-    if (watched_pipes[i].fd == -1)
-      break;
-  if (i == N_WATCHED_PIPES)
-    g_error ("Too many watched pipes");
-  else
-    {
-      watched_pipes[i].fd = win32_channel->fd;
-      watched_pipes[i].watch = watch;
-      watched_pipes[i].channel = win32_channel;
-      watched_pipes[i].user_data = user_data;
-      n_watched_pipes = MAX (i + 1, n_watched_pipes);
     }
-  return g_source_add (priority, FALSE, &win32_watch_pipe_funcs, watch, user_data, notify);
-}
-
-static void
-g_io_win32_pipe_free (GIOChannel *channel)
-{
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  gint i;
-
-  /* g_print ("g_io_win32_pipe_free: %#x %#x\n", channel, channel->channel_data); */
-
-  for (i = 0; i < n_watched_pipes; i++)
-    if (watched_pipes[i].fd == win32_channel->fd)
-      {
-       watched_pipes[i].fd = -1;
-       break;
-      }
-  g_io_win32_free (channel);
-}
-
-static GIOError 
-g_io_win32_sock_read (GIOChannel *channel, 
-                     gchar      *buf, 
-                     guint       count,
-                     guint      *bytes_read)
-{
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  gint result;
 
   result = recv (win32_channel->fd, buf, count, 0);
-  if (result == SOCKET_ERROR)
+
+  if (result < 0)
     {
       *bytes_read = 0;
-      switch (WSAGetLastError ())
-       {
-       case WSAEINVAL:
-         return G_IO_ERROR_INVAL;
-       case WSAEWOULDBLOCK:
-       case WSAEINTR:
-         return G_IO_ERROR_AGAIN;
-       default:
-         return G_IO_ERROR_UNKNOWN;
-       }
+      return G_IO_ERROR_UNKNOWN;
     }
   else
     {
@@ -843,18 +661,18 @@ g_io_win32_sock_read (GIOChannel *channel,
       return G_IO_ERROR_NONE;
     }
 }
-                      
-static GIOError 
-g_io_win32_sock_write(GIOChannel *channel, 
-                     gchar      *buf, 
+
+static GIOError
+g_io_win32_sock_write(GIOChannel *channel,
+                     gchar      *buf,
                      guint       count,
                      guint      *bytes_written)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
   gint result;
-
+  
   result = send (win32_channel->fd, buf, count, 0);
-      
+  
   if (result == SOCKET_ERROR)
     {
       *bytes_written = 0;
@@ -876,16 +694,23 @@ g_io_win32_sock_write(GIOChannel *channel,
     }
 }
 
-static void 
+static void
 g_io_win32_sock_close (GIOChannel *channel)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
 
   closesocket (win32_channel->fd);
-  return;
 }
 
-static guint 
+static int
+sock_reader (int     fd,
+            guchar *buf,
+            int     len)
+{
+  return recv (fd, buf, len, 0);
+}
+
+static guint
 g_io_win32_sock_add_watch (GIOChannel    *channel,
                           gint           priority,
                           GIOCondition   condition,
@@ -895,21 +720,54 @@ g_io_win32_sock_add_watch (GIOChannel    *channel,
 {
   GIOWin32Watch *watch = g_new (GIOWin32Watch, 1);
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-  
+
   watch->channel = channel;
   g_io_channel_ref (channel);
 
   watch->callback = func;
   watch->condition = condition;
 
-  watch->pollfd.fd = win32_channel->fd;
+  create_events (win32_channel);
+
+  watch->pollfd.fd = (gint) win32_channel->data_avail_event;
   watch->pollfd.events = condition;
 
+  /* Sockets are always readable, aren't they? */
+  create_reader_thread (win32_channel, sock_reader);
+
   g_main_add_poll (&watch->pollfd, priority);
 
-  return g_source_add (priority, TRUE, &win32_watch_sock_funcs, watch, user_data, notify);
+  return g_source_add (priority, TRUE, &win32_watch_funcs, watch,
+                      user_data, notify);
 }
 
+static GIOFuncs win32_channel_msg_funcs = {
+  g_io_win32_msg_read,
+  g_io_win32_msg_write,
+  g_io_win32_no_seek,
+  g_io_win32_msg_close,
+  g_io_win32_msg_add_watch,
+  g_io_win32_free
+};
+
+static GIOFuncs win32_channel_fd_funcs = {
+  g_io_win32_fd_read,
+  g_io_win32_fd_write,
+  g_io_win32_fd_seek,
+  g_io_win32_fd_close,
+  g_io_win32_fd_add_watch,
+  g_io_win32_free
+};
+
+static GIOFuncs win32_channel_sock_funcs = {
+  g_io_win32_sock_read,
+  g_io_win32_sock_write,
+  g_io_win32_no_seek,
+  g_io_win32_sock_close,
+  g_io_win32_sock_add_watch,
+  g_io_win32_free
+};
+
 GIOChannel *
 g_io_channel_win32_new_messages (guint hwnd)
 {
@@ -918,7 +776,6 @@ g_io_channel_win32_new_messages (guint hwnd)
 
   g_io_channel_init (channel);
   channel->funcs = &win32_channel_msg_funcs;
-  win32_channel->fd = -1;
   win32_channel->type = G_IO_WINDOWS_MESSAGES;
   win32_channel->hwnd = (HWND) hwnd;
 
@@ -926,21 +783,37 @@ g_io_channel_win32_new_messages (guint hwnd)
 }
 
 GIOChannel *
-g_io_channel_unix_new (gint fd)
+g_io_channel_win32_new_fd (gint fd)
 {
-  GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
-  GIOChannel *channel = (GIOChannel *) win32_channel;
+  GIOWin32Channel *win32_channel;
+  GIOChannel *channel;
+  struct stat st;
+
+  if (fstat (fd, &st) == -1)
+    {
+      g_warning ("%d isn't a (emulated) file descriptor", fd);
+      return NULL;
+    }
+
+  PRINT (("g_io_channel_win32_new_fd: %d\n", fd));
+
+  win32_channel = g_new (GIOWin32Channel, 1);
+  channel = (GIOChannel *) win32_channel;
 
   g_io_channel_init (channel);
+
   channel->funcs = &win32_channel_fd_funcs;
   win32_channel->fd = fd;
   win32_channel->type = G_IO_FILE_DESC;
+  win32_channel->buffer = NULL;
+  win32_channel->running = FALSE;
+  win32_channel->thread_id = 0;
 
   return channel;
 }
 
 gint
-g_io_channel_unix_get_fd (GIOChannel *channel)
+g_io_channel_win32_get_fd (GIOChannel *channel)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
 
@@ -948,88 +821,92 @@ g_io_channel_unix_get_fd (GIOChannel *channel)
 }
 
 GIOChannel *
-g_io_channel_win32_new_pipe_with_wakeups (int   fd,
-                                         guint peer,
-                                         int   peer_fd)
+g_io_channel_win32_new_stream_socket (int socket)
 {
   GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
   GIOChannel *channel = (GIOChannel *) win32_channel;
 
-  /* g_print ("g_io_channel_win32_new_pipe_with_wakeups %d %#x %d\n", fd, peer, peer_fd); */
-
   g_io_channel_init (channel);
-  channel->funcs = &win32_channel_pipe_funcs;
-  win32_channel->fd = fd;
-  win32_channel->type = G_IO_PIPE;
-  win32_channel->peer = peer;
-  win32_channel->peer_fd = peer_fd;
-  win32_channel->offset = 0;
-  win32_channel->need_wakeups = TRUE;
+  channel->funcs = &win32_channel_sock_funcs;
+  win32_channel->fd = socket;
+  win32_channel->type = G_IO_STREAM_SOCKET;
+  win32_channel->buffer = NULL;
+  win32_channel->running = FALSE;
+  win32_channel->thread_id = 0;
 
   return channel;
 }
 
 GIOChannel *
-g_io_channel_win32_new_pipe (int fd)
+g_io_channel_unix_new (gint fd)
 {
-  GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
-  GIOChannel *channel = (GIOChannel *) win32_channel;
-
-  g_io_channel_init (channel);
-  channel->funcs = &win32_channel_pipe_funcs;
-  win32_channel->fd = fd;
-  win32_channel->type = G_IO_PIPE;
-  win32_channel->offset = 0;
-  win32_channel->need_wakeups = FALSE;
+  return g_io_channel_win32_new_fd (fd);
+}
 
-  return channel;
+gint
+g_io_channel_unix_get_fd (GIOChannel *channel)
+{
+  return g_io_channel_win32_get_fd (channel);
 }
 
-GIOChannel *
-g_io_channel_win32_new_stream_socket (int socket)
+gint
+g_io_channel_win32_wait_for_condition (GIOChannel  *channel,
+                                      GIOCondition condition,
+                                      gint         timeout)
 {
-  GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
-  GIOChannel *channel = (GIOChannel *) win32_channel;
+  GPollFD pollfd;
+  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+  int result;
 
-  g_io_channel_init (channel);
-  channel->funcs = &win32_channel_sock_funcs;
-  win32_channel->fd = socket;
-  win32_channel->type = G_IO_STREAM_SOCKET;
+  pollfd.fd = (gint) win32_channel->data_avail_event;
+  pollfd.events = condition;
 
-  return channel;
+  PRINT (("g_io_channel_win32_wait_for_condition: fd:%d event:%#x timeout:%d\n",
+         win32_channel->fd, pollfd.fd, timeout));
+
+  result = (*g_main_win32_get_poll_func ()) (&pollfd, 1, timeout);
+
+  PRINT (("g_io_channel_win32_wait_for_condition: done:%d\n", result));
+
+  return result;
 }
 
-gint
-g_io_channel_win32_get_fd (GIOChannel *channel)
+
+/* This variable and the functions below are present just to be 
+ * binary compatible with old clients... But note that in GIMP, the
+ * libgimp/gimp.c:gimp_extension_process() function will have to be modified
+ * anyhow for this new approach.
+ *
+ * These will be removed after some weeks.
+ */
+guint g_pipe_readable_msg = 0;
+
+GIOChannel *
+g_io_channel_win32_new_pipe (int fd)
+{
+  return g_io_channel_win32_new_fd (fd);
+}
+
+GIOChannel *
+g_io_channel_win32_new_pipe_with_wakeups (int   fd,
+                           guint peer,
+                           int   peer_fd)
 {
-  return g_io_channel_unix_get_fd (channel);
+  return g_io_channel_win32_new_fd (fd);
 }
 
 void
 g_io_channel_win32_pipe_request_wakeups (GIOChannel *channel,
-                                        guint       peer,
-                                        int         peer_fd)
+                          guint       peer,
+                          int         peer_fd)
 {
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-
-  win32_channel->peer = peer;
-  win32_channel->peer_fd = peer_fd;
-  win32_channel->need_wakeups = TRUE;
+  /* Nothing needed now */
 }
 
 void
 g_io_channel_win32_pipe_readable (gint  fd,
-                                 guint offset)
+                      guint offset)
 {
-  gint i;
-
-  for (i = 0; i < n_watched_pipes; i++)
-    if (watched_pipes[i].fd == fd)
-      {
-       if ((watched_pipes[i].watch->condition & G_IO_IN)
-           && watched_pipes[i].channel->offset < offset)
-         (*watched_pipes[i].watch->callback) (watched_pipes[i].watch->channel,
-                                              G_IO_IN,
-                                              watched_pipes[i].user_data);
-      }
+  /* Nothing needed now */
 }
+
index e7fdb71..9e1b56c 100644 (file)
@@ -160,11 +160,13 @@ EXPORTS
        g_io_channel_unref
        g_io_channel_win32_get_fd
        g_io_channel_win32_new_messages
+       g_io_channel_win32_new_fd
        g_io_channel_win32_new_pipe
        g_io_channel_win32_new_pipe_with_wakeups
        g_io_channel_win32_new_stream_socket
        g_io_channel_win32_pipe_readable
        g_io_channel_win32_pipe_request_wakeups
+       g_io_channel_win32_wait_for_condition
        g_io_channel_write
        g_list_alloc
        g_list_append
@@ -207,6 +209,7 @@ EXPORTS
        g_main_remove_poll
        g_main_run
        g_main_set_poll_func
+       g_main_win32_get_poll_func
        g_malloc
        g_malloc0
        g_mem_check
index 3100f52..4ebe662 100644 (file)
@@ -2881,29 +2881,43 @@ void        g_main_add_poll          (GPollFD    *fd,
 void        g_main_remove_poll       (GPollFD    *fd);
 void        g_main_set_poll_func     (GPollFunc   func);
 
+#ifdef G_OS_WIN32
+
+/* Useful on other platforms, too? */
+
+GPollFunc   g_main_win32_get_poll_func (void);
+
+#endif
+
 /* On Unix, IO channels created with this function for any file
  * descriptor or socket.
  *
- * On Win32, use this only for plain files opened with the MSVCRT (the
- * Microsoft run-time C library) _open(), including file descriptors
- * 0, 1 and 2 (corresponding to stdin, stdout and stderr).
- * Actually, don't do even that, this code isn't done yet.
+ * On Win32, use this only for files opened with the MSVCRT (the
+ * Microsoft run-time C library) _open() or _pipe, including file
+ * descriptors 0, 1 and 2 (corresponding to stdin, stdout and stderr).
  *
  * The term file descriptor as used in the context of Win32 refers to
- * the emulated Unix-like file descriptors MSVCRT provides.
+ * the emulated Unix-like file descriptors MSVCRT provides. The native
+ * corresponding concept is file HANDLE. There isn't as of yet a way to
+ * get GIOChannels for file HANDLEs.
  */
 GIOChannel* g_io_channel_unix_new    (int         fd);
 gint        g_io_channel_unix_get_fd (GIOChannel *channel);
 
 #ifdef G_OS_WIN32
 
-GLIB_VAR guint g_pipe_readable_msg;
-
 #define G_WIN32_MSG_HANDLE 19981206
 
+/* This can be used to wait until a channel is readable.  On Unix you
+ * would do a select() on the fd of the channel. This should probably
+ * be replaced by something for all platforms?
+ */
+gint        g_io_channel_win32_wait_for_condition (GIOChannel  *channel,
+                                                  GIOCondition condition,
+                                                  gint         timeout);
+
 /* This is used to add polling for Windows messages. GDK (GTk+) programs
- * should *not* use this. (In fact, I can't think of any program that
- * would want to use this, but it's here just for completeness's sake.
+ * should *not* use this.
  */
 void        g_main_poll_win32_msg_add(gint        priority,
                                      GPollFD    *fd,
@@ -2912,40 +2926,16 @@ void        g_main_poll_win32_msg_add(gint        priority,
 /* An IO channel for Windows messages for window handle hwnd. */
 GIOChannel *g_io_channel_win32_new_messages (guint hwnd);
 
-/* An IO channel for an anonymous pipe as returned from the MSVCRT
- * _pipe(), with no mechanism for the writer to tell the reader when
- * there is data in the pipe.
- *
- * This is not really implemented yet.
- */
-GIOChannel *g_io_channel_win32_new_pipe (int fd);
-
-/* An IO channel for a pipe as returned from the MSVCRT _pipe(), with
- * Windows user messages used to signal data in the pipe for the
- * reader.
- *
- * fd is the file descriptor. For the write end, peer is the thread id
- * of the reader, and peer_fd is his file descriptor for the read end
- * of the pipe.
- *
- * This is used by the GIMP, and works.
+/* An IO channel for C runtime (emulated Unix-like) file
+ * descriptors. Identical to g_io_channel_unix_new above.
  */
-GIOChannel *g_io_channel_win32_new_pipe_with_wakeups (int   fd,
-                                                     guint peer,
-                                                     int   peer_fd);
-
-void        g_io_channel_win32_pipe_request_wakeups (GIOChannel *channel,
-                                                    guint       peer,
-                                                    int         peer_fd);
-
-void        g_io_channel_win32_pipe_readable (int   fd,
-                                             guint offset);
+GIOChannel* g_io_channel_win32_new_fd (int         fd);
 
 /* Get the C runtime file descriptor of a channel. */
 gint        g_io_channel_win32_get_fd (GIOChannel *channel);
 
-/* An IO channel for a SOCK_STREAM winsock socket. The parameter is
- * actually a SOCKET.
+/* An IO channel for a SOCK_STREAM winsock socket. The parameter should
+ * be a SOCKET.
  */
 GIOChannel *g_io_channel_win32_new_stream_socket (int socket);
 
index 23a0c30..a3404a6 100644 (file)
@@ -36,8 +36,6 @@
 /* uncomment the next line to get poll() debugging info */
 /* #define G_MAIN_POLL_DEBUG */
 
-
-
 #include "glib.h"
 #include <sys/types.h>
 #include <time.h>
@@ -192,34 +190,39 @@ static GPollFunc poll_func = (GPollFunc) poll;
 #ifdef G_OS_WIN32
 
 static gint
-g_poll (GPollFD *fds, guint nfds, gint timeout)
+g_poll (GPollFD *fds,
+       guint    nfds,
+       gint     timeout)
 {
   HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+  gboolean poll_msgs = FALSE;
   GPollFD *f;
   DWORD ready;
   MSG msg;
   UINT timer;
-  LONG prevcnt;
-  gint poll_msgs = -1;
   gint nhandles = 0;
 
   for (f = fds; f < &fds[nfds]; ++f)
     if (f->fd >= 0)
       {
        if (f->events & G_IO_IN)
-         if (f->fd == G_WIN32_MSG_HANDLE)
-           poll_msgs = f - fds;
-         else
-           {
-             /* g_print ("g_poll: waiting for handle %#x\n", f->fd); */
-             handles[nhandles++] = (HANDLE) f->fd;
-           }
+         {
+           if (f->fd == G_WIN32_MSG_HANDLE)
+             poll_msgs = TRUE;
+           else
+             {
+#ifdef G_MAIN_POLL_DEBUG
+               g_print ("g_poll: waiting for %#x\n", f->fd);
+#endif
+               handles[nhandles++] = (HANDLE) f->fd;
+             }
+         }
       }
 
   if (timeout == -1)
     timeout = INFINITE;
 
-  if (poll_msgs >= 0)
+  if (poll_msgs)
     {
       /* Waiting for messages, and maybe events */
       if (nhandles == 0)
@@ -229,7 +232,9 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
              /* Waiting just for messages, infinite timeout
               * -> Use PeekMessage, then WaitMessage
               */
-             /* g_print ("WaitMessage, PeekMessage\n"); */
+#ifdef G_MAIN_POLL_DEBUG
+             g_print ("PeekMessage, then WaitMessage\n");
+#endif
              if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
                ready = WAIT_OBJECT_0;
              else if (!WaitMessage ())
@@ -241,7 +246,9 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
              /* Waiting just for messages, zero timeout
               * -> Use PeekMessage
               */
-             /* g_print ("PeekMessage\n"); */
+#ifdef G_MAIN_POLL_DEBUG
+             g_print ("PeekMessage\n");
+#endif
              if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
                ready = WAIT_OBJECT_0;
              else
@@ -253,17 +260,22 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
               * -> First try PeekMessage, then set a timer, wait for message,
               * kill timer, use PeekMessage
               */
-             /* g_print ("PeekMessage\n"); */
+#ifdef G_MAIN_POLL_DEBUG
+             g_print ("PeekMessage\n");
+#endif
              if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
                ready = WAIT_OBJECT_0;
              else if ((timer = SetTimer (NULL, 0, timeout, NULL)) == 0)
                g_warning ("g_poll: SetTimer failed");
              else
                {
-                 /* g_print ("WaitMessage\n"); */
+#ifdef G_MAIN_POLL_DEBUG
+                 g_print ("WaitMessage\n");
+#endif
                  WaitMessage ();
                  KillTimer (NULL, timer);
-                 if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
+                 if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)
+                     && msg.message != WM_TIMER)
                    ready = WAIT_OBJECT_0;
                  else
                    ready = WAIT_TIMEOUT;
@@ -275,10 +287,12 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
          /* Wait for either message or event
           * -> Use MsgWaitForMultipleObjects
           */
-         /* g_print ("MsgWaitForMultipleObjects(%d, %d)\n", nhandles, timeout); */
+#ifdef G_MAIN_POLL_DEBUG
+         g_print ("MsgWaitForMultipleObjects(%d, %d)\n", nhandles, timeout);
+#endif
          ready = MsgWaitForMultipleObjects (nhandles, handles, FALSE,
                                             timeout, QS_ALLINPUT);
-         /* g_print("=%d\n", ready); */
+
          if (ready == WAIT_FAILED)
            g_warning ("g_poll: MsgWaitForMultipleObjects failed");
        }
@@ -293,9 +307,10 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
       /* Wait for just events
        * -> Use WaitForMultipleObjects
        */
-      /* g_print ("WaitForMultipleObjects(%d, %d)\n", nhandles, timeout); */
+#ifdef G_MAIN_POLL_DEBUG
+      g_print ("WaitForMultipleObjects(%d, %d)\n", nhandles, timeout);
+#endif
       ready = WaitForMultipleObjects (nhandles, handles, FALSE, timeout);
-      /* g_print("=%d\n", ready); */
       if (ready == WAIT_FAILED)
        g_warning ("g_poll: WaitForMultipleObjects failed");
     }
@@ -305,9 +320,17 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
 
   if (ready == WAIT_FAILED)
     return -1;
-  else if (poll_msgs >= 0 && ready == WAIT_OBJECT_0 + nhandles)
+  else if (ready == WAIT_TIMEOUT)
+    return 0;
+  else if (poll_msgs && ready == WAIT_OBJECT_0 + nhandles)
     {
-      fds[poll_msgs].revents |= G_IO_IN;
+      for (f = fds; f < &fds[nfds]; ++f)
+       if (f->fd >= 0)
+         {
+           if (f->events & G_IO_IN)
+             if (f->fd == G_WIN32_MSG_HANDLE)
+               f->revents |= G_IO_IN;
+         }
     }
   else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles)
     for (f = fds; f < &fds[nfds]; ++f)
@@ -316,15 +339,19 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
            && f->fd == (gint) handles[ready - WAIT_OBJECT_0])
          {
            f->revents |= G_IO_IN;
-           /* g_print ("event %#x\n", f->fd); */
+#ifdef G_MAIN_POLL_DEBUG
+           g_print ("g_poll: got event %#x\n", f->fd);
+#endif
+#if 0
            ResetEvent ((HANDLE) f->fd);
+#endif
          }
       }
     
-  if (ready == WAIT_TIMEOUT)
-    return 0;
+  if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles)
+    return ready - WAIT_OBJECT_0 + 1;
   else
-    return 1;
+    return 0;
 }
 
 #else  /* !G_OS_WIN32 */
@@ -592,7 +619,6 @@ void
 g_get_current_time (GTimeVal *result)
 {
 #ifndef G_OS_WIN32
-  struct timeval r;
   g_return_if_fail (result != NULL);
 
   /*this is required on alpha, there the timeval structs are int's
@@ -608,7 +634,6 @@ g_get_current_time (GTimeVal *result)
   static DWORD start_tick = 0;
   static time_t start_time;
   DWORD tick;
-  time_t t;
 
   g_return_if_fail (result != NULL);
  
@@ -992,9 +1017,12 @@ g_main_poll (gint     timeout,
   if (wake_up_semaphore == NULL)
     {
       if ((wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL)) == NULL)
-       g_error ("Cannot create wake-up semaphore: %d", GetLastError ());
+       g_error ("Cannot create wake-up semaphore: %s", g_win32_error_message (GetLastError ()));
       wake_up_rec.fd = (gint) wake_up_semaphore;
       wake_up_rec.events = G_IO_IN;
+#ifdef G_MAIN_POLL_DEBUG
+      g_print ("wake-up semaphore: %#x\n", (guint) wake_up_semaphore);
+#endif
       g_main_add_poll_unlocked (0, &wake_up_rec);
     }
 #endif
@@ -1029,7 +1057,7 @@ g_main_poll (gint     timeout,
   if (npoll || timeout != 0)
     {
 #ifdef G_MAIN_POLL_DEBUG
-      g_print ("g_main_poll(%d) timeout: %d\r", npoll, timeout);
+      g_print ("g_main_poll(%d) timeout: %d\n", npoll, timeout);
       poll_timer = g_timer_new ();
 #endif
       
@@ -1223,6 +1251,18 @@ g_main_set_poll_func (GPollFunc func)
 #endif
 }
 
+#ifdef G_OS_WIN32
+
+/* Useful on other platforms, too? */
+
+GPollFunc
+g_main_win32_get_poll_func (void)
+{
+  return poll_func;
+}
+
+#endif
+
 /* Wake the main loop up from a poll() */
 static void
 g_main_wakeup (void)
diff --git a/gmain.c b/gmain.c
index 23a0c30..a3404a6 100644 (file)
--- a/gmain.c
+++ b/gmain.c
@@ -36,8 +36,6 @@
 /* uncomment the next line to get poll() debugging info */
 /* #define G_MAIN_POLL_DEBUG */
 
-
-
 #include "glib.h"
 #include <sys/types.h>
 #include <time.h>
@@ -192,34 +190,39 @@ static GPollFunc poll_func = (GPollFunc) poll;
 #ifdef G_OS_WIN32
 
 static gint
-g_poll (GPollFD *fds, guint nfds, gint timeout)
+g_poll (GPollFD *fds,
+       guint    nfds,
+       gint     timeout)
 {
   HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+  gboolean poll_msgs = FALSE;
   GPollFD *f;
   DWORD ready;
   MSG msg;
   UINT timer;
-  LONG prevcnt;
-  gint poll_msgs = -1;
   gint nhandles = 0;
 
   for (f = fds; f < &fds[nfds]; ++f)
     if (f->fd >= 0)
       {
        if (f->events & G_IO_IN)
-         if (f->fd == G_WIN32_MSG_HANDLE)
-           poll_msgs = f - fds;
-         else
-           {
-             /* g_print ("g_poll: waiting for handle %#x\n", f->fd); */
-             handles[nhandles++] = (HANDLE) f->fd;
-           }
+         {
+           if (f->fd == G_WIN32_MSG_HANDLE)
+             poll_msgs = TRUE;
+           else
+             {
+#ifdef G_MAIN_POLL_DEBUG
+               g_print ("g_poll: waiting for %#x\n", f->fd);
+#endif
+               handles[nhandles++] = (HANDLE) f->fd;
+             }
+         }
       }
 
   if (timeout == -1)
     timeout = INFINITE;
 
-  if (poll_msgs >= 0)
+  if (poll_msgs)
     {
       /* Waiting for messages, and maybe events */
       if (nhandles == 0)
@@ -229,7 +232,9 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
              /* Waiting just for messages, infinite timeout
               * -> Use PeekMessage, then WaitMessage
               */
-             /* g_print ("WaitMessage, PeekMessage\n"); */
+#ifdef G_MAIN_POLL_DEBUG
+             g_print ("PeekMessage, then WaitMessage\n");
+#endif
              if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
                ready = WAIT_OBJECT_0;
              else if (!WaitMessage ())
@@ -241,7 +246,9 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
              /* Waiting just for messages, zero timeout
               * -> Use PeekMessage
               */
-             /* g_print ("PeekMessage\n"); */
+#ifdef G_MAIN_POLL_DEBUG
+             g_print ("PeekMessage\n");
+#endif
              if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
                ready = WAIT_OBJECT_0;
              else
@@ -253,17 +260,22 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
               * -> First try PeekMessage, then set a timer, wait for message,
               * kill timer, use PeekMessage
               */
-             /* g_print ("PeekMessage\n"); */
+#ifdef G_MAIN_POLL_DEBUG
+             g_print ("PeekMessage\n");
+#endif
              if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
                ready = WAIT_OBJECT_0;
              else if ((timer = SetTimer (NULL, 0, timeout, NULL)) == 0)
                g_warning ("g_poll: SetTimer failed");
              else
                {
-                 /* g_print ("WaitMessage\n"); */
+#ifdef G_MAIN_POLL_DEBUG
+                 g_print ("WaitMessage\n");
+#endif
                  WaitMessage ();
                  KillTimer (NULL, timer);
-                 if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
+                 if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)
+                     && msg.message != WM_TIMER)
                    ready = WAIT_OBJECT_0;
                  else
                    ready = WAIT_TIMEOUT;
@@ -275,10 +287,12 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
          /* Wait for either message or event
           * -> Use MsgWaitForMultipleObjects
           */
-         /* g_print ("MsgWaitForMultipleObjects(%d, %d)\n", nhandles, timeout); */
+#ifdef G_MAIN_POLL_DEBUG
+         g_print ("MsgWaitForMultipleObjects(%d, %d)\n", nhandles, timeout);
+#endif
          ready = MsgWaitForMultipleObjects (nhandles, handles, FALSE,
                                             timeout, QS_ALLINPUT);
-         /* g_print("=%d\n", ready); */
+
          if (ready == WAIT_FAILED)
            g_warning ("g_poll: MsgWaitForMultipleObjects failed");
        }
@@ -293,9 +307,10 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
       /* Wait for just events
        * -> Use WaitForMultipleObjects
        */
-      /* g_print ("WaitForMultipleObjects(%d, %d)\n", nhandles, timeout); */
+#ifdef G_MAIN_POLL_DEBUG
+      g_print ("WaitForMultipleObjects(%d, %d)\n", nhandles, timeout);
+#endif
       ready = WaitForMultipleObjects (nhandles, handles, FALSE, timeout);
-      /* g_print("=%d\n", ready); */
       if (ready == WAIT_FAILED)
        g_warning ("g_poll: WaitForMultipleObjects failed");
     }
@@ -305,9 +320,17 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
 
   if (ready == WAIT_FAILED)
     return -1;
-  else if (poll_msgs >= 0 && ready == WAIT_OBJECT_0 + nhandles)
+  else if (ready == WAIT_TIMEOUT)
+    return 0;
+  else if (poll_msgs && ready == WAIT_OBJECT_0 + nhandles)
     {
-      fds[poll_msgs].revents |= G_IO_IN;
+      for (f = fds; f < &fds[nfds]; ++f)
+       if (f->fd >= 0)
+         {
+           if (f->events & G_IO_IN)
+             if (f->fd == G_WIN32_MSG_HANDLE)
+               f->revents |= G_IO_IN;
+         }
     }
   else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles)
     for (f = fds; f < &fds[nfds]; ++f)
@@ -316,15 +339,19 @@ g_poll (GPollFD *fds, guint nfds, gint timeout)
            && f->fd == (gint) handles[ready - WAIT_OBJECT_0])
          {
            f->revents |= G_IO_IN;
-           /* g_print ("event %#x\n", f->fd); */
+#ifdef G_MAIN_POLL_DEBUG
+           g_print ("g_poll: got event %#x\n", f->fd);
+#endif
+#if 0
            ResetEvent ((HANDLE) f->fd);
+#endif
          }
       }
     
-  if (ready == WAIT_TIMEOUT)
-    return 0;
+  if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles)
+    return ready - WAIT_OBJECT_0 + 1;
   else
-    return 1;
+    return 0;
 }
 
 #else  /* !G_OS_WIN32 */
@@ -592,7 +619,6 @@ void
 g_get_current_time (GTimeVal *result)
 {
 #ifndef G_OS_WIN32
-  struct timeval r;
   g_return_if_fail (result != NULL);
 
   /*this is required on alpha, there the timeval structs are int's
@@ -608,7 +634,6 @@ g_get_current_time (GTimeVal *result)
   static DWORD start_tick = 0;
   static time_t start_time;
   DWORD tick;
-  time_t t;
 
   g_return_if_fail (result != NULL);
  
@@ -992,9 +1017,12 @@ g_main_poll (gint     timeout,
   if (wake_up_semaphore == NULL)
     {
       if ((wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL)) == NULL)
-       g_error ("Cannot create wake-up semaphore: %d", GetLastError ());
+       g_error ("Cannot create wake-up semaphore: %s", g_win32_error_message (GetLastError ()));
       wake_up_rec.fd = (gint) wake_up_semaphore;
       wake_up_rec.events = G_IO_IN;
+#ifdef G_MAIN_POLL_DEBUG
+      g_print ("wake-up semaphore: %#x\n", (guint) wake_up_semaphore);
+#endif
       g_main_add_poll_unlocked (0, &wake_up_rec);
     }
 #endif
@@ -1029,7 +1057,7 @@ g_main_poll (gint     timeout,
   if (npoll || timeout != 0)
     {
 #ifdef G_MAIN_POLL_DEBUG
-      g_print ("g_main_poll(%d) timeout: %d\r", npoll, timeout);
+      g_print ("g_main_poll(%d) timeout: %d\n", npoll, timeout);
       poll_timer = g_timer_new ();
 #endif
       
@@ -1223,6 +1251,18 @@ g_main_set_poll_func (GPollFunc func)
 #endif
 }
 
+#ifdef G_OS_WIN32
+
+/* Useful on other platforms, too? */
+
+GPollFunc
+g_main_win32_get_poll_func (void)
+{
+  return poll_func;
+}
+
+#endif
+
 /* Wake the main loop up from a poll() */
 static void
 g_main_wakeup (void)
index f96d38b..4e3d0fb 100644 (file)
@@ -11,6 +11,7 @@ TESTS = \
        array-test      \
        date-test       \
        dirname-test    \
+       gio-test        \
        hash-test       \
        list-test       \
        node-test       \
@@ -33,6 +34,7 @@ thread_LDADD = $(progs_LDADD) $(top_builddir)/gthread/libgthread-1.3.la @G_THREA
 array_test_LDADD = $(progs_LDADD)
 date_test_LDADD = $(progs_LDADD)
 dirname_test_LDADD = $(progs_LDADD)
+gio_test_LDADD = $(progs_LDADD)
 hash_test_LDADD = $(progs_LDADD)
 list_test_LDADD = $(progs_LDADD)
 node_test_LDADD = $(progs_LDADD)
diff --git a/tests/gio-test.c b/tests/gio-test.c
new file mode 100644 (file)
index 0000000..cbdbe60
--- /dev/null
@@ -0,0 +1,285 @@
+/* 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.
+ */
+
+#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>
+#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 gboolean
+recv_message (GIOChannel  *channel,
+             GIOCondition cond,
+             gpointer    data)
+{
+  gint fd = g_io_channel_unix_get_fd (channel);
+
+  g_print ("testgio: ...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))
+    {
+      g_source_remove (*(guint *) data);
+      nrunning--;
+      if (nrunning == 0)
+       g_main_quit (main_loop);
+    }
+
+  if (cond & G_IO_IN)
+    {
+      char buf[BUFSIZE];
+      guint nbytes;
+      guint nb;
+      int i, j, seq;
+      GIOError error;
+      
+      error = g_io_channel_read (channel, (gchar *) &seq, sizeof (seq), &nb);
+      if (error == G_IO_ERROR_NONE)
+       {
+         if (nb == 0)
+           {
+             g_print ("testgio: ...from %d: EOF\n", fd);
+             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 ("testgio: ...from &d: invalid sequence number %d, expected %d\n",
+                            seq, seqtab[i].seq);
+                   g_assert_not_reached ();
+                 }
+               seqtab[i].seq++;
+               break;
+             }
+
+         error = g_io_channel_read (channel, (gchar *) &nbytes, sizeof (nbytes), &nb);
+       }
+
+      if (error != G_IO_ERROR_NONE)
+       {
+         g_print ("testgio: ...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" : "???"))));
+
+         return FALSE;
+       }
+      
+      if (nb == 0)
+       {
+         g_print ("testgio: ...from %d: EOF\n", fd);
+         return FALSE;
+       }
+      
+      g_assert (nb == sizeof (nbytes));
+
+      if (nbytes >= BUFSIZE)
+       {
+         g_print ("testgio: ...from %d: nbytes = %d (%#x)!\n", fd, nbytes, nbytes);
+         g_assert_not_reached ();
+       }
+      g_assert (nbytes >= 0 && nbytes < BUFSIZE);
+      
+      g_print ("testgio: ...from %d: %d bytes\n", fd, nbytes);
+      
+      if (nbytes > 0)
+       {
+         error = g_io_channel_read (channel, buf, nbytes, &nb);
+         
+         if (error != G_IO_ERROR_NONE)
+           {
+             g_print ("testgio: ...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" : "???"))));
+             
+             return FALSE;
+           }
+
+         if (nb != nbytes)
+           {
+             g_print ("testgio: ...from %d: nb=%d != nbytes=%d\n",
+                      fd, nb, nbytes);
+             g_assert_not_reached ();
+           }
+         
+         for (j = 0; j < nbytes; j++)
+           if (buf[j] != ' ' + ((nbytes + j) % 95))
+             {
+               g_print ("testgio: ...from %d: buf[%d] == '%c', should be '%c'\n",
+                        fd, j, buf[j], 'a' + ((nbytes + j) % 32));
+               g_assert_not_reached ();
+             }
+         g_print ("testgio: ...from %d: OK\n", fd);
+       }
+    }
+  return TRUE;
+}
+
+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;
+      int pollresult;
+#endif
+
+      nkiddies = (argc == 1 ? 1 : atoi(argv[1]));
+      seqtab = g_malloc (nkiddies * 2 * sizeof (int));
+
+      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);
+         
+         cmdline = g_strdup_printf ("%s %d %d &", argv[0],
+                                    pipe_to_sub[0], pipe_from_sub[1]);
+         
+         nrunning++;
+         
+#ifdef G_OS_WIN32
+         {
+           gchar *readfd = g_strdup_printf ("%d", pipe_to_sub[0]);
+           gchar *writefd = g_strdup_printf ("%d", pipe_from_sub[1]);
+           _spawnl (_P_NOWAIT, argv[0], argv[0], readfd, writefd, NULL);
+         }
+#else
+         system (cmdline);
+#endif
+         close (pipe_to_sub[0]);
+         close (pipe_from_sub [1]);
+
+#ifdef G_OS_WIN32
+         g_get_current_time (&start);
+         pollresult = g_io_channel_win32_wait_for_condition (my_read_channel, G_IO_IN, 100);
+         g_get_current_time (&end);
+         if (end.tv_usec < start.tv_usec)
+           end.tv_sec--, end.tv_usec += 1000000;
+         g_print ("testgio: 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;
+      int i, j;
+      char buf[BUFSIZE];
+      int buflen;
+      GTimeVal tv;
+  
+      g_get_current_time (&tv);
+      
+      readfd = atoi (argv[1]);
+      writefd = atoi (argv[2]);
+      
+      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 ("testgio: child writing %d bytes to %d\n", buflen, writefd);
+         write (writefd, &i, sizeof (i));
+         write (writefd, &buflen, sizeof (buflen));
+         write (writefd, buf, buflen);
+       }
+      g_print ("testgio: child exiting, closing %d\n", writefd);
+      close (writefd);
+    }
+  else
+    g_print ("Huh?\n");
+  
+  return 0;
+}
+
index 051c4ac..b010455 100644 (file)
@@ -18,6 +18,7 @@ TESTS = \
        array-test.exe  \
        date-test.exe   \
        dirname-test.exe\
+       gio-test.exe    \
        hash-test.exe   \
        list-test.exe   \
        node-test.exe   \