X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgiowin32.c;h=171fb6803bffb4a5f1000b1f39fbdfb731759e48;hb=20f6cc2a10ba26860e7a6d27c100deadb5497772;hp=db5e9ca4d74c59c553475d9c93643c62943c4929;hpb=3d0a1b3b221d32a53557a3512ab6eeadbf816352;p=platform%2Fupstream%2Fglib.git diff --git a/glib/giowin32.c b/glib/giowin32.c index db5e9ca..171fb68 100644 --- a/glib/giowin32.c +++ b/glib/giowin32.c @@ -4,6 +4,7 @@ * giowin32.c: IO Channels for Win32. * Copyright 1998 Owen Taylor and Tor Lillqvist * Copyright 1999-2000 Tor Lillqvist and Craig Setera + * Copyright 2001-2003 Andrew Lanoix * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,9 +17,7 @@ * 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. + * License along with this library; if not, see . */ /* @@ -28,34 +27,79 @@ * GLib at ftp://ftp.gtk.org/pub/gtk/. */ -/* Define this to get (very) verbose logging of all channels */ -/* #define G_IO_WIN32_DEBUG */ +/* + * Bugs that are related to the code in this file: + * + * Bug 137968 - Sometimes a GIOFunc on Win32 is called with zero condition + * http://bugzilla.gnome.org/show_bug.cgi?id=137968 + * + * Bug 324234 - Using g_io_add_watch_full() to wait for connect() to return on a non-blocking socket returns prematurely + * http://bugzilla.gnome.org/show_bug.cgi?id=324234 + * + * Bug 331214 - g_io_channel async socket io stalls + * http://bugzilla.gnome.org/show_bug.cgi?id=331214 + * + * Bug 338943 - Multiple watches on the same socket + * http://bugzilla.gnome.org/show_bug.cgi?id=338943 + * + * Bug 357674 - 2 serious bugs in giowin32.c making glib iochannels useless + * http://bugzilla.gnome.org/show_bug.cgi?id=357674 + * + * Bug 425156 - GIOChannel deadlocks on a win32 socket + * http://bugzilla.gnome.org/show_bug.cgi?id=425156 + * + * Bug 468910 - giofunc condition=0 + * http://bugzilla.gnome.org/show_bug.cgi?id=468910 + * + * Bug 500246 - Bug fixes for giowin32 + * http://bugzilla.gnome.org/show_bug.cgi?id=500246 + * + * Bug 548278 - Async GETs connections are always terminated unexpectedly on windows + * http://bugzilla.gnome.org/show_bug.cgi?id=548278 + * + * Bug 548536 - giowin32 problem when adding and removing watches + * http://bugzilla.gnome.org/show_bug.cgi?id=548536 + * + * When fixing bugs related to the code in this file, either the above + * bugs or others, make sure that the test programs attached to the + * above bugs continue to work. + */ + +#include "config.h" #include "glib.h" #include +#include #include -#include /* Not everybody has winsock2 */ +#include #include #include #include #include #include +#include "gstdio.h" +#include "glibintl.h" + + typedef struct _GIOWin32Channel GIOWin32Channel; typedef struct _GIOWin32Watch GIOWin32Watch; #define BUFFER_SIZE 4096 typedef enum { - G_IO_WINDOWS_MESSAGES, /* Windows messages */ - 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(). + G_IO_WIN32_WINDOWS_MESSAGES, /* Windows messages */ + + G_IO_WIN32_FILE_DESC, /* Unix-like file descriptors from + * _open() or _pipe(), except for + * console IO. Separate thread to read + * or write. */ + + G_IO_WIN32_CONSOLE, /* Console IO (usually stdin, stdout, stderr) */ + + G_IO_WIN32_SOCKET /* Sockets. No separate thread. */ } GIOWin32ChannelType; struct _GIOWin32Channel { @@ -68,13 +112,34 @@ struct _GIOWin32Channel { gboolean debug; - /* This is used by G_IO_WINDOWS_MESSAGES channels */ - HWND hwnd; /* handle of window, or NULL */ - - /* Following fields used by fd and socket channels for input */ + /* Field used by G_IO_WIN32_WINDOWS_MESSAGES channels */ + HWND hwnd; /* Handle of window, or NULL */ + /* Fields used by G_IO_WIN32_FILE_DESC channels. */ + CRITICAL_SECTION mutex; + + int direction; /* 0 means we read from it, + * 1 means we write to it. + */ + + gboolean running; /* Is reader or writer thread + * running. FALSE if EOF has been + * reached by the reader thread. + */ + + gboolean needs_close; /* If the channel has been closed while + * the reader thread was still running. + */ + + guint thread_id; /* If non-NULL the channel has or has + * had a reader or writer thread. + */ + HANDLE data_avail_event; + + gushort revents; + /* 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 + * empty and full buffers, 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). @@ -85,45 +150,141 @@ struct _GIOWin32Channel { */ 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) + /* Fields used by G_IO_WIN32_SOCKET channels */ + int event_mask; + int last_events; + HANDLE event; + gboolean write_would_have_blocked; + gboolean ever_writable; +}; struct _GIOWin32Watch { + GSource source; GPollFD pollfd; GIOChannel *channel; GIOCondition condition; - GIOFunc callback; }; static void +g_win32_print_access_mode (int flags) +{ + g_print ("%s%s%s%s%s%s%s%s%s%s", + ((flags & 0x3) == _O_RDWR ? "O_RDWR" : + ((flags & 0x3) == _O_RDONLY ? "O_RDONLY" : + ((flags & 0x3) == _O_WRONLY ? "O_WRONLY" : "0"))), + (flags & _O_APPEND ? "|O_APPEND" : ""), + (flags & _O_RANDOM ? "|O_RANDOM" : ""), + (flags & _O_SEQUENTIAL ? "|O_SEQUENTIAL" : ""), + (flags & _O_TEMPORARY ? "|O_TEMPORARY" : ""), + (flags & _O_CREAT ? "|O_CREAT" : ""), + (flags & _O_TRUNC ? "|O_TRUNC" : ""), + (flags & _O_EXCL ? "|O_EXCL" : ""), + (flags & _O_TEXT ? "|O_TEXT" : ""), + (flags & _O_BINARY ? "|O_BINARY" : "")); +} + +static void +g_win32_print_gioflags (GIOFlags flags) +{ + char *bar = ""; + + if (flags & G_IO_FLAG_APPEND) + bar = "|", g_print ("APPEND"); + if (flags & G_IO_FLAG_NONBLOCK) + g_print ("%sNONBLOCK", bar), bar = "|"; + if (flags & G_IO_FLAG_IS_READABLE) + g_print ("%sREADABLE", bar), bar = "|"; + if (flags & G_IO_FLAG_IS_WRITABLE) + g_print ("%sWRITABLE", bar), bar = "|"; + if (flags & G_IO_FLAG_IS_SEEKABLE) + g_print ("%sSEEKABLE", bar), bar = "|"; +} + +static const char * +event_mask_to_string (int mask) +{ + char buf[100]; + int checked_bits = 0; + char *bufp = buf; + + if (mask == 0) + return ""; + +#define BIT(n) checked_bits |= FD_##n; if (mask & FD_##n) bufp += sprintf (bufp, "%s" #n, (bufp>buf ? "|" : "")) + + BIT (READ); + BIT (WRITE); + BIT (OOB); + BIT (ACCEPT); + BIT (CONNECT); + BIT (CLOSE); + BIT (QOS); + BIT (GROUP_QOS); + BIT (ROUTING_INTERFACE_CHANGE); + BIT (ADDRESS_LIST_CHANGE); + +#undef BIT + + if ((mask & ~checked_bits) != 0) + bufp += sprintf (bufp, "|%#x", mask & ~checked_bits); + + return g_quark_to_string (g_quark_from_string (buf)); +} + +static const char * +condition_to_string (GIOCondition condition) +{ + char buf[100]; + int checked_bits = 0; + char *bufp = buf; + + if (condition == 0) + return ""; + +#define BIT(n) checked_bits |= G_IO_##n; if (condition & G_IO_##n) bufp += sprintf (bufp, "%s" #n, (bufp>buf ? "|" : "")) + + BIT (IN); + BIT (OUT); + BIT (PRI); + BIT (ERR); + BIT (HUP); + BIT (NVAL); + +#undef BIT + + if ((condition & ~checked_bits) != 0) + bufp += sprintf (bufp, "|%#x", condition & ~checked_bits); + + return g_quark_to_string (g_quark_from_string (buf)); +} + +static gboolean +g_io_win32_get_debug_flag (void) +{ + return (getenv ("G_IO_WIN32_DEBUG") != NULL); +} + +static void g_io_channel_win32_init (GIOWin32Channel *channel) { -#ifdef G_IO_WIN32_DEBUG - channel->debug = TRUE; -#else - if (getenv ("G_IO_WIN32_DEBUG") != NULL) - channel->debug = TRUE; - else - channel->debug = FALSE; -#endif - channel->buffer = NULL; + channel->debug = g_io_win32_get_debug_flag (); + + InitializeCriticalSection (&channel->mutex); channel->running = FALSE; + channel->needs_close = FALSE; channel->thread_id = 0; channel->data_avail_event = NULL; + channel->revents = 0; + channel->buffer = NULL; channel->space_avail_event = NULL; + + channel->event_mask = 0; + channel->last_events = 0; + channel->event = NULL; + channel->write_would_have_blocked = FALSE; + channel->ever_writable = FALSE; } static void @@ -131,7 +292,7 @@ create_events (GIOWin32Channel *channel) { SECURITY_ATTRIBUTES sec_attrs; - sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES); + sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES); sec_attrs.lpSecurityDescriptor = NULL; sec_attrs.bInheritHandle = FALSE; @@ -141,55 +302,57 @@ create_events (GIOWin32Channel *channel) 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); + gchar *emsg = g_win32_error_message (GetLastError ()); + + g_error ("Error creating event: %s", emsg); + g_free (emsg); } - InitializeCriticalSection (&channel->mutex); } static unsigned __stdcall -reader_thread (void *parameter) +read_thread (void *parameter) { GIOWin32Channel *channel = parameter; guchar *buffer; - guint nbytes; + gint nbytes; - g_io_channel_ref ((GIOChannel *) channel); + g_io_channel_ref ((GIOChannel *)channel); if (channel->debug) - g_print ("thread %#x: starting. pid:%#x, fd:%d, data_avail:%#x, space_avail:%#x\n", + g_print ("read_thread %#x: start fd=%d, data_avail=%p space_avail=%p\n", channel->thread_id, - (guint) GetCurrentProcessId (), channel->fd, - (guint) channel->data_avail_event, - (guint) channel->space_avail_event); - + channel->data_avail_event, + channel->space_avail_event); + + channel->direction = 0; channel->buffer = g_malloc (BUFFER_SIZE); channel->rdp = channel->wrp = 0; channel->running = TRUE; SetEvent (channel->space_avail_event); + EnterCriticalSection (&channel->mutex); while (channel->running) { - LOCK (channel->mutex); if (channel->debug) - g_print ("thread %#x: rdp=%d, wrp=%d\n", + g_print ("read_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 */ if (channel->debug) - g_print ("thread %#x: resetting space_available\n", + g_print ("read_thread %#x: resetting space_avail\n", channel->thread_id); ResetEvent (channel->space_avail_event); if (channel->debug) - g_print ("thread %#x: waiting for space\n", channel->thread_id); - UNLOCK (channel->mutex); + g_print ("read_thread %#x: waiting for space\n", + channel->thread_id); + LeaveCriticalSection (&channel->mutex); WaitForSingleObject (channel->space_avail_event, INFINITE); - LOCK (channel->mutex); + EnterCriticalSection (&channel->mutex); if (channel->debug) - g_print ("thread %#x: rdp=%d, wrp=%d\n", + g_print ("read_thread %#x: rdp=%d, wrp=%d\n", channel->thread_id, channel->rdp, channel->wrp); } @@ -201,80 +364,227 @@ reader_thread (void *parameter) nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE, BUFFER_SIZE - channel->wrp); - UNLOCK (channel->mutex); + if (channel->debug) + g_print ("read_thread %#x: calling read() for %d bytes\n", + channel->thread_id, nbytes); + + LeaveCriticalSection (&channel->mutex); - nbytes = (*channel->reader) (channel->fd, buffer, nbytes); + nbytes = read (channel->fd, buffer, nbytes); - if (nbytes <= 0) - break; + EnterCriticalSection (&channel->mutex); + + channel->revents = G_IO_IN; + if (nbytes == 0) + channel->revents |= G_IO_HUP; + else if (nbytes < 0) + channel->revents |= G_IO_ERR; - LOCK (channel->mutex); if (channel->debug) - g_print ("thread %#x: got %d bytes, rdp=%d, wrp=%d\n", + g_print ("read_thread %#x: read() returned %d, rdp=%d, wrp=%d\n", channel->thread_id, nbytes, channel->rdp, channel->wrp); + + if (nbytes <= 0) + break; + channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE; if (channel->debug) - g_print ("thread %#x: rdp=%d, wrp=%d, setting data available\n", + g_print ("read_thread %#x: rdp=%d, wrp=%d, setting data_avail\n", channel->thread_id, channel->rdp, channel->wrp); SetEvent (channel->data_avail_event); - UNLOCK (channel->mutex); } - LOCK (channel->mutex); channel->running = FALSE; + if (channel->needs_close) + { + if (channel->debug) + g_print ("read_thread %#x: channel fd %d needs closing\n", + channel->thread_id, channel->fd); + close (channel->fd); + channel->fd = -1; + } + if (channel->debug) - g_print ("thread %#x: got EOF, rdp=%d, wrp=%d, setting data available\n", + g_print ("read_thread %#x: EOF, rdp=%d, wrp=%d, setting data_avail\n", channel->thread_id, channel->rdp, channel->wrp); SetEvent (channel->data_avail_event); - UNLOCK (channel->mutex); + LeaveCriticalSection (&channel->mutex); + + g_io_channel_unref ((GIOChannel *)channel); - g_io_channel_unref((GIOChannel *) channel); + /* No need to call _endthreadex(), the actual thread starter routine + * in MSVCRT (see crt/src/threadex.c:_threadstartex) calls + * _endthreadex() for us. + */ + + return 0; +} + +static unsigned __stdcall +write_thread (void *parameter) +{ + GIOWin32Channel *channel = parameter; + guchar *buffer; + gint nbytes; + + g_io_channel_ref ((GIOChannel *)channel); + + if (channel->debug) + g_print ("write_thread %#x: start fd=%d, data_avail=%p space_avail=%p\n", + channel->thread_id, + channel->fd, + channel->data_avail_event, + channel->space_avail_event); - /* All of the Microsoft docs say we should explicitly - * end the thread... + channel->direction = 1; + channel->buffer = g_malloc (BUFFER_SIZE); + channel->rdp = channel->wrp = 0; + channel->running = TRUE; + + SetEvent (channel->space_avail_event); + + /* We use the same event objects as for a reader thread, but with + * reversed meaning. So, space_avail is used if data is available + * for writing, and data_avail is used if space is available in the + * write buffer. */ - _endthreadex(1); + + EnterCriticalSection (&channel->mutex); + while (channel->running || channel->rdp != channel->wrp) + { + if (channel->debug) + g_print ("write_thread %#x: rdp=%d, wrp=%d\n", + channel->thread_id, channel->rdp, channel->wrp); + if (channel->wrp == channel->rdp) + { + /* Buffer is empty. */ + if (channel->debug) + g_print ("write_thread %#x: resetting space_avail\n", + channel->thread_id); + ResetEvent (channel->space_avail_event); + if (channel->debug) + g_print ("write_thread %#x: waiting for data\n", + channel->thread_id); + channel->revents = G_IO_OUT; + SetEvent (channel->data_avail_event); + LeaveCriticalSection (&channel->mutex); + WaitForSingleObject (channel->space_avail_event, INFINITE); + + EnterCriticalSection (&channel->mutex); + if (channel->rdp == channel->wrp) + break; + + if (channel->debug) + g_print ("write_thread %#x: rdp=%d, wrp=%d\n", + channel->thread_id, channel->rdp, channel->wrp); + } + + buffer = channel->buffer + channel->rdp; + if (channel->rdp < channel->wrp) + nbytes = channel->wrp - channel->rdp; + else + nbytes = BUFFER_SIZE - channel->rdp; + + if (channel->debug) + g_print ("write_thread %#x: calling write() for %d bytes\n", + channel->thread_id, nbytes); + + LeaveCriticalSection (&channel->mutex); + nbytes = write (channel->fd, buffer, nbytes); + EnterCriticalSection (&channel->mutex); + + if (channel->debug) + g_print ("write_thread %#x: write(%i) returned %d, rdp=%d, wrp=%d\n", + channel->thread_id, channel->fd, nbytes, channel->rdp, channel->wrp); + + channel->revents = 0; + if (nbytes > 0) + channel->revents |= G_IO_OUT; + else if (nbytes <= 0) + channel->revents |= G_IO_ERR; + + channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE; + + if (nbytes <= 0) + break; + + if (channel->debug) + g_print ("write_thread: setting data_avail for thread %#x\n", + channel->thread_id); + SetEvent (channel->data_avail_event); + } + + channel->running = FALSE; + if (channel->needs_close) + { + if (channel->debug) + g_print ("write_thread %#x: channel fd %d needs closing\n", + channel->thread_id, channel->fd); + close (channel->fd); + channel->fd = -1; + } + + LeaveCriticalSection (&channel->mutex); + + g_io_channel_unref ((GIOChannel *)channel); return 0; } static void -create_reader_thread (GIOWin32Channel *channel, - gpointer reader) +create_thread (GIOWin32Channel *channel, + GIOCondition condition, + unsigned (__stdcall *thread) (void *parameter)) { - channel->reader = reader; + HANDLE thread_handle; + + thread_handle = (HANDLE) _beginthreadex (NULL, 0, thread, channel, 0, + &channel->thread_id); + if (thread_handle == 0) + g_warning ("Error creating thread: %s.", + g_strerror (errno)); + else if (!CloseHandle (thread_handle)) + { + gchar *emsg = g_win32_error_message (GetLastError ()); + + g_warning ("Error closing thread handle: %s.", emsg); + g_free (emsg); + } - 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 int +static GIOStatus buffer_read (GIOWin32Channel *channel, - guchar *dest, - guint count, - GIOError *error) + gchar *dest, + gsize count, + gsize *bytes_read, + GError **err) { guint nbytes; guint left = count; - LOCK (channel->mutex); + EnterCriticalSection (&channel->mutex); if (channel->debug) - g_print ("reading from thread %#x %d bytes, rdp=%d, wrp=%d\n", + g_print ("reading from thread %#x %" G_GSIZE_FORMAT " bytes, rdp=%d, wrp=%d\n", channel->thread_id, count, channel->rdp, channel->wrp); - if (channel->rdp == channel->wrp) + if (channel->wrp == channel->rdp) { - UNLOCK (channel->mutex); + LeaveCriticalSection (&channel->mutex); if (channel->debug) g_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) + if (channel->debug) + g_print ("done waiting for data from thread %#x\n", channel->thread_id); + EnterCriticalSection (&channel->mutex); + if (channel->wrp == channel->rdp && !channel->running) { - UNLOCK (channel->mutex); - return 0; + if (channel->debug) + g_print ("wrp==rdp, !running\n"); + LeaveCriticalSection (&channel->mutex); + *bytes_read = 0; + return G_IO_STATUS_EOF; } } @@ -282,7 +592,7 @@ buffer_read (GIOWin32Channel *channel, nbytes = channel->wrp - channel->rdp; else nbytes = BUFFER_SIZE - channel->rdp; - UNLOCK (channel->mutex); + LeaveCriticalSection (&channel->mutex); nbytes = MIN (left, nbytes); if (channel->debug) g_print ("moving %d bytes from thread %#x\n", @@ -290,314 +600,685 @@ buffer_read (GIOWin32Channel *channel, memcpy (dest, channel->buffer + channel->rdp, nbytes); dest += nbytes; left -= nbytes; - LOCK (channel->mutex); + EnterCriticalSection (&channel->mutex); channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE; if (channel->debug) - g_print ("setting space available for thread %#x\n", channel->thread_id); + g_print ("setting space_avail for thread %#x\n", channel->thread_id); SetEvent (channel->space_avail_event); if (channel->debug) g_print ("for thread %#x: rdp=%d, wrp=%d\n", channel->thread_id, channel->rdp, channel->wrp); - if (channel->running && channel->rdp == channel->wrp) + if (channel->running && channel->wrp == channel->rdp) { if (channel->debug) - g_print ("resetting data_available of thread %#x\n", + g_print ("resetting data_avail of thread %#x\n", channel->thread_id); ResetEvent (channel->data_avail_event); }; - UNLOCK (channel->mutex); + LeaveCriticalSection (&channel->mutex); /* We have no way to indicate any errors form the actual * read() or recv() call in the reader thread. Should we have? */ - *error = G_IO_ERROR_NONE; - return count - left; + *bytes_read = count - left; + return (*bytes_read > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF; } -static gboolean -g_io_win32_prepare (gpointer source_data, - GTimeVal *current_time, - gint *timeout, - gpointer user_data) -{ - *timeout = -1; - - return FALSE; -} -static gboolean -g_io_win32_check (gpointer source_data, - GTimeVal *current_time, - gpointer user_data) +static GIOStatus +buffer_write (GIOWin32Channel *channel, + const gchar *dest, + gsize count, + gsize *bytes_written, + GError **err) { - GIOWin32Watch *data = source_data; - GIOWin32Channel *channel = (GIOWin32Channel *) data->channel; + guint nbytes; + guint left = count; - /* 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) + EnterCriticalSection (&channel->mutex); + if (channel->debug) + g_print ("buffer_write: writing to thread %#x %" G_GSIZE_FORMAT " bytes, rdp=%d, wrp=%d\n", + channel->thread_id, count, channel->rdp, channel->wrp); + + if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp) { + /* Buffer is full */ + if (channel->debug) + g_print ("buffer_write: tid %#x: resetting data_avail\n", + channel->thread_id); + ResetEvent (channel->data_avail_event); + if (channel->debug) + g_print ("buffer_write: tid %#x: waiting for space\n", + channel->thread_id); + LeaveCriticalSection (&channel->mutex); + WaitForSingleObject (channel->data_avail_event, INFINITE); + EnterCriticalSection (&channel->mutex); if (channel->debug) - g_print ("g_io_win32_check: setting G_IO_HUP thread %#x rdp=%d wrp=%d\n", + g_print ("buffer_write: tid %#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); -} + + nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE, + BUFFER_SIZE - channel->wrp); -static gboolean -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); -} + LeaveCriticalSection (&channel->mutex); + nbytes = MIN (left, nbytes); + if (channel->debug) + g_print ("buffer_write: tid %#x: writing %d bytes\n", + channel->thread_id, nbytes); + memcpy (channel->buffer + channel->wrp, dest, nbytes); + dest += nbytes; + left -= nbytes; + EnterCriticalSection (&channel->mutex); -static void -g_io_win32_destroy (gpointer source_data) -{ - GIOWin32Watch *data = source_data; + channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE; + if (channel->debug) + g_print ("buffer_write: tid %#x: rdp=%d, wrp=%d, setting space_avail\n", + channel->thread_id, channel->rdp, channel->wrp); + SetEvent (channel->space_avail_event); + + if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp) + { + /* Buffer is full */ + if (channel->debug) + g_print ("buffer_write: tid %#x: resetting data_avail\n", + channel->thread_id); + ResetEvent (channel->data_avail_event); + } + + LeaveCriticalSection (&channel->mutex); - g_main_remove_poll (&data->pollfd); - g_io_channel_unref (data->channel); - g_free (data); + /* We have no way to indicate any errors form the actual + * write() call in the writer thread. Should we have? + */ + *bytes_written = count - left; + return (*bytes_written > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF; } -static GSourceFuncs win32_watch_funcs = { - g_io_win32_prepare, - g_io_win32_check, - g_io_win32_dispatch, - g_io_win32_destroy -}; -static guint -g_io_win32_add_watch (GIOChannel *channel, - gint priority, - GIOCondition condition, - GIOFunc func, - gpointer user_data, - GDestroyNotify notify, - int (*reader) (int, guchar *, int)) +static gboolean +g_io_win32_prepare (GSource *source, + gint *timeout) { - GIOWin32Watch *watch = g_new (GIOWin32Watch, 1); - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - - watch->channel = channel; - g_io_channel_ref (channel); + GIOWin32Watch *watch = (GIOWin32Watch *)source; + GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel); + GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel; + int event_mask; - watch->callback = func; - watch->condition = condition; + *timeout = -1; - if (win32_channel->data_avail_event == NULL) - create_events (win32_channel); + if (channel->debug) + g_print ("g_io_win32_prepare: source=%p channel=%p", source, channel); - watch->pollfd.fd = (gint) win32_channel->data_avail_event; - watch->pollfd.events = condition; - - if (win32_channel->debug) - g_print ("g_io_win32_add_watch: fd:%d handle:%#x\n", - win32_channel->fd, watch->pollfd.fd); - - if (win32_channel->thread_id == 0) - create_reader_thread (win32_channel, reader); + switch (channel->type) + { + case G_IO_WIN32_WINDOWS_MESSAGES: + if (channel->debug) + g_print (" MSG"); + break; - g_main_add_poll (&watch->pollfd, priority); - - return g_source_add (priority, TRUE, &win32_watch_funcs, watch, - user_data, notify); + case G_IO_WIN32_CONSOLE: + if (channel->debug) + g_print (" CON"); + break; + + case G_IO_WIN32_FILE_DESC: + if (channel->debug) + g_print (" FD thread=%#x buffer_condition:{%s}" + "\n watch->pollfd.events:{%s} watch->pollfd.revents:{%s} channel->revents:{%s}", + channel->thread_id, condition_to_string (buffer_condition), + condition_to_string (watch->pollfd.events), + condition_to_string (watch->pollfd.revents), + condition_to_string (channel->revents)); + + EnterCriticalSection (&channel->mutex); + if (channel->running) + { + if (channel->direction == 0 && channel->wrp == channel->rdp) + { + if (channel->debug) + g_print ("\n setting revents=0"); + channel->revents = 0; + } + } + else + { + if (channel->direction == 1 + && (channel->wrp + 1) % BUFFER_SIZE == channel->rdp) + { + if (channel->debug) + g_print ("\n setting revents=0"); + channel->revents = 0; + } + } + LeaveCriticalSection (&channel->mutex); + break; + + case G_IO_WIN32_SOCKET: + if (channel->debug) + g_print (" SOCK"); + event_mask = 0; + if (watch->condition & G_IO_IN) + event_mask |= (FD_READ | FD_ACCEPT); + if (watch->condition & G_IO_OUT) + event_mask |= (FD_WRITE | FD_CONNECT); + event_mask |= FD_CLOSE; + + if (channel->event_mask != event_mask) + { + if (channel->debug) + g_print ("\n WSAEventSelect(%d,%p,{%s})", + channel->fd, (HANDLE) watch->pollfd.fd, + event_mask_to_string (event_mask)); + if (WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd, + event_mask) == SOCKET_ERROR) + if (channel->debug) + { + gchar *emsg = g_win32_error_message (WSAGetLastError ()); + + g_print (" failed: %s", emsg); + g_free (emsg); + } + channel->event_mask = event_mask; + + if (channel->debug) + g_print ("\n setting last_events=0"); + channel->last_events = 0; + + if ((event_mask & FD_WRITE) && + channel->ever_writable && + !channel->write_would_have_blocked) + { + if (channel->debug) + g_print (" WSASetEvent(%p)", (WSAEVENT) watch->pollfd.fd); + WSASetEvent ((WSAEVENT) watch->pollfd.fd); + } + } + break; + + default: + g_assert_not_reached (); + abort (); + } + if (channel->debug) + g_print ("\n"); + + return ((watch->condition & buffer_condition) == watch->condition); } -static GIOError -g_io_win32_msg_read (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_read) +static gboolean +g_io_win32_check (GSource *source) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - 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; + MSG msg; + GIOWin32Watch *watch = (GIOWin32Watch *)source; + GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel; + GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel); + WSANETWORKEVENTS events; + + if (channel->debug) + g_print ("g_io_win32_check: source=%p channel=%p", source, channel); + + switch (channel->type) + { + case G_IO_WIN32_WINDOWS_MESSAGES: + if (channel->debug) + g_print (" MSG\n"); + return (PeekMessage (&msg, channel->hwnd, 0, 0, PM_NOREMOVE)); + + case G_IO_WIN32_FILE_DESC: + if (channel->debug) + g_print (" FD thread=%#x buffer_condition=%s\n" + " watch->pollfd.events={%s} watch->pollfd.revents={%s} channel->revents={%s}\n", + channel->thread_id, condition_to_string (buffer_condition), + condition_to_string (watch->pollfd.events), + condition_to_string (watch->pollfd.revents), + condition_to_string (channel->revents)); + + watch->pollfd.revents = (watch->pollfd.events & channel->revents); + + return ((watch->pollfd.revents | buffer_condition) & watch->condition); + + case G_IO_WIN32_CONSOLE: + if (channel->debug) + g_print (" CON\n"); + if (watch->channel->is_writeable) + return TRUE; + else if (watch->channel->is_readable) + { + INPUT_RECORD buffer; + DWORD n; + if (PeekConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n) && + n == 1) + { + /* _kbhit() does quite complex processing to find out + * whether at least one of the key events pending corresponds + * to a "real" character that can be read. + */ + if (_kbhit ()) + return TRUE; + + /* Discard all other kinds of events */ + ReadConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n); + } + } + return FALSE; + + case G_IO_WIN32_SOCKET: + if (channel->debug) + g_print (" SOCK"); + if (channel->last_events & FD_WRITE) + { + if (channel->debug) + g_print (" sock=%d event=%p last_events has FD_WRITE", + channel->fd, (HANDLE) watch->pollfd.fd); + } + else + { + WSAEnumNetworkEvents (channel->fd, 0, &events); + + if (channel->debug) + g_print ("\n revents={%s} condition={%s}" + "\n WSAEnumNetworkEvents(%d,0) sets events={%s}", + condition_to_string (watch->pollfd.revents), + condition_to_string (watch->condition), + channel->fd, + event_mask_to_string (events.lNetworkEvents)); + + if (watch->pollfd.revents != 0 && + events.lNetworkEvents == 0 && + !(channel->event_mask & FD_WRITE)) + { + channel->event_mask = 0; + if (channel->debug) + g_print ("\n WSAEventSelect(%d,%p,{})", + channel->fd, (HANDLE) watch->pollfd.fd); + WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd, 0); + if (channel->debug) + g_print (" ResetEvent(%p)", + (HANDLE) watch->pollfd.fd); + ResetEvent ((HANDLE) watch->pollfd.fd); + } + else if (events.lNetworkEvents & FD_WRITE) + channel->ever_writable = TRUE; + channel->last_events = events.lNetworkEvents; + } + + watch->pollfd.revents = 0; + if (channel->last_events & (FD_READ | FD_ACCEPT)) + watch->pollfd.revents |= G_IO_IN; + + if (channel->last_events & FD_WRITE) + watch->pollfd.revents |= G_IO_OUT; + else + { + /* We have called WSAEnumNetworkEvents() above but it didn't + * set FD_WRITE. + */ + if (events.lNetworkEvents & FD_CONNECT) + { + if (events.iErrorCode[FD_CONNECT_BIT] == 0) + watch->pollfd.revents |= G_IO_OUT; + else + watch->pollfd.revents |= (G_IO_HUP | G_IO_ERR); + } + if (watch->pollfd.revents == 0 && (channel->last_events & (FD_CLOSE))) + watch->pollfd.revents |= G_IO_HUP; + } + + /* Regardless of WSAEnumNetworkEvents() result, if watching for + * writability, and if we have ever got a FD_WRITE event, and + * unless last write would have blocked, set G_IO_OUT. But never + * set both G_IO_OUT and G_IO_HUP. + */ + if (!(watch->pollfd.revents & G_IO_HUP) && + channel->ever_writable && + !channel->write_would_have_blocked && + (channel->event_mask & FD_WRITE)) + watch->pollfd.revents |= G_IO_OUT; + + if (channel->debug) + g_print ("\n revents={%s} retval={%s}\n", + condition_to_string (watch->pollfd.revents), + condition_to_string ((watch->pollfd.revents | buffer_condition) & watch->condition)); + + return ((watch->pollfd.revents | buffer_condition) & watch->condition); + + default: + g_assert_not_reached (); + abort (); + } +} + +static gboolean +g_io_win32_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GIOFunc func = (GIOFunc)callback; + GIOWin32Watch *watch = (GIOWin32Watch *)source; + GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel; + GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel); + + if (!func) + { + g_warning ("IO Watch dispatched without callback\n" + "You must call g_source_connect()."); + return FALSE; + } + + if (channel->debug) + g_print ("g_io_win32_dispatch: pollfd.revents=%s condition=%s result=%s\n", + condition_to_string (watch->pollfd.revents), + condition_to_string (watch->condition), + condition_to_string ((watch->pollfd.revents | buffer_condition) & watch->condition)); + + return (*func) (watch->channel, + (watch->pollfd.revents | buffer_condition) & watch->condition, + user_data); +} + +static void +g_io_win32_finalize (GSource *source) +{ + GIOWin32Watch *watch = (GIOWin32Watch *)source; + GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel; + + if (channel->debug) + g_print ("g_io_win32_finalize: source=%p channel=%p", source, channel); + + switch (channel->type) + { + case G_IO_WIN32_WINDOWS_MESSAGES: + if (channel->debug) + g_print (" MSG"); + break; + + case G_IO_WIN32_CONSOLE: + if (channel->debug) + g_print (" CON"); + break; + + case G_IO_WIN32_FILE_DESC: + if (channel->debug) + g_print (" FD thread=%#x", channel->thread_id); + break; + + case G_IO_WIN32_SOCKET: + if (channel->debug) + g_print (" SOCK sock=%d", channel->fd); + break; + + default: + g_assert_not_reached (); + abort (); + } + if (channel->debug) + g_print ("\n"); + g_io_channel_unref (watch->channel); +} + +GSourceFuncs g_io_watch_funcs = { + g_io_win32_prepare, + g_io_win32_check, + g_io_win32_dispatch, + g_io_win32_finalize +}; + +static GIOStatus +g_io_win32_msg_read (GIOChannel *channel, + gchar *buf, + gsize count, + gsize *bytes_read, + GError **err) +{ + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + MSG msg; /* In case of alignment problems */ + + if (count < sizeof (MSG)) + { + g_set_error_literal (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL, + "Incorrect message size"); /* Informative enough error message? */ + return G_IO_STATUS_ERROR; + } + if (win32_channel->debug) + g_print ("g_io_win32_msg_read: channel=%p hwnd=%p\n", + channel, win32_channel->hwnd); + if (!PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_REMOVE)) + return G_IO_STATUS_AGAIN; + memmove (buf, &msg, sizeof (MSG)); *bytes_read = sizeof (MSG); - return G_IO_ERROR_NONE; + + return G_IO_STATUS_NORMAL; } -static GIOError -g_io_win32_msg_write (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written) +static GIOStatus +g_io_win32_msg_write (GIOChannel *channel, + const gchar *buf, + gsize count, + gsize *bytes_written, + GError **err) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; MSG msg; if (count != sizeof (MSG)) - return G_IO_ERROR_INVAL; + { + g_set_error_literal (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL, + "Incorrect message size"); /* Informative enough error message? */ + return G_IO_STATUS_ERROR; + } /* 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; - + { + gchar *emsg = g_win32_error_message (GetLastError ()); + + g_set_error_literal (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, emsg); + g_free (emsg); + + return G_IO_STATUS_ERROR; + } + *bytes_written = sizeof (MSG); - return G_IO_ERROR_NONE; -} -static GIOError -g_io_win32_no_seek (GIOChannel *channel, - gint offset, - GSeekType type) -{ - return G_IO_ERROR_UNKNOWN; + return G_IO_STATUS_NORMAL; } -static void -g_io_win32_msg_close (GIOChannel *channel) +static GIOStatus +g_io_win32_msg_close (GIOChannel *channel, + GError **err) { /* Nothing to be done. Or should we set hwnd to some invalid value? */ + + return G_IO_STATUS_NORMAL; } static void g_io_win32_free (GIOChannel *channel) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) 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); - } + if (win32_channel->debug) + g_print ("g_io_win32_free channel=%p fd=%d\n", channel, win32_channel->fd); + + DeleteCriticalSection (&win32_channel->mutex); + + if (win32_channel->data_avail_event) + if (!CloseHandle (win32_channel->data_avail_event)) + if (win32_channel->debug) + { + gchar *emsg = g_win32_error_message (GetLastError ()); + + g_print (" CloseHandle(%p) failed: %s\n", + win32_channel->data_avail_event, emsg); + g_free (emsg); + } g_free (win32_channel->buffer); + + if (win32_channel->space_avail_event) + if (!CloseHandle (win32_channel->space_avail_event)) + if (win32_channel->debug) + { + gchar *emsg = g_win32_error_message (GetLastError ()); + + g_print (" CloseHandle(%p) failed: %s\n", + win32_channel->space_avail_event, emsg); + g_free (emsg); + } + + if (win32_channel->type == G_IO_WIN32_SOCKET && + win32_channel->fd != -1) + if (WSAEventSelect (win32_channel->fd, NULL, 0) == SOCKET_ERROR) + if (win32_channel->debug) + { + gchar *emsg = g_win32_error_message (WSAGetLastError ()); + + g_print (" WSAEventSelect(%d,NULL,{}) failed: %s\n", + win32_channel->fd, emsg); + g_free (emsg); + } + + if (win32_channel->event) + if (!WSACloseEvent (win32_channel->event)) + if (win32_channel->debug) + { + gchar *emsg = g_win32_error_message (WSAGetLastError ()); + + g_print (" WSACloseEvent(%p) failed: %s\n", + win32_channel->event, emsg); + g_free (emsg); + } + g_free (win32_channel); } -static guint -g_io_win32_msg_add_watch (GIOChannel *channel, - gint priority, - GIOCondition condition, - GIOFunc func, - gpointer user_data, - GDestroyNotify notify) +static GSource * +g_io_win32_msg_create_watch (GIOChannel *channel, + GIOCondition condition) { - GIOWin32Watch *watch = g_new (GIOWin32Watch, 1); + GIOWin32Watch *watch; + GSource *source; + + source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch)); + g_source_set_name (source, "GIOChannel (Win32)"); + watch = (GIOWin32Watch *)source; watch->channel = channel; g_io_channel_ref (channel); - watch->callback = func; watch->condition = condition; - watch->pollfd.fd = G_WIN32_MSG_HANDLE; + watch->pollfd.fd = (gintptr) G_WIN32_MSG_HANDLE; watch->pollfd.events = condition; - g_main_add_poll (&watch->pollfd, priority); + g_source_add_poll (source, &watch->pollfd); - return g_source_add (priority, TRUE, &win32_watch_funcs, - watch, user_data, notify); + return source; } -static GIOError -g_io_win32_fd_read (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_read) +static GIOStatus +g_io_win32_fd_and_console_read (GIOChannel *channel, + gchar *buf, + gsize count, + gsize *bytes_read, + GError **err) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; gint result; - GIOError error; if (win32_channel->debug) - g_print ("g_io_win32_fd_read: fd:%d count:%d\n", + g_print ("g_io_win32_fd_read: fd=%d count=%" G_GSIZE_FORMAT "\n", win32_channel->fd, count); 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; - } + return buffer_read (win32_channel, buf, count, bytes_read, err); } result = read (win32_channel->fd, buf, count); + if (win32_channel->debug) + g_print ("g_io_win32_fd_read: read() => %d\n", result); + if (result < 0) { *bytes_read = 0; - if (errno == EINVAL) - return G_IO_ERROR_INVAL; - else - return G_IO_ERROR_UNKNOWN; - } - else - { - *bytes_read = result; - return G_IO_ERROR_NONE; + + switch (errno) + { +#ifdef EAGAIN + case EAGAIN: + return G_IO_STATUS_AGAIN; +#endif + default: + g_set_error_literal (err, G_IO_CHANNEL_ERROR, + g_io_channel_error_from_errno (errno), + g_strerror (errno)); + return G_IO_STATUS_ERROR; + } } + + *bytes_read = result; + + return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF; } -static GIOError -g_io_win32_fd_write (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written) +static GIOStatus +g_io_win32_fd_and_console_write (GIOChannel *channel, + const gchar *buf, + gsize count, + gsize *bytes_written, + GError **err) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; gint result; + + if (win32_channel->thread_id) + { + return buffer_write (win32_channel, buf, count, bytes_written, err); + } result = write (win32_channel->fd, buf, count); if (win32_channel->debug) - g_print ("g_io_win32_fd_write: fd:%d count:%d = %d\n", + g_print ("g_io_win32_fd_write: fd=%d count=%" G_GSIZE_FORMAT " => %d\n", win32_channel->fd, count, result); - + if (result < 0) { *bytes_written = 0; + switch (errno) - { - case EINVAL: - return G_IO_ERROR_INVAL; + { +#ifdef EAGAIN case EAGAIN: - return G_IO_ERROR_AGAIN; + return G_IO_STATUS_AGAIN; +#endif default: - return G_IO_ERROR_UNKNOWN; - } - } - else - { - *bytes_written = result; - return G_IO_ERROR_NONE; + g_set_error_literal (err, G_IO_CHANNEL_ERROR, + g_io_channel_error_from_errno (errno), + g_strerror (errno)); + return G_IO_STATUS_ERROR; + } } + + *bytes_written = result; + + return G_IO_STATUS_NORMAL; } -static GIOError +static GIOStatus g_io_win32_fd_seek (GIOChannel *channel, - gint offset, - GSeekType type) + gint64 offset, + GSeekType type, + GError **err) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; int whence; + off_t tmp_offset; off_t result; switch (type) @@ -612,248 +1293,867 @@ g_io_win32_fd_seek (GIOChannel *channel, whence = SEEK_END; break; default: - g_warning ("g_io_win32_fd_seek: unknown seek type"); - return G_IO_ERROR_UNKNOWN; + whence = -1; /* Keep the compiler quiet */ + g_assert_not_reached (); + abort (); + } + + tmp_offset = offset; + if (tmp_offset != offset) + { + g_set_error_literal (err, G_IO_CHANNEL_ERROR, + g_io_channel_error_from_errno (EINVAL), + g_strerror (EINVAL)); + return G_IO_STATUS_ERROR; } - result = lseek (win32_channel->fd, offset, whence); + result = lseek (win32_channel->fd, tmp_offset, whence); if (result < 0) { - switch (errno) - { - case EINVAL: - return G_IO_ERROR_INVAL; - default: - return G_IO_ERROR_UNKNOWN; - } + g_set_error_literal (err, G_IO_CHANNEL_ERROR, + g_io_channel_error_from_errno (errno), + g_strerror (errno)); + return G_IO_STATUS_ERROR; + } + + return G_IO_STATUS_NORMAL; +} + +static GIOStatus +g_io_win32_fd_close (GIOChannel *channel, + GError **err) +{ + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + + if (win32_channel->debug) + g_print ("g_io_win32_fd_close: thread=%#x: fd=%d\n", + win32_channel->thread_id, + win32_channel->fd); + EnterCriticalSection (&win32_channel->mutex); + if (win32_channel->running) + { + if (win32_channel->debug) + g_print ("thread %#x: running, marking fd %d for later close\n", + win32_channel->thread_id, win32_channel->fd); + win32_channel->running = FALSE; + win32_channel->needs_close = TRUE; + if (win32_channel->direction == 0) + SetEvent (win32_channel->data_avail_event); + else + SetEvent (win32_channel->space_avail_event); } else - return G_IO_ERROR_NONE; + { + if (win32_channel->debug) + g_print ("closing fd %d\n", win32_channel->fd); + close (win32_channel->fd); + if (win32_channel->debug) + g_print ("closed fd %d, setting to -1\n", + win32_channel->fd); + win32_channel->fd = -1; + } + LeaveCriticalSection (&win32_channel->mutex); + + /* FIXME error detection? */ + + return G_IO_STATUS_NORMAL; } -static void -g_io_win32_fd_close (GIOChannel *channel) +static GSource * +g_io_win32_fd_create_watch (GIOChannel *channel, + GIOCondition condition) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch)); + GIOWin32Watch *watch = (GIOWin32Watch *)source; + + watch->channel = channel; + g_io_channel_ref (channel); + + watch->condition = condition; + + if (win32_channel->data_avail_event == NULL) + create_events (win32_channel); + + watch->pollfd.fd = (gintptr) win32_channel->data_avail_event; + watch->pollfd.events = condition; - close (win32_channel->fd); - return; + if (win32_channel->debug) + g_print ("g_io_win32_fd_create_watch: channel=%p fd=%d condition={%s} event=%p\n", + channel, win32_channel->fd, + condition_to_string (condition), (HANDLE) watch->pollfd.fd); + + EnterCriticalSection (&win32_channel->mutex); + if (win32_channel->thread_id == 0) + { + if (condition & G_IO_IN) + create_thread (win32_channel, condition, read_thread); + else if (condition & G_IO_OUT) + create_thread (win32_channel, condition, write_thread); + } + + g_source_add_poll (source, &watch->pollfd); + LeaveCriticalSection (&win32_channel->mutex); + + return source; } -static int -fd_reader (int fd, - guchar *buf, - int len) +static GIOStatus +g_io_win32_console_close (GIOChannel *channel, + GError **err) { - return read (fd, buf, len); + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + + if (close (win32_channel->fd) < 0) + { + g_set_error_literal (err, G_IO_CHANNEL_ERROR, + g_io_channel_error_from_errno (errno), + g_strerror (errno)); + return G_IO_STATUS_ERROR; + } + + return G_IO_STATUS_NORMAL; } -static guint -g_io_win32_fd_add_watch (GIOChannel *channel, - gint priority, - GIOCondition condition, - GIOFunc func, - gpointer user_data, - GDestroyNotify notify) +static GSource * +g_io_win32_console_create_watch (GIOChannel *channel, + GIOCondition condition) { - return g_io_win32_add_watch (channel, priority, condition, - func, user_data, notify, fd_reader); + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch)); + GIOWin32Watch *watch = (GIOWin32Watch *)source; + + watch->channel = channel; + g_io_channel_ref (channel); + + watch->condition = condition; + + watch->pollfd.fd = _get_osfhandle (win32_channel->fd); + watch->pollfd.events = condition; + + g_source_add_poll (source, &watch->pollfd); + + return source; } -static GIOError +static GIOStatus g_io_win32_sock_read (GIOChannel *channel, gchar *buf, - guint count, - guint *bytes_read) + gsize count, + gsize *bytes_read, + GError **err) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + 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; - } - } + GIOChannelError error; + int winsock_error; + + if (win32_channel->debug) + g_print ("g_io_win32_sock_read: channel=%p sock=%d count=%" G_GSIZE_FORMAT, + channel, win32_channel->fd, count); result = recv (win32_channel->fd, buf, count, 0); + if (result == SOCKET_ERROR) + winsock_error = WSAGetLastError (); - if (result < 0) + if (win32_channel->debug) + g_print (" recv=%d", result); + + if (result == SOCKET_ERROR) { + gchar *emsg = g_win32_error_message (winsock_error); + + if (win32_channel->debug) + g_print (" %s\n", emsg); + *bytes_read = 0; - return G_IO_ERROR_UNKNOWN; + + switch (winsock_error) + { + case WSAEINVAL: + error = G_IO_CHANNEL_ERROR_INVAL; + break; + case WSAEWOULDBLOCK: + g_free (emsg); + return G_IO_STATUS_AGAIN; + default: + error = G_IO_CHANNEL_ERROR_FAILED; + break; + } + g_set_error_literal (err, G_IO_CHANNEL_ERROR, error, emsg); + g_free (emsg); + + return G_IO_STATUS_ERROR; } else { + if (win32_channel->debug) + g_print ("\n"); *bytes_read = result; - return G_IO_ERROR_NONE; + if (result == 0) + return G_IO_STATUS_EOF; + else + return G_IO_STATUS_NORMAL; } } -static GIOError -g_io_win32_sock_write (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written) +static GIOStatus +g_io_win32_sock_write (GIOChannel *channel, + const gchar *buf, + gsize count, + gsize *bytes_written, + GError **err) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; gint result; + GIOChannelError error; + int winsock_error; + if (win32_channel->debug) + g_print ("g_io_win32_sock_write: channel=%p sock=%d count=%" G_GSIZE_FORMAT, + channel, win32_channel->fd, count); + result = send (win32_channel->fd, buf, count, 0); + if (result == SOCKET_ERROR) + winsock_error = WSAGetLastError (); + + if (win32_channel->debug) + g_print (" send=%d", result); if (result == SOCKET_ERROR) { + gchar *emsg = g_win32_error_message (winsock_error); + + if (win32_channel->debug) + g_print (" %s\n", emsg); + *bytes_written = 0; - switch (WSAGetLastError ()) + + switch (winsock_error) { case WSAEINVAL: - return G_IO_ERROR_INVAL; + error = G_IO_CHANNEL_ERROR_INVAL; + break; case WSAEWOULDBLOCK: - case WSAEINTR: - return G_IO_ERROR_AGAIN; + win32_channel->write_would_have_blocked = TRUE; + win32_channel->last_events = 0; + g_free (emsg); + return G_IO_STATUS_AGAIN; default: - return G_IO_ERROR_UNKNOWN; + error = G_IO_CHANNEL_ERROR_FAILED; + break; } + g_set_error_literal (err, G_IO_CHANNEL_ERROR, error, emsg); + g_free (emsg); + + return G_IO_STATUS_ERROR; } else { + if (win32_channel->debug) + g_print ("\n"); *bytes_written = result; - return G_IO_ERROR_NONE; + win32_channel->write_would_have_blocked = FALSE; + + return G_IO_STATUS_NORMAL; } } -static void -g_io_win32_sock_close (GIOChannel *channel) +static GIOStatus +g_io_win32_sock_close (GIOChannel *channel, + GError **err) +{ + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + + if (win32_channel->fd != -1) + { + if (win32_channel->debug) + g_print ("g_io_win32_sock_close: channel=%p sock=%d\n", + channel, win32_channel->fd); + + closesocket (win32_channel->fd); + win32_channel->fd = -1; + } + + /* FIXME error detection? */ + + return G_IO_STATUS_NORMAL; +} + +static GSource * +g_io_win32_sock_create_watch (GIOChannel *channel, + GIOCondition condition) +{ + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch)); + GIOWin32Watch *watch = (GIOWin32Watch *)source; + + watch->channel = channel; + g_io_channel_ref (channel); + + watch->condition = condition; + + if (win32_channel->event == 0) + win32_channel->event = WSACreateEvent (); + + watch->pollfd.fd = (gintptr) win32_channel->event; + watch->pollfd.events = condition; + + if (win32_channel->debug) + g_print ("g_io_win32_sock_create_watch: channel=%p sock=%d event=%p condition={%s}\n", + channel, win32_channel->fd, (HANDLE) watch->pollfd.fd, + condition_to_string (watch->condition)); + + g_source_add_poll (source, &watch->pollfd); + + return source; +} + +GIOChannel * +g_io_channel_new_file (const gchar *filename, + const gchar *mode, + GError **error) +{ + int fid, flags, pmode; + GIOChannel *channel; + + enum { /* Cheesy hack */ + MODE_R = 1 << 0, + MODE_W = 1 << 1, + MODE_A = 1 << 2, + MODE_PLUS = 1 << 3, + }; + int mode_num; + + g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (mode != NULL, NULL); + g_return_val_if_fail ((error == NULL) || (*error == NULL), NULL); + + switch (mode[0]) + { + case 'r': + mode_num = MODE_R; + break; + case 'w': + mode_num = MODE_W; + break; + case 'a': + mode_num = MODE_A; + break; + default: + g_warning ("Invalid GIOFileMode %s.", mode); + return NULL; + } + + switch (mode[1]) + { + case '\0': + break; + case '+': + if (mode[2] == '\0') + { + mode_num |= MODE_PLUS; + break; + } + /* Fall through */ + default: + g_warning ("Invalid GIOFileMode %s.", mode); + return NULL; + } + + switch (mode_num) + { + case MODE_R: + flags = O_RDONLY; + pmode = _S_IREAD; + break; + case MODE_W: + flags = O_WRONLY | O_TRUNC | O_CREAT; + pmode = _S_IWRITE; + break; + case MODE_A: + flags = O_WRONLY | O_APPEND | O_CREAT; + pmode = _S_IWRITE; + break; + case MODE_R | MODE_PLUS: + flags = O_RDWR; + pmode = _S_IREAD | _S_IWRITE; + break; + case MODE_W | MODE_PLUS: + flags = O_RDWR | O_TRUNC | O_CREAT; + pmode = _S_IREAD | _S_IWRITE; + break; + case MODE_A | MODE_PLUS: + flags = O_RDWR | O_APPEND | O_CREAT; + pmode = _S_IREAD | _S_IWRITE; + break; + default: + g_assert_not_reached (); + abort (); + } + + /* always open 'untranslated' */ + fid = g_open (filename, flags | _O_BINARY, pmode); + + if (g_io_win32_get_debug_flag ()) + { + g_print ("g_io_channel_win32_new_file: open(\"%s\",", filename); + g_win32_print_access_mode (flags|_O_BINARY); + g_print (",%#o)=%d\n", pmode, fid); + } + + if (fid < 0) + { + g_set_error_literal (error, G_FILE_ERROR, + g_file_error_from_errno (errno), + g_strerror (errno)); + return (GIOChannel *)NULL; + } + + channel = g_io_channel_win32_new_fd (fid); + + /* XXX: move this to g_io_channel_win32_new_fd () */ + channel->close_on_unref = TRUE; + channel->is_seekable = TRUE; + + /* g_io_channel_win32_new_fd sets is_readable and is_writeable to + * correspond to actual readability/writeability. Set to FALSE those + * that mode doesn't allow + */ + switch (mode_num) + { + case MODE_R: + channel->is_writeable = FALSE; + break; + case MODE_W: + case MODE_A: + channel->is_readable = FALSE; + break; + case MODE_R | MODE_PLUS: + case MODE_W | MODE_PLUS: + case MODE_A | MODE_PLUS: + break; + default: + g_assert_not_reached (); + abort (); + } + + return channel; +} + +#if !defined (_WIN64) + +#undef g_io_channel_new_file + +/* Binary compatibility version. Not for newly compiled code. */ + +GIOChannel * +g_io_channel_new_file (const gchar *filename, + const gchar *mode, + GError **error) +{ + gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error); + GIOChannel *retval; + + if (utf8_filename == NULL) + return NULL; + + retval = g_io_channel_new_file_utf8 (utf8_filename, mode, error); + + g_free (utf8_filename); + + return retval; +} + +#endif + +static GIOStatus +g_io_win32_unimpl_set_flags (GIOChannel *channel, + GIOFlags flags, + GError **err) +{ + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + + if (win32_channel->debug) + { + g_print ("g_io_win32_unimpl_set_flags: "); + g_win32_print_gioflags (flags); + g_print ("\n"); + } + + g_set_error_literal (err, G_IO_CHANNEL_ERROR, + G_IO_CHANNEL_ERROR_FAILED, + "Not implemented on Win32"); + + return G_IO_STATUS_ERROR; +} + +static GIOFlags +g_io_win32_fd_get_flags_internal (GIOChannel *channel, + struct _stati64 *st) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + gchar c; + DWORD count; + + if (st->st_mode & _S_IFIFO) + { + channel->is_readable = + (PeekNamedPipe ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL, NULL) != 0) || GetLastError () == ERROR_BROKEN_PIPE; + channel->is_writeable = + (WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0); + channel->is_seekable = FALSE; + } + else + { + channel->is_readable = + (ReadFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0); + channel->is_writeable = + (WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0); + channel->is_seekable = TRUE; + } + + /* XXX: G_IO_FLAG_APPEND */ + /* XXX: G_IO_FLAG_NONBLOCK */ + + return 0; +} + +static GIOFlags +g_io_win32_fd_get_flags (GIOChannel *channel) +{ + struct _stati64 st; + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + + g_return_val_if_fail (win32_channel != NULL, 0); + g_return_val_if_fail (win32_channel->type == G_IO_WIN32_FILE_DESC, 0); + + if (0 == _fstati64 (win32_channel->fd, &st)) + return g_io_win32_fd_get_flags_internal (channel, &st); + else + return 0; +} + +static GIOFlags +g_io_win32_console_get_flags_internal (GIOChannel *channel) +{ + GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + HANDLE handle = (HANDLE) _get_osfhandle (win32_channel->fd); + gchar c; + DWORD count; + INPUT_RECORD record; + + channel->is_readable = PeekConsoleInput (handle, &record, 1, &count); + channel->is_writeable = WriteFile (handle, &c, 0, &count, NULL); + channel->is_seekable = FALSE; + + return 0; +} + +static GIOFlags +g_io_win32_console_get_flags (GIOChannel *channel) +{ + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; - closesocket (win32_channel->fd); + g_return_val_if_fail (win32_channel != NULL, 0); + g_return_val_if_fail (win32_channel->type == G_IO_WIN32_CONSOLE, 0); + + return g_io_win32_console_get_flags_internal (channel); +} + +static GIOFlags +g_io_win32_msg_get_flags (GIOChannel *channel) +{ + return 0; } -static int -sock_reader (int fd, - guchar *buf, - int len) +static GIOStatus +g_io_win32_sock_set_flags (GIOChannel *channel, + GIOFlags flags, + GError **err) { - return recv (fd, buf, len, 0); + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; + u_long arg; + + if (win32_channel->debug) + { + g_print ("g_io_win32_sock_set_flags: "); + g_win32_print_gioflags (flags); + g_print ("\n"); + } + + if (flags & G_IO_FLAG_NONBLOCK) + { + arg = 1; + if (ioctlsocket (win32_channel->fd, FIONBIO, &arg) == SOCKET_ERROR) + { + gchar *emsg = g_win32_error_message (WSAGetLastError ()); + + g_set_error_literal (err, G_IO_CHANNEL_ERROR, + G_IO_CHANNEL_ERROR_FAILED, + emsg); + g_free (emsg); + + return G_IO_STATUS_ERROR; + } + } + else + { + arg = 0; + if (ioctlsocket (win32_channel->fd, FIONBIO, &arg) == SOCKET_ERROR) + { + gchar *emsg = g_win32_error_message (WSAGetLastError ()); + + g_set_error_literal (err, G_IO_CHANNEL_ERROR, + G_IO_CHANNEL_ERROR_FAILED, + emsg); + g_free (emsg); + + return G_IO_STATUS_ERROR; + } + } + + return G_IO_STATUS_NORMAL; } -static guint -g_io_win32_sock_add_watch (GIOChannel *channel, - gint priority, - GIOCondition condition, - GIOFunc func, - gpointer user_data, - GDestroyNotify notify) +static GIOFlags +g_io_win32_sock_get_flags (GIOChannel *channel) { - return g_io_win32_add_watch (channel, priority, condition, - func, user_data, notify, sock_reader); + /* Could we do something here? */ + return 0; } static GIOFuncs win32_channel_msg_funcs = { g_io_win32_msg_read, g_io_win32_msg_write, - g_io_win32_no_seek, + NULL, g_io_win32_msg_close, - g_io_win32_msg_add_watch, - g_io_win32_free + g_io_win32_msg_create_watch, + g_io_win32_free, + g_io_win32_unimpl_set_flags, + g_io_win32_msg_get_flags, }; static GIOFuncs win32_channel_fd_funcs = { - g_io_win32_fd_read, - g_io_win32_fd_write, + g_io_win32_fd_and_console_read, + g_io_win32_fd_and_console_write, g_io_win32_fd_seek, g_io_win32_fd_close, - g_io_win32_fd_add_watch, - g_io_win32_free + g_io_win32_fd_create_watch, + g_io_win32_free, + g_io_win32_unimpl_set_flags, + g_io_win32_fd_get_flags, +}; + +static GIOFuncs win32_channel_console_funcs = { + g_io_win32_fd_and_console_read, + g_io_win32_fd_and_console_write, + NULL, + g_io_win32_console_close, + g_io_win32_console_create_watch, + g_io_win32_free, + g_io_win32_unimpl_set_flags, + g_io_win32_console_get_flags, }; static GIOFuncs win32_channel_sock_funcs = { g_io_win32_sock_read, g_io_win32_sock_write, - g_io_win32_no_seek, + NULL, g_io_win32_sock_close, - g_io_win32_sock_add_watch, - g_io_win32_free + g_io_win32_sock_create_watch, + g_io_win32_free, + g_io_win32_sock_set_flags, + g_io_win32_sock_get_flags, }; +/** + * g_io_channel_win32_new_messages: + * @hwnd: a window handle. + * + * Creates a new #GIOChannel given a window handle on Windows. + * + * This function creates a #GIOChannel that can be used to poll for + * Windows messages for the window in question. + * + * Returns: a new #GIOChannel. + **/ GIOChannel * +#if GLIB_SIZEOF_VOID_P == 8 +g_io_channel_win32_new_messages (gsize hwnd) +#else g_io_channel_win32_new_messages (guint hwnd) +#endif { GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1); - GIOChannel *channel = (GIOChannel *) win32_channel; + GIOChannel *channel = (GIOChannel *)win32_channel; g_io_channel_init (channel); g_io_channel_win32_init (win32_channel); + if (win32_channel->debug) + g_print ("g_io_channel_win32_new_messages: channel=%p hwnd=%p\n", + channel, (HWND) hwnd); channel->funcs = &win32_channel_msg_funcs; - win32_channel->type = G_IO_WINDOWS_MESSAGES; + win32_channel->type = G_IO_WIN32_WINDOWS_MESSAGES; win32_channel->hwnd = (HWND) hwnd; + /* XXX: check this. */ + channel->is_readable = IsWindow (win32_channel->hwnd); + channel->is_writeable = IsWindow (win32_channel->hwnd); + + channel->is_seekable = FALSE; + return channel; } -GIOChannel * -g_io_channel_win32_new_fd (gint fd) +static GIOChannel * +g_io_channel_win32_new_fd_internal (gint fd, + struct _stati64 *st) { 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; - } win32_channel = g_new (GIOWin32Channel, 1); - channel = (GIOChannel *) win32_channel; + channel = (GIOChannel *)win32_channel; g_io_channel_init (channel); g_io_channel_win32_init (win32_channel); - channel->funcs = &win32_channel_fd_funcs; - win32_channel->type = G_IO_FILE_DESC; + win32_channel->fd = fd; + if (win32_channel->debug) + g_print ("g_io_channel_win32_new_fd: channel=%p fd=%u\n", + channel, fd); + + if (st->st_mode & _S_IFCHR) /* console */ + { + channel->funcs = &win32_channel_console_funcs; + win32_channel->type = G_IO_WIN32_CONSOLE; + g_io_win32_console_get_flags_internal (channel); + } + else + { + channel->funcs = &win32_channel_fd_funcs; + win32_channel->type = G_IO_WIN32_FILE_DESC; + g_io_win32_fd_get_flags_internal (channel, st); + } + return channel; } +/** + * g_io_channel_win32_new_fd: + * @fd: a C library file descriptor. + * + * Creates a new #GIOChannel given a file descriptor on Windows. This + * works for file descriptors from the C runtime. + * + * This function works for file descriptors as returned by the open(), + * creat(), pipe() and fileno() calls in the Microsoft C runtime. In + * order to meaningfully use this function your code should use the + * same C runtime as GLib uses, which is msvcrt.dll. Note that in + * current Microsoft compilers it is near impossible to convince it to + * build code that would use msvcrt.dll. The last Microsoft compiler + * version that supported using msvcrt.dll as the C runtime was version + * 6. The GNU compiler and toolchain for Windows, also known as Mingw, + * fully supports msvcrt.dll. + * + * If you have created a #GIOChannel for a file descriptor and started + * watching (polling) it, you shouldn't call read() on the file + * descriptor. This is because adding polling for a file descriptor is + * implemented in GLib on Windows by starting a thread that sits + * blocked in a read() from the file descriptor most of the time. All + * reads from the file descriptor should be done by this internal GLib + * thread. Your code should call only g_io_channel_read(). + * + * This function is available only in GLib on Windows. + * + * Returns: a new #GIOChannel. + **/ +GIOChannel * +g_io_channel_win32_new_fd (gint fd) +{ + struct _stati64 st; + + if (_fstati64 (fd, &st) == -1) + { + g_warning ("g_io_channel_win32_new_fd: %d isn't an open file descriptor in the C library GLib uses.", fd); + return NULL; + } + + return g_io_channel_win32_new_fd_internal (fd, &st); +} + gint g_io_channel_win32_get_fd (GIOChannel *channel) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; return win32_channel->fd; } +/** + * g_io_channel_win32_new_socket: + * @socket: a Winsock socket + * + * Creates a new #GIOChannel given a socket on Windows. + * + * This function works for sockets created by Winsock. It's available + * only in GLib on Windows. + * + * Polling a #GSource created to watch a channel for a socket puts the + * socket in non-blocking mode. This is a side-effect of the + * implementation and unavoidable. + * + * Returns: a new #GIOChannel + **/ GIOChannel * -g_io_channel_win32_new_stream_socket (int socket) +g_io_channel_win32_new_socket (int socket) { GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1); - GIOChannel *channel = (GIOChannel *) win32_channel; + GIOChannel *channel = (GIOChannel *)win32_channel; g_io_channel_init (channel); g_io_channel_win32_init (win32_channel); + if (win32_channel->debug) + g_print ("g_io_channel_win32_new_socket: channel=%p sock=%d\n", + channel, socket); channel->funcs = &win32_channel_sock_funcs; - win32_channel->type = G_IO_STREAM_SOCKET; + win32_channel->type = G_IO_WIN32_SOCKET; win32_channel->fd = socket; + channel->is_readable = TRUE; + channel->is_writeable = TRUE; + channel->is_seekable = FALSE; + return channel; } GIOChannel * g_io_channel_unix_new (gint fd) { - return g_io_channel_win32_new_fd (fd); + gboolean is_fd, is_socket; + struct _stati64 st; + int optval, optlen; + + is_fd = (_fstati64 (fd, &st) == 0); + + optlen = sizeof (optval); + is_socket = (getsockopt (fd, SOL_SOCKET, SO_TYPE, (char *) &optval, &optlen) != SOCKET_ERROR); + + if (is_fd && is_socket) + g_warning ("g_io_channel_unix_new: %d is both a file descriptor and a socket. File descriptor interpretation assumed. To avoid ambiguity, call either g_io_channel_win32_new_fd() or g_io_channel_win32_new_socket() instead.", fd); + + if (is_fd) + return g_io_channel_win32_new_fd_internal (fd, &st); + + if (is_socket) + return g_io_channel_win32_new_socket(fd); + + g_warning ("g_io_channel_unix_new: %d is neither a file descriptor or a socket.", fd); + + return NULL; } gint @@ -866,104 +2166,78 @@ void g_io_channel_win32_set_debug (GIOChannel *channel, gboolean flag) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; win32_channel->debug = flag; } gint -g_io_channel_win32_poll (GIOChannel **channels, - gint n_channels, - GIOCondition condition, - gint timeout) +g_io_channel_win32_poll (GPollFD *fds, + gint n_fds, + gint timeout) { - GPollFD *pollfd; - GIOWin32Channel *win32_channel; - int i; - int result; - gboolean debug = FALSE; + g_return_val_if_fail (n_fds >= 0, 0); - g_return_val_if_fail (n_channels >= 0, 0); + return g_poll (fds, n_fds, timeout); +} - pollfd = g_new (GPollFD, n_channels); +void +g_io_channel_win32_make_pollfd (GIOChannel *channel, + GIOCondition condition, + GPollFD *fd) +{ + GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel; - for (i = 0; i < n_channels; i++) + switch (win32_channel->type) { - win32_channel = (GIOWin32Channel *) channels[i]; - debug |= win32_channel->debug; - pollfd[i].fd = (gint) win32_channel->data_avail_event; - pollfd[i].events = condition; - } + case G_IO_WIN32_FILE_DESC: + if (win32_channel->data_avail_event == NULL) + create_events (win32_channel); - if (debug) - { - g_print ("g_io_channel_win32_poll: "); - for (i = 0; i < n_channels; i++) + fd->fd = (gintptr) win32_channel->data_avail_event; + + if (win32_channel->thread_id == 0) { - win32_channel = (GIOWin32Channel *) channels[i]; - g_print ("fd:%d event:%#x ", win32_channel->fd, pollfd[i].fd); + /* Is it meaningful for a file descriptor to be polled for + * both IN and OUT? For what kind of file descriptor would + * that be? Doesn't seem to make sense, in practise the file + * descriptors handled here are always read or write ends of + * pipes surely, and thus unidirectional. + */ + if (condition & G_IO_IN) + create_thread (win32_channel, condition, read_thread); + else if (condition & G_IO_OUT) + create_thread (win32_channel, condition, write_thread); } - g_print ("condition:%s%s%s%s timeout:%d\n", - (condition & G_IO_ERR) ? " ERR" : "", - (condition & G_IO_HUP) ? " HUP" : "", - (condition & G_IO_IN) ? " IN" : "", - (condition & G_IO_PRI) ? " PRI" : "", - timeout); - } - - result = (*g_main_win32_get_poll_func ()) (pollfd, n_channels, timeout); - - if (debug) - g_print ("g_io_channel_win32_poll: done:%d\n", result); + break; - g_free (pollfd); + case G_IO_WIN32_CONSOLE: + fd->fd = _get_osfhandle (win32_channel->fd); + break; - return result; -} + case G_IO_WIN32_SOCKET: + fd->fd = (gintptr) WSACreateEvent (); + break; + + case G_IO_WIN32_WINDOWS_MESSAGES: + fd->fd = G_WIN32_MSG_HANDLE; + break; -gint -g_io_channel_win32_wait_for_condition (GIOChannel *channel, - GIOCondition condition, - gint timeout) -{ - return g_io_channel_win32_poll (&channel, 1, condition, timeout); + default: + g_assert_not_reached (); + abort (); + } + + fd->events = condition; } -/* 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; +#ifndef _WIN64 +/* Binary compatibility */ 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_win32_new_fd (fd); -} - -void -g_io_channel_win32_pipe_request_wakeups (GIOChannel *channel, - guint peer, - int peer_fd) -{ - /* Nothing needed now */ -} - -void -g_io_channel_win32_pipe_readable (gint fd, - guint offset) +g_io_channel_win32_new_stream_socket (int socket) { - /* Nothing needed now */ + return g_io_channel_win32_new_socket (socket); } +#endif