* giowin32.c: IO Channels for Win32.
* Copyright 1998 Owen Taylor and Tor Lillqvist
* Copyright 1999-2000 Tor Lillqvist and Craig Setera
- * Copyright 2001 Andrew Lanoix
+ * 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
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
-/* Define this to get (very) verbose logging of all channels */
-/* #define G_IO_WIN32_DEBUG */
+#include "config.h"
#include "glib.h"
#include <stdlib.h>
+#include <winsock2.h>
#include <windows.h>
-#include <winsock.h> /* Not everybody has winsock2 */
+#include <conio.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
#include <errno.h>
#include <sys/stat.h>
+#include "gstdio.h"
#include "glibintl.h"
+#include "galias.h"
+
typedef struct _GIOWin32Channel GIOWin32Channel;
typedef struct _GIOWin32Watch GIOWin32Watch;
typedef enum {
G_IO_WIN32_WINDOWS_MESSAGES, /* Windows messages */
G_IO_WIN32_FILE_DESC, /* Unix-like file descriptors from
- * _open() or _pipe(). Read with read().
+ * _open() or _pipe(), except for console IO.
* Have to create separate thread to read.
*/
- G_IO_WIN32_SOCKET /* Sockets. A separate thread is blocked
- * in select() most of the time.
- */
+ G_IO_WIN32_CONSOLE, /* Console IO (usually stdin, stdout, stderr) */
+ G_IO_WIN32_SOCKET /* Sockets. No separate thread */
} GIOWin32ChannelType;
struct _GIOWin32Channel {
gboolean debug;
- CRITICAL_SECTION mutex;
-
/* This is used by G_IO_WIN32_WINDOWS_MESSAGES channels */
HWND hwnd; /* handle of window, or NULL */
- /* Following fields are used by both fd and socket channels. */
+ /* Following fields are used by fd channels. */
+ CRITICAL_SECTION mutex;
+
+ int direction; /* 0 means we read from it,
+ * 1 means we write to it.
+ */
+
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 thread_handle;
HANDLE data_avail_event;
gushort revents;
HANDLE space_avail_event;
/* Following fields used by socket channels */
- GSList *watches;
- HANDLE data_avail_noticed_event;
+ int event_mask;
+ int last_events;
+ int event;
+ gboolean write_would_have_blocked;
};
#define LOCK(mutex) EnterCriticalSection (&mutex)
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_WRITEABLE)
+ g_print ("%sWRITEABLE", 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 char *
+winsock_error_message (int number)
+{
+ static char unk[100];
+
+ switch (number) {
+ case WSAEINTR:
+ return "Interrupted function call";
+ case WSAEACCES:
+ return "Permission denied";
+ case WSAEFAULT:
+ return "Bad address";
+ case WSAEINVAL:
+ return "Invalid argument";
+ case WSAEMFILE:
+ return "Too many open sockets";
+ case WSAEWOULDBLOCK:
+ return "Resource temporarily unavailable";
+ case WSAEINPROGRESS:
+ return "Operation now in progress";
+ case WSAEALREADY:
+ return "Operation already in progress";
+ case WSAENOTSOCK:
+ return "Socket operation on nonsocket";
+ case WSAEDESTADDRREQ:
+ return "Destination address required";
+ case WSAEMSGSIZE:
+ return "Message too long";
+ case WSAEPROTOTYPE:
+ return "Protocol wrong type for socket";
+ case WSAENOPROTOOPT:
+ return "Bad protocol option";
+ case WSAEPROTONOSUPPORT:
+ return "Protocol not supported";
+ case WSAESOCKTNOSUPPORT:
+ return "Socket type not supported";
+ case WSAEOPNOTSUPP:
+ return "Operation not supported on transport endpoint";
+ case WSAEPFNOSUPPORT:
+ return "Protocol family not supported";
+ case WSAEAFNOSUPPORT:
+ return "Address family not supported by protocol family";
+ case WSAEADDRINUSE:
+ return "Address already in use";
+ case WSAEADDRNOTAVAIL:
+ return "Address not available";
+ case WSAENETDOWN:
+ return "Network interface is not configured";
+ case WSAENETUNREACH:
+ return "Network is unreachable";
+ case WSAENETRESET:
+ return "Network dropped connection on reset";
+ case WSAECONNABORTED:
+ return "Software caused connection abort";
+ case WSAECONNRESET:
+ return "Connection reset by peer";
+ case WSAENOBUFS:
+ return "No buffer space available";
+ case WSAEISCONN:
+ return "Socket is already connected";
+ case WSAENOTCONN:
+ return "Socket is not connected";
+ case WSAESHUTDOWN:
+ return "Can't send after socket shutdown";
+ case WSAETIMEDOUT:
+ return "Connection timed out";
+ case WSAECONNREFUSED:
+ return "Connection refused";
+ case WSAEHOSTDOWN:
+ return "Host is down";
+ case WSAEHOSTUNREACH:
+ return "Host is unreachable";
+ case WSAEPROCLIM:
+ return "Too many processes";
+ case WSASYSNOTREADY:
+ return "Network subsystem is unavailable";
+ case WSAVERNOTSUPPORTED:
+ return "Winsock.dll version out of range";
+ case WSANOTINITIALISED:
+ return "Successful WSAStartup not yet performed";
+ case WSAEDISCON:
+ return "Graceful shutdown in progress";
+ case WSATYPE_NOT_FOUND:
+ return "Class type not found";
+ case WSAHOST_NOT_FOUND:
+ return "Host not found";
+ case WSATRY_AGAIN:
+ return "Nonauthoritative host not found";
+ case WSANO_RECOVERY:
+ return "This is a nonrecoverable error";
+ case WSANO_DATA:
+ return "Valid name, no data record of requested type";
+ case WSA_INVALID_HANDLE:
+ return "Specified event object handle is invalid";
+ case WSA_INVALID_PARAMETER:
+ return "One or more parameters are invalid";
+ case WSA_IO_INCOMPLETE:
+ return "Overlapped I/O event object not in signaled state";
+ case WSA_NOT_ENOUGH_MEMORY:
+ return "Insufficient memory available";
+ case WSA_OPERATION_ABORTED:
+ return "Overlapped operation aborted";
+ case WSAEINVALIDPROCTABLE:
+ return "Invalid procedure table from service provider";
+ case WSAEINVALIDPROVIDER:
+ return "Invalid service provider version number";
+ case WSAEPROVIDERFAILEDINIT:
+ return "Unable to initialize a service provider";
+ case WSASYSCALLFAILURE:
+ return "System call failure";
+ default:
+ sprintf (unk, "Unknown WinSock error %d", number);
+ return unk;
+ }
+}
+
+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->debug = g_io_win32_get_debug_flag ();
channel->buffer = NULL;
channel->running = FALSE;
channel->needs_close = FALSE;
channel->data_avail_event = NULL;
channel->revents = 0;
channel->space_avail_event = NULL;
- channel->data_avail_noticed_event = NULL;
- channel->watches = NULL;
+ channel->event_mask = 0;
+ channel->last_events = 0;
+ channel->event = 0;
+ channel->write_would_have_blocked = FALSE;
InitializeCriticalSection (&channel->mutex);
}
{
SECURITY_ATTRIBUTES sec_attrs;
- sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
sec_attrs.lpSecurityDescriptor = NULL;
sec_attrs.bInheritHandle = FALSE;
* is automatic reset.
*/
if (!(channel->data_avail_event = CreateEvent (&sec_attrs, TRUE, FALSE, NULL))
- || !(channel->space_avail_event = CreateEvent (&sec_attrs, FALSE, FALSE, NULL))
- || !(channel->data_avail_noticed_event = CreateEvent (&sec_attrs, FALSE, 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);
}
}
g_io_channel_ref ((GIOChannel *)channel);
if (channel->debug)
- g_print ("read_thread %#x: start fd:%d, data_avail:%#x, space_avail:%#x\n",
+ g_print ("read_thread %#x: start fd=%d, data_avail=%#x space_avail=%#x\n",
channel->thread_id,
channel->fd,
(guint) channel->data_avail_event,
(guint) 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);
+ LOCK (channel->mutex);
while (channel->running)
{
- LOCK (channel->mutex);
if (channel->debug)
g_print ("read_thread %#x: rdp=%d, wrp=%d\n",
channel->thread_id, channel->rdp, channel->wrp);
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);
}
channel->running = FALSE;
SetEvent (channel->data_avail_event);
UNLOCK (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.
*/
- CloseHandle (channel->thread_handle);
+ return 0;
+}
+
+static unsigned __stdcall
+write_thread (void *parameter)
+{
+ GIOWin32Channel *channel = parameter;
+ guchar *buffer;
+ guint nbytes;
+
+ g_io_channel_ref ((GIOChannel *)channel);
+
+ if (channel->debug)
+ g_print ("write_thread %#x: start fd=%d, data_avail=%#x space_avail=%#x\n",
+ channel->thread_id,
+ channel->fd,
+ (guint) channel->data_avail_event,
+ (guint) channel->space_avail_event);
+
+ 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.
+ */
+
+ LOCK (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);
+ UNLOCK (channel->mutex);
+ WaitForSingleObject (channel->space_avail_event, INFINITE);
+
+ LOCK (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);
+
+ UNLOCK (channel->mutex);
+ nbytes = write (channel->fd, buffer, nbytes);
+ LOCK (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;
+ }
+ UNLOCK (channel->mutex);
+
+ g_io_channel_unref ((GIOChannel *)channel);
+
return 0;
}
GIOCondition condition,
unsigned (__stdcall *thread) (void *parameter))
{
- channel->thread_handle =
- (HANDLE) _beginthreadex (NULL, 0, thread, channel, 0,
- &channel->thread_id);
- if (channel->thread_handle == 0)
+ HANDLE thread_handle;
+
+ thread_handle = (HANDLE) _beginthreadex (NULL, 0, thread, channel, 0,
+ &channel->thread_id);
+ if (thread_handle == 0)
g_warning (G_STRLOC ": Error creating reader thread: %s",
- strerror (errno));
+ g_strerror (errno));
+ else if (!CloseHandle (thread_handle))
+ g_warning (G_STRLOC ": Error closing thread handle: %s\n",
+ g_win32_error_message (GetLastError ()));
+
WaitForSingleObject (channel->space_avail_event, INFINITE);
}
LOCK (channel->mutex);
if (channel->wrp == channel->rdp && !channel->running)
{
+ if (channel->debug)
+ g_print ("wrp==rdp, !running\n");
UNLOCK (channel->mutex);
*bytes_read = 0;
- return G_IO_STATUS_EOF; /* Is this correct? FIXME */
+ return G_IO_STATUS_EOF;
}
}
return (*bytes_read > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
}
-static unsigned __stdcall
-select_thread (void *parameter)
-{
- GIOWin32Channel *channel = parameter;
- fd_set read_fds, write_fds, except_fds;
- GSList *tmp;
- int n;
-
- g_io_channel_ref ((GIOChannel *)channel);
- if (channel->debug)
- g_print ("select_thread %#x: start fd:%d,\n\tdata_avail:%#x, data_avail_noticed:%#x\n",
- channel->thread_id,
- channel->fd,
- (guint) channel->data_avail_event,
- (guint) channel->data_avail_noticed_event);
+static GIOStatus
+buffer_write (GIOWin32Channel *channel,
+ const guchar *dest,
+ gsize count,
+ gsize *bytes_written,
+ GError **err)
+{
+ guint nbytes;
+ guint left = count;
- channel->rdp = channel->wrp = 0;
- channel->running = TRUE;
-
- SetEvent (channel->space_avail_event);
+ LOCK (channel->mutex);
+ if (channel->debug)
+ g_print ("buffer_write: writing to thread %#x %d bytes, rdp=%d, wrp=%d\n",
+ channel->thread_id, count, channel->rdp, channel->wrp);
- while (channel->running)
+ if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
{
- FD_ZERO (&read_fds);
- FD_ZERO (&write_fds);
- FD_ZERO (&except_fds);
-
- tmp = channel->watches;
- while (tmp)
- {
- GIOWin32Watch *watch = (GIOWin32Watch *)tmp->data;
-
- if (watch->condition & (G_IO_IN | G_IO_HUP))
- FD_SET (channel->fd, &read_fds);
- if (watch->condition & G_IO_OUT)
- FD_SET (channel->fd, &write_fds);
- if (watch->condition & G_IO_ERR)
- FD_SET (channel->fd, &except_fds);
-
- tmp = tmp->next;
- }
- if (channel->debug)
- g_print ("select_thread %#x: calling select() for%s%s%s\n",
- channel->thread_id,
- (FD_ISSET (channel->fd, &read_fds) ? " IN" : ""),
- (FD_ISSET (channel->fd, &write_fds) ? " OUT" : ""),
- (FD_ISSET (channel->fd, &except_fds) ? " ERR" : ""));
-
- n = select (1, &read_fds, &write_fds, &except_fds, NULL);
-
- if (n == SOCKET_ERROR)
- {
- if (channel->debug)
- g_print ("select_thread %#x: select returned SOCKET_ERROR\n",
- channel->thread_id);
- break;
- }
-
+ /* Buffer is full */
if (channel->debug)
- g_print ("select_thread %#x: got%s%s%s\n",
- channel->thread_id,
- (FD_ISSET (channel->fd, &read_fds) ? " IN" : ""),
- (FD_ISSET (channel->fd, &write_fds) ? " OUT" : ""),
- (FD_ISSET (channel->fd, &except_fds) ? " ERR" : ""));
-
- if (FD_ISSET (channel->fd, &read_fds))
- channel->revents |= G_IO_IN;
- if (FD_ISSET (channel->fd, &write_fds))
- channel->revents |= G_IO_OUT;
- if (FD_ISSET (channel->fd, &except_fds))
- channel->revents |= G_IO_ERR;
-
- if (channel->debug)
- g_print ("select_thread %#x: resetting data_avail_noticed,\n"
- "\tsetting data_avail\n",
+ g_print ("buffer_write: tid %#x: resetting data_avail\n",
channel->thread_id);
- ResetEvent (channel->data_avail_noticed_event);
- SetEvent (channel->data_avail_event);
-
- LOCK (channel->mutex);
- if (channel->needs_close)
- {
- UNLOCK (channel->mutex);
- break;
- }
- UNLOCK (channel->mutex);
-
+ ResetEvent (channel->data_avail_event);
if (channel->debug)
- g_print ("select_thread %#x: waiting for data_avail_noticed\n",
+ g_print ("buffer_write: tid %#x: waiting for space\n",
channel->thread_id);
-
- WaitForSingleObject (channel->data_avail_noticed_event, INFINITE);
+ UNLOCK (channel->mutex);
+ WaitForSingleObject (channel->data_avail_event, INFINITE);
+ LOCK (channel->mutex);
if (channel->debug)
- g_print ("select_thread %#x: got data_avail_noticed\n",
- channel->thread_id);
+ g_print ("buffer_write: tid %#x: rdp=%d, wrp=%d\n",
+ channel->thread_id, channel->rdp, channel->wrp);
}
-
- channel->running = FALSE;
+
+ nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
+ BUFFER_SIZE - channel->wrp);
+
+ UNLOCK (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;
LOCK (channel->mutex);
- if (channel->fd != -1)
+
+ 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)
{
- /* DO NOT close the fd here */
- channel->fd = -1;
+ /* 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 ("select_thread %#x: got error, setting data_avail\n",
- channel->thread_id);
- SetEvent (channel->data_avail_event);
UNLOCK (channel->mutex);
- 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.
+ /* We have no way to indicate any errors form the actual
+ * write() call in the writer thread. Should we have?
*/
-
- CloseHandle (channel->thread_handle);
-
- return 0;
+ *bytes_written = count - left;
+ return (*bytes_written > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
}
+
static gboolean
g_io_win32_prepare (GSource *source,
gint *timeout)
{
GIOWin32Watch *watch = (GIOWin32Watch *)source;
+ GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
+ int event_mask;
*timeout = -1;
- if (channel->type == G_IO_WIN32_FILE_DESC)
+ switch (channel->type)
{
+ case G_IO_WIN32_WINDOWS_MESSAGES:
+ case G_IO_WIN32_CONSOLE:
+ break;
+
+ case G_IO_WIN32_FILE_DESC:
+ if (channel->debug)
+ g_print ("g_io_win32_prepare: for 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));
+
LOCK (channel->mutex);
- if (channel->running && channel->wrp == channel->rdp)
- channel->revents = 0;
+ if (channel->running)
+ {
+ if (channel->direction == 0 && channel->wrp == channel->rdp)
+ {
+ if (channel->debug)
+ g_print ("g_io_win32_prepare: for thread %#x, setting channel->revents = 0\n",
+ channel->thread_id);
+ channel->revents = 0;
+ }
+ }
+ else
+ {
+ if (channel->direction == 1
+ && (channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
+ {
+ if (channel->debug)
+ g_print ("g_io_win32_prepare: for thread %#x, setting channel->revents = %i\n",
+ channel->thread_id, 0);
+ channel->revents = 0;
+ }
+ }
UNLOCK (channel->mutex);
- }
- else if (channel->type == G_IO_WIN32_SOCKET)
- {
- channel->revents = 0;
+ break;
- if (channel->debug)
- g_print ("g_io_win32_prepare: thread %#x, setting data_avail_noticed\n",
- channel->thread_id);
- SetEvent (channel->data_avail_noticed_event);
- if (channel->debug)
- g_print ("g_io_win32_prepare: thread %#x, there.\n",
- channel->thread_id);
- }
+ case G_IO_WIN32_SOCKET:
+ 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;
- watch->condition = g_io_channel_get_buffer_condition (watch->channel);
+ if (channel->event_mask != event_mask /* || channel->event != watch->pollfd.fd*/)
+ {
+ if (channel->debug)
+ g_print ("g_io_win32_prepare: WSAEventSelect(%d, %#x, {%s}\n",
+ channel->fd, watch->pollfd.fd,
+ event_mask_to_string (event_mask));
+ if (WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd,
+ event_mask) == SOCKET_ERROR)
+ ; /* What? */
+ channel->event_mask = event_mask;
+#if 0
+ channel->event = watch->pollfd.fd;
+#endif
+ channel->last_events = 0;
+ }
+ break;
- return (watch->pollfd.revents & (G_IO_IN | G_IO_OUT)) == watch->condition;
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
+ return ((watch->condition & buffer_condition) == watch->condition);
}
static gboolean
g_io_win32_check (GSource *source)
{
- MSG msg;
+ MSG msg;
GIOWin32Watch *watch = (GIOWin32Watch *)source;
GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
-
- if (channel->debug)
- g_print ("g_io_win32_check: for thread %#x:\n"
- "\twatch->pollfd.events:%#x, watch->pollfd.revents:%#x, channel->revents:%#x\n",
- channel->thread_id,
- watch->pollfd.events, watch->pollfd.revents, channel->revents);
+ GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
+ WSANETWORKEVENTS events;
+
+ switch (channel->type)
+ {
+ case G_IO_WIN32_WINDOWS_MESSAGES:
+ return (PeekMessage (&msg, channel->hwnd, 0, 0, PM_NOREMOVE));
+
+ case G_IO_WIN32_FILE_DESC:
+ if (channel->debug)
+ g_print ("g_io_win32_check: for 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 (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;
- if (channel->type != G_IO_WIN32_WINDOWS_MESSAGES)
+ case G_IO_WIN32_SOCKET:
+ if (channel->last_events & FD_WRITE)
{
- watch->pollfd.revents = (watch->pollfd.events & channel->revents);
+ if (channel->debug)
+ g_print ("g_io_win32_check: sock=%d event=%#x last_events has FD_WRITE\n",
+ channel->fd, watch->pollfd.fd);
+ }
+ else
+ {
+ WSAEnumNetworkEvents (channel->fd, 0, &events);
+
+ if (channel->debug)
+ g_print ("g_io_win32_check: WSAEnumNetworkEvents (%d, %#x) revents={%s} condition={%s} events={%s}\n",
+ channel->fd, watch->pollfd.fd,
+ condition_to_string (watch->pollfd.revents),
+ condition_to_string (watch->condition),
+ 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 ("g_io_win32_check: WSAEventSelect(%d, %#x, {})\n",
+ channel->fd, watch->pollfd.fd);
+ WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd, 0);
+ if (channel->debug)
+ g_print ("g_io_win32_check: ResetEvent(%#x)\n",
+ watch->pollfd.fd);
+ ResetEvent ((HANDLE) watch->pollfd.fd);
+ }
+ channel->last_events = events.lNetworkEvents;
}
- else
+ 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
{
- return (PeekMessage (&msg, channel->hwnd, 0, 0, PM_NOREMOVE));
+ /* 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;
}
- if (channel->type == G_IO_WIN32_SOCKET)
- {
- if (channel->debug)
- g_print ("g_io_win32_check: thread %#x, resetting data_avail\n",
- channel->thread_id);
- ResetEvent (channel->data_avail_event);
- if (channel->debug)
- g_print ("g_io_win32_check: thread %#x, there.\n",
- channel->thread_id);
- }
+ /* Regardless of WSAEnumNetworkEvents() result, if watching for
+ * writability, 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->write_would_have_blocked &&
+ (channel->event_mask & FD_WRITE))
+ watch->pollfd.revents |= G_IO_OUT;
- watch->condition &= g_io_channel_get_buffer_condition (watch->channel);
-
- return (watch->pollfd.revents & watch->condition);
+ return ((watch->pollfd.revents | buffer_condition) & watch->condition);
+
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
}
static gboolean
{
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)
{
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 & watch->condition,
+ (watch->pollfd.revents | buffer_condition) & watch->condition,
user_data);
}
GIOWin32Watch *watch = (GIOWin32Watch *)source;
GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
- if (channel->debug)
- g_print ("g_io_win32_finalize: channel with thread %#x\n",
- channel->thread_id);
+ switch (channel->type)
+ {
+ case G_IO_WIN32_WINDOWS_MESSAGES:
+ case G_IO_WIN32_CONSOLE:
+ break;
+
+ case G_IO_WIN32_FILE_DESC:
+ LOCK (channel->mutex);
+ if (channel->debug)
+ g_print ("g_io_win32_finalize: channel with thread %#x\n",
+ channel->thread_id);
+ UNLOCK (channel->mutex);
+ break;
- channel->watches = g_slist_remove (channel->watches, watch);
+ case G_IO_WIN32_SOCKET:
+ if (channel->debug)
+ g_print ("g_io_win32_finalize: channel is for sock=%d\n", channel->fd);
+#if 0
+ CloseHandle ((HANDLE) watch->pollfd.fd);
+ channel->event = 0;
+ channel->event_mask = 0;
+#endif
+ break;
- SetEvent (channel->data_avail_noticed_event);
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
g_io_channel_unref (watch->channel);
}
-static GSourceFuncs win32_watch_funcs = {
+GSourceFuncs g_io_watch_funcs = {
g_io_win32_prepare,
g_io_win32_check,
g_io_win32_dispatch,
g_io_win32_finalize
};
-static GSource *
-g_io_win32_create_watch (GIOChannel *channel,
- GIOCondition condition,
- unsigned (__stdcall *thread) (void *parameter))
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- GIOWin32Watch *watch;
- GSource *source;
-
- source = g_source_new (&win32_watch_funcs, sizeof (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 = (gint) win32_channel->data_avail_event;
- watch->pollfd.events = condition;
-
- if (win32_channel->debug)
- g_print ("g_io_win32_create_watch: fd:%d condition:%#x handle:%#x\n",
- win32_channel->fd, condition, watch->pollfd.fd);
-
- win32_channel->watches = g_slist_append (win32_channel->watches, watch);
-
- if (win32_channel->thread_id == 0)
- create_thread (win32_channel, condition, thread);
-
- g_source_add_poll (source, &watch->pollfd);
-
- return source;
-}
-
static GIOStatus
g_io_win32_msg_read (GIOChannel *channel,
gchar *buf,
if (count < sizeof (MSG))
{
- g_set_error(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL,
- _("Incorrect message size")); /* Correct error message? FIXME */
+ g_set_error (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: for %#x\n",
- win32_channel->hwnd);
+ (guint) 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 (*bytes_read > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+ return G_IO_STATUS_NORMAL;
}
static GIOStatus
if (count != sizeof (MSG))
{
- g_set_error(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL,
- _("Incorrect message size")); /* Correct error message? FIXME */
+ g_set_error (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL,
+ "Incorrect message size"); /* Informative enough error message? */
return G_IO_STATUS_ERROR;
}
memmove (&msg, buf, sizeof (MSG));
if (!PostMessage (win32_channel->hwnd, msg.message, msg.wParam, msg.lParam))
{
- g_set_error(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED,
- _("Unknown error")); /* Correct error message? FIXME */
+ gchar *emsg = g_win32_error_message (GetLastError ());
+ g_set_error (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, emsg);
+ g_free (emsg);
return G_IO_STATUS_ERROR;
}
}
static GIOStatus
-g_io_win32_no_seek (GIOChannel *channel,
- glong offset,
- GSeekType type,
- GError **err)
-{
- g_assert_not_reached ();
-
- return G_IO_STATUS_ERROR;
-}
-
-static GIOStatus
g_io_win32_msg_close (GIOChannel *channel,
GError **err)
{
GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
if (win32_channel->debug)
- g_print ("thread %#x: freeing channel, fd: %d\n",
- win32_channel->thread_id,
- win32_channel->fd);
+ g_print ("g_io_win32_free channel fd=%d\n", win32_channel->fd);
if (win32_channel->data_avail_event)
CloseHandle (win32_channel->data_avail_event);
if (win32_channel->space_avail_event)
CloseHandle (win32_channel->space_avail_event);
- if (win32_channel->data_avail_noticed_event)
- CloseHandle (win32_channel->data_avail_noticed_event);
+ if (win32_channel->type == G_IO_WIN32_SOCKET)
+ WSAEventSelect (win32_channel->fd, NULL, 0);
DeleteCriticalSection (&win32_channel->mutex);
g_free (win32_channel->buffer);
- g_slist_free (win32_channel->watches);
g_free (win32_channel);
}
static GSource *
-g_io_win32_msg_create_watch (GIOChannel *channel,
- GIOCondition condition)
+g_io_win32_msg_create_watch (GIOChannel *channel,
+ GIOCondition condition)
{
GIOWin32Watch *watch;
GSource *source;
- source = g_source_new (&win32_watch_funcs, sizeof (GIOWin32Watch));
+ source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
watch = (GIOWin32Watch *)source;
watch->channel = channel;
}
static GIOStatus
-g_io_win32_fd_read (GIOChannel *channel,
- gchar *buf,
- gsize count,
- gsize *bytes_read,
- GError **err)
+g_io_win32_fd_and_console_read (GIOChannel *channel,
+ gchar *buf,
+ gsize count,
+ gsize *bytes_read,
+ GError **err)
{
GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
gint result;
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=%d\n",
win32_channel->fd, count);
if (win32_channel->thread_id)
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;
- switch(errno)
+ switch (errno)
{
#ifdef EAGAIN
- case EAGAIN:
- return G_IO_STATUS_AGAIN;
+ case EAGAIN:
+ return G_IO_STATUS_AGAIN;
#endif
- default:
- g_set_error (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errno),
- strerror (errno));
- return G_IO_STATUS_ERROR;
+ default:
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
}
}
}
static GIOStatus
-g_io_win32_fd_write (GIOChannel *channel,
- const gchar *buf,
- gsize count,
- gsize *bytes_written,
- GError **err)
+g_io_win32_fd_and_console_write (GIOChannel *channel,
+ const gchar *buf,
+ gsize count,
+ gsize *bytes_written,
+ GError **err)
{
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=%d => %d\n",
win32_channel->fd, count, result);
if (result < 0)
{
*bytes_written = 0;
- switch(errno)
+ switch (errno)
{
#ifdef EAGAIN
- case EAGAIN:
- return G_IO_STATUS_AGAIN;
+ case EAGAIN:
+ return G_IO_STATUS_AGAIN;
#endif
- default:
- g_set_error (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errno),
- strerror (errno));
- return G_IO_STATUS_ERROR;
+ default:
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
}
}
static GIOStatus
g_io_win32_fd_seek (GIOChannel *channel,
- glong offset,
+ gint64 offset,
GSeekType type,
GError **err)
{
GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
int whence;
+ off_t tmp_offset;
off_t result;
switch (type)
break;
default:
whence = -1; /* Keep the compiler quiet */
- g_assert_not_reached();
+ g_assert_not_reached ();
+ abort ();
+ }
+
+ tmp_offset = offset;
+ if (tmp_offset != offset)
+ {
+ g_set_error (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)
{
g_set_error (err, G_IO_CHANNEL_ERROR,
g_io_channel_error_from_errno (errno),
- strerror (errno));
+ g_strerror (errno));
return G_IO_STATUS_ERROR;
}
win32_channel->thread_id, win32_channel->fd);
win32_channel->running = FALSE;
win32_channel->needs_close = TRUE;
- SetEvent (win32_channel->data_avail_event);
+ if (win32_channel->direction == 0)
+ SetEvent (win32_channel->data_avail_event);
+ else
+ SetEvent (win32_channel->space_avail_event);
}
else
{
g_io_win32_fd_create_watch (GIOChannel *channel,
GIOCondition condition)
{
- return g_io_win32_create_watch (channel, condition, read_thread);
+ 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 = (gint) win32_channel->data_avail_event;
+ watch->pollfd.events = condition;
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_fd_create_watch: fd=%d condition={%s} handle=%#x\n",
+ win32_channel->fd, condition_to_string (condition), watch->pollfd.fd);
+
+ LOCK (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);
+ UNLOCK (win32_channel->mutex);
+
+ return source;
+}
+
+static GIOStatus
+g_io_win32_console_close (GIOChannel *channel,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ if (close (win32_channel->fd) < 0)
+ {
+ g_set_error (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 GSource *
+g_io_win32_console_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;
+
+ watch->pollfd.fd = (gint) _get_osfhandle (win32_channel->fd);
+ watch->pollfd.events = condition;
+
+ g_source_add_poll (source, &watch->pollfd);
+
+ return source;
}
static GIOStatus
GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
gint result;
GIOChannelError error;
+ int winsock_error;
if (win32_channel->debug)
- g_print ("g_io_win32_sock_read: sockfd:%d count:%d\n",
+ g_print ("g_io_win32_sock_read: sockfd=%d count=%d\n",
win32_channel->fd, count);
-repeat:
+
result = recv (win32_channel->fd, buf, count, 0);
+ if (result == SOCKET_ERROR)
+ winsock_error = WSAGetLastError ();
if (win32_channel->debug)
- g_print ("g_io_win32_sock_read: recv:%d\n", result);
+ g_print ("g_io_win32_sock_read: recv=%d %s\n",
+ result,
+ (result == SOCKET_ERROR ? winsock_error_message (winsock_error) : ""));
if (result == SOCKET_ERROR)
{
*bytes_read = 0;
- switch (WSAGetLastError ())
+ switch (winsock_error)
{
case WSAEINVAL:
error = G_IO_CHANNEL_ERROR_INVAL;
break;
case WSAEWOULDBLOCK:
return G_IO_STATUS_AGAIN;
-#ifdef WE_NEED_TO_HANDLE_WSAEINTR /* not anymore with wsock2 ? */
- case WSAEINTR:
- goto repeat;
-#endif
default:
error = G_IO_CHANNEL_ERROR_FAILED;
break;
}
- g_set_error(err, G_IO_CHANNEL_ERROR, error, _("Socket error"));
+ g_set_error (err, G_IO_CHANNEL_ERROR, error,
+ winsock_error_message (winsock_error));
return G_IO_STATUS_ERROR;
- /* FIXME get all errors, better error messages */
}
else
{
*bytes_read = result;
-
- return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+ if (result == 0)
+ return G_IO_STATUS_EOF;
+ else
+ return G_IO_STATUS_NORMAL;
}
}
GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
gint result;
GIOChannelError error;
+ int winsock_error;
if (win32_channel->debug)
- g_print ("g_io_win32_sock_write: sockfd:%d count:%d\n",
+ g_print ("g_io_win32_sock_write: sockfd=%d count=%d\n",
win32_channel->fd, count);
-repeat:
+
result = send (win32_channel->fd, buf, count, 0);
-
+ if (result == SOCKET_ERROR)
+ winsock_error = WSAGetLastError ();
+
if (win32_channel->debug)
- g_print ("g_io_win32_sock_write: send:%d\n", result);
+ g_print ("g_io_win32_sock_write: send=%d %s\n",
+ result,
+ (result == SOCKET_ERROR ? winsock_error_message (winsock_error) : ""));
if (result == SOCKET_ERROR)
{
*bytes_written = 0;
- switch (WSAGetLastError ())
+ switch (winsock_error)
{
case WSAEINVAL:
error = G_IO_CHANNEL_ERROR_INVAL;
break;
case WSAEWOULDBLOCK:
+ win32_channel->write_would_have_blocked = TRUE;
+ win32_channel->last_events = 0;
return G_IO_STATUS_AGAIN;
-#ifdef WE_NEED_TO_HANDLE_WSAEINTR /* not anymore with wsock2 ? */
- case WSAEINTR:
- goto repeat;
-#endif
default:
error = G_IO_CHANNEL_ERROR_FAILED;
break;
}
- g_set_error(err, G_IO_CHANNEL_ERROR, error, _("Socket error"));
+ g_set_error (err, G_IO_CHANNEL_ERROR, error,
+ winsock_error_message (winsock_error));
+
return G_IO_STATUS_ERROR;
- /* FIXME get all errors, better error messages */
}
else
{
*bytes_written = result;
+ win32_channel->write_would_have_blocked = FALSE;
return G_IO_STATUS_NORMAL;
}
{
GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- LOCK(win32_channel->mutex);
- if (win32_channel->running)
- {
- if (win32_channel->debug)
- g_print ("thread %#x: running, marking for later close\n",
- win32_channel->thread_id);
- win32_channel->running = FALSE;
- win32_channel->needs_close = TRUE;
- SetEvent(win32_channel->data_avail_noticed_event);
- }
if (win32_channel->fd != -1)
- {
- if (win32_channel->debug)
- g_print ("thread %#x: closing socket %d\n",
- win32_channel->thread_id,
- win32_channel->fd);
-
- closesocket (win32_channel->fd);
- win32_channel->fd = -1;
- }
- UNLOCK(win32_channel->mutex);
+ {
+ if (win32_channel->debug)
+ g_print ("g_io_win32_sock_close: closing socket %d\n",
+ win32_channel->fd);
+
+ closesocket (win32_channel->fd);
+ win32_channel->fd = -1;
+ }
/* FIXME error detection? */
g_io_win32_sock_create_watch (GIOChannel *channel,
GIOCondition condition)
{
- return g_io_win32_create_watch (channel, condition, select_thread);
+ 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 = (int) WSACreateEvent ();
+
+ watch->pollfd.fd = win32_channel->event;
+ watch->pollfd.events = condition;
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_sock_create_watch: sock=%d handle=%#x condition={%s}\n",
+ win32_channel->fd, watch->pollfd.fd,
+ condition_to_string (watch->condition));
+
+ g_source_add_poll (source, &watch->pollfd);
+
+ return source;
}
GIOChannel *
const gchar *mode,
GError **error)
{
- int fid, flags;
+ int fid, flags, pmode;
GIOChannel *channel;
enum { /* Cheesy hack */
{
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 ();
- flags = 0;
+ 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);
+ }
- fid = open (filename, flags);
if (fid < 0)
{
g_set_error (error, G_FILE_ERROR,
g_file_error_from_errno (errno),
- strerror (errno));
+ g_strerror (errno));
return (GIOChannel *)NULL;
}
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_readable = TRUE;
channel->is_writeable = FALSE;
break;
case MODE_W:
case MODE_A:
channel->is_readable = FALSE;
- channel->is_writeable = TRUE;
break;
case MODE_R | MODE_PLUS:
case MODE_W | MODE_PLUS:
case MODE_A | MODE_PLUS:
- channel->is_readable = TRUE;
- channel->is_writeable = TRUE;
break;
default:
g_assert_not_reached ();
+ abort ();
}
- if (((GIOWin32Channel *)channel)->debug)
- g_print ("g_io_channel_win32_new_file: fd = %ud\n", fid);
-
return channel;
}
-GIOStatus
-g_io_win32_set_flags (GIOChannel *channel,
- GIOFlags flags,
- GError **err)
+#ifdef G_OS_WIN32
+
+#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;
- g_set_error (err,
- G_IO_CHANNEL_ERROR,
- g_file_error_from_errno (EACCES),
- _("Channel set flags unsupported"));
+ if (win32_channel->debug)
+ {
+ g_print ("g_io_win32_unimpl_set_flags: ");
+ g_win32_print_gioflags (flags);
+ g_print ("\n");
+ }
+
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED,
+ "Not implemented on Win32");
+
return G_IO_STATUS_ERROR;
}
-GIOFlags
-g_io_win32_fd_get_flags (GIOChannel *channel)
+static GIOFlags
+g_io_win32_fd_get_flags_internal (GIOChannel *channel,
+ struct stat *st)
{
- GIOFlags flags = 0;
- struct _stat 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 stat 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 == _fstat (win32_channel->fd, &st))
- {
- /* XXX: G_IO_FLAG_APPEND */
- /* XXX: G_IO_FLAG_NONBLOCK */
- if (st.st_mode & _S_IREAD) flags |= G_IO_FLAG_IS_READABLE;
- if (st.st_mode & _S_IWRITE) flags |= G_IO_FLAG_IS_WRITEABLE;
- /* XXX: */
- if (!(st.st_mode & _S_IFIFO)) flags |= G_IO_FLAG_IS_SEEKABLE;
- }
+ if (0 == fstat (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 flags;
+ return 0;
}
-/*
- * Generic implementation, just translating createion flags
- */
-GIOFlags
-g_io_win32_get_flags (GIOChannel *channel)
+static GIOFlags
+g_io_win32_console_get_flags (GIOChannel *channel)
+{
+ 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_CONSOLE, 0);
+
+ return g_io_win32_console_get_flags_internal (channel);
+}
+
+static GIOFlags
+g_io_win32_msg_get_flags (GIOChannel *channel)
+{
+ return 0;
+}
+
+static GIOStatus
+g_io_win32_sock_set_flags (GIOChannel *channel,
+ GIOFlags flags,
+ GError **err)
{
- GIOFlags flags;
+ 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)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED,
+ winsock_error_message (WSAGetLastError ()));
+ return G_IO_STATUS_ERROR;
+ }
+ }
+ else
+ {
+ arg = 0;
+ if (ioctlsocket (win32_channel->fd, FIONBIO, &arg) == SOCKET_ERROR)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED,
+ winsock_error_message (WSAGetLastError ()));
+ return G_IO_STATUS_ERROR;
+ }
+ }
- flags = (channel->is_readable ? G_IO_FLAG_IS_READABLE : 0)
- | (channel->is_writeable ? G_IO_FLAG_IS_READABLE : 0)
- | (channel->is_seekable ? G_IO_FLAG_IS_SEEKABLE : 0);
+ return G_IO_STATUS_NORMAL;
+}
- return flags;
+static GIOFlags
+g_io_win32_sock_get_flags (GIOChannel *channel)
+{
+ /* 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_create_watch,
g_io_win32_free,
- g_io_win32_set_flags,
- g_io_win32_get_flags,
+ 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_create_watch,
g_io_win32_free,
- g_io_win32_set_flags,
+ 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_create_watch,
g_io_win32_free,
- g_io_win32_set_flags,
- g_io_win32_get_flags,
+ g_io_win32_sock_set_flags,
+ g_io_win32_sock_get_flags,
};
GIOChannel *
g_io_channel_init (channel);
g_io_channel_win32_init (win32_channel);
if (win32_channel->debug)
- g_print ("g_io_channel_win32_new_messages: hwnd = %ud\n", hwnd);
+ g_print ("g_io_channel_win32_new_messages: hwnd=%#x\n", hwnd);
channel->funcs = &win32_channel_msg_funcs;
win32_channel->type = G_IO_WIN32_WINDOWS_MESSAGES;
win32_channel->hwnd = (HWND) hwnd;
return channel;
}
-GIOChannel *
-g_io_channel_win32_new_fd (gint fd)
+static GIOChannel *
+g_io_channel_win32_new_fd_internal (gint fd,
+ struct stat *st)
{
GIOWin32Channel *win32_channel;
GIOChannel *channel;
- struct stat st;
-
- if (fstat (fd, &st) == -1)
- {
- g_warning (G_STRLOC ": %d isn't a (emulated) file descriptor", fd);
- return NULL;
- }
win32_channel = g_new (GIOWin32Channel, 1);
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_fd: fd = %d\n", fd);
- channel->funcs = &win32_channel_fd_funcs;
- win32_channel->type = G_IO_WIN32_FILE_DESC;
+
win32_channel->fd = fd;
+ if (win32_channel->debug)
+ g_print ("g_io_channel_win32_new_fd: %u\n", 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;
+}
+
+GIOChannel *
+g_io_channel_win32_new_fd (gint fd)
+{
+ struct stat st;
- channel->is_readable = !!(st.st_mode & _S_IREAD);
- channel->is_writeable = !!(st.st_mode & _S_IWRITE);
- /* XXX: pipes aren't seeakable, are they ? */
- channel->is_seekable = !(st.st_mode & _S_IFIFO);
+ if (fstat (fd, &st) == -1)
+ {
+ g_warning (G_STRLOC ": %d isn't a C library file descriptor", fd);
+ return NULL;
+ }
- return channel;
+ return g_io_channel_win32_new_fd_internal (fd, &st);
}
gint
g_io_channel_init (channel);
g_io_channel_win32_init (win32_channel);
if (win32_channel->debug)
- g_print ("g_io_channel_win32_new_socket: sockfd:%d\n", socket);
+ g_print ("g_io_channel_win32_new_socket: sockfd=%d\n", socket);
channel->funcs = &win32_channel_sock_funcs;
win32_channel->type = G_IO_WIN32_SOCKET;
win32_channel->fd = socket;
- /* XXX: check this */
channel->is_readable = TRUE;
channel->is_writeable = TRUE;
channel->is_seekable = FALSE;
GIOChannel *
g_io_channel_unix_new (gint fd)
{
+ gboolean is_fd, is_socket;
struct stat st;
+ int optval, optlen;
- if (fstat (fd, &st) == 0)
- return g_io_channel_win32_new_fd (fd);
-
- if (getsockopt (fd, SOL_SOCKET, SO_TYPE, NULL, NULL) != SO_ERROR)
+ is_fd = (fstat (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_STRLOC ": %d is both a file descriptor and a socket, file descriptor interpretation assumed.", 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_STRLOC ": %d is neither a file descriptor or a socket", fd);
+
return NULL;
}
{
GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- if (win32_channel->data_avail_event == NULL)
- create_events (win32_channel);
+ switch (win32_channel->type)
+ {
+ case G_IO_WIN32_FILE_DESC:
+ if (win32_channel->data_avail_event == NULL)
+ create_events (win32_channel);
+
+ fd->fd = (gint) win32_channel->data_avail_event;
+
+ if (win32_channel->thread_id == 0 && (condition & G_IO_IN))
+ {
+ 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);
+ }
+ break;
+
+ case G_IO_WIN32_CONSOLE:
+ fd->fd = (gint) _get_osfhandle (win32_channel->fd);
+ break;
+
+ case G_IO_WIN32_SOCKET:
+ fd->fd = (int) WSACreateEvent ();
+ break;
+
+ case G_IO_WIN32_WINDOWS_MESSAGES:
+ fd->fd = G_WIN32_MSG_HANDLE;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
- fd->fd = (gint) win32_channel->data_avail_event;
fd->events = condition;
-
- if (win32_channel->thread_id == 0)
- if ((condition & G_IO_IN) && win32_channel->type == G_IO_WIN32_FILE_DESC)
- create_thread (win32_channel, condition, read_thread);
- else if (win32_channel->type == G_IO_WIN32_SOCKET)
- create_thread (win32_channel, condition, select_thread);
}
/* Binary compatibility */
{
return g_io_channel_win32_new_socket (socket);
}
+
+#define __G_IO_WIN32_C__
+#include "galiasdef.c"