diff options
Diffstat (limited to 'giowin32.c')
-rw-r--r-- | giowin32.c | 1133 |
1 files changed, 505 insertions, 628 deletions
diff --git a/giowin32.c b/giowin32.c index 36fa82c5b..c06df4a0d 100644 --- a/giowin32.c +++ b/giowin32.c @@ -3,6 +3,7 @@ * * giowin32.c: IO Channels for Win32. * Copyright 1998 Owen Taylor and Tor Lillqvist + * Copyright 1999-2000 Tor Lillqvist and Craig Setera * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -11,7 +12,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -24,31 +25,37 @@ * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with - * GLib at ftp://ftp.gtk.org/pub/gtk/. + * GLib at ftp://ftp.gtk.org/pub/gtk/. */ +/* Define this to get (very) verbose logging */ +/* #define G_IO_WIN32_DEBUG */ -#include "config.h" #include "glib.h" + +#include <stdlib.h> #include <windows.h> -#include <winsock.h> /* Not everybody has winsock2 */ +#include <winsock.h> /* Not everybody has winsock2 */ #include <fcntl.h> #include <io.h> +#include <process.h> #include <errno.h> -#include <sys/types.h> - -#include <stdio.h> +#include <sys/stat.h> typedef struct _GIOWin32Channel GIOWin32Channel; typedef struct _GIOWin32Watch GIOWin32Watch; -guint g_pipe_readable_msg; +#define BUFFER_SIZE 4096 typedef enum { G_IO_WINDOWS_MESSAGES, /* Windows messages */ - G_IO_FILE_DESC, /* Unix-like file descriptors from _open*/ - G_IO_PIPE, /* pipe, with windows messages for signalling */ - G_IO_STREAM_SOCKET /* Stream sockets */ + G_IO_FILE_DESC, /* Unix-like file descriptors from + * _open() or _pipe(). Read with read(). + * Have to create separate thread to read. + */ + G_IO_STREAM_SOCKET /* Stream sockets. Similar as fds, but + * read with recv(). + */ } GIOWin32ChannelType; struct _GIOWin32Channel { @@ -58,19 +65,49 @@ struct _GIOWin32Channel { * as provided by WinSock. */ GIOWin32ChannelType type; - + /* This is used by G_IO_WINDOWS_MESSAGES channels */ HWND hwnd; /* handle of window, or NULL */ - - /* This is used by G_IO_PIPE channels */ - guint peer; /* thread id of reader */ - guint peer_fd; /* fd in the reader */ - guint offset; /* counter of accumulated bytes */ - guint need_wakeups; /* in output channels whether the - * reader needs wakeups + + /* Following fields used by fd and socket channels for input */ + + /* Data is kept in a circular buffer. To be able to distinguish between + * empty and full buffer, we cannot fill it completely, but have to + * leave a one character gap. + * + * Data available is between indexes rdp and wrp-1 (modulo BUFFER_SIZE). + * + * Empty: wrp == rdp + * Full: (wrp + 1) % BUFFER_SIZE == rdp + * Partial: otherwise + */ + guchar *buffer; /* (Circular) buffer */ + gint wrp, rdp; /* Buffer indices for writing and reading */ + gboolean running; /* Is reader thread running. FALSE if + * EOF has been reached. */ + guint thread_id; /* If non-NULL has a reader thread, or has + * had.*/ + HANDLE data_avail_event; + HANDLE space_avail_event; + CRITICAL_SECTION mutex; + + /* Function that actually reads from fd */ + int (*reader) (int fd, guchar *buf, int len); }; +#define LOCK(mutex) EnterCriticalSection (&mutex) +#define UNLOCK(mutex) LeaveCriticalSection (&mutex) + +/* Temporarilyu change a PRINT to PRINT_ to get just *that* message */ +#define PRINT_(x) g_print x + +#ifdef G_IO_WIN32_DEBUG +#define PRINT(x) PRINT_(x) +#else +#define PRINT(x) +#endif + struct _GIOWin32Watch { GPollFD pollfd; GIOChannel *channel; @@ -78,346 +115,314 @@ struct _GIOWin32Watch { GIOFunc callback; }; -static gboolean g_io_win32_msg_prepare (gpointer source_data, - GTimeVal *current_time, - gint *timeout); -static gboolean g_io_win32_msg_check (gpointer source_data, - GTimeVal *current_time); -static gboolean g_io_win32_msg_dispatch (gpointer source_data, - GTimeVal *current_time, - gpointer user_data); - -static gboolean g_io_win32_fd_prepare (gpointer source_data, - GTimeVal *current_time, - gint *timeout); -static gboolean g_io_win32_fd_check (gpointer source_data, - GTimeVal *current_time); -static gboolean g_io_win32_fd_dispatch (gpointer source_data, - GTimeVal *current_time, - gpointer user_data); - -static gboolean g_io_win32_pipe_prepare (gpointer source_data, - GTimeVal *current_time, - gint *timeout); -static gboolean g_io_win32_pipe_check (gpointer source_data, - GTimeVal *current_time); -static gboolean g_io_win32_pipe_dispatch (gpointer source_data, - GTimeVal *current_time, - gpointer user_data); -static void g_io_win32_pipe_destroy (gpointer source_data); - -static gboolean g_io_win32_sock_prepare (gpointer source_data, - GTimeVal *current_time, - gint *timeout); -static gboolean g_io_win32_sock_check (gpointer source_data, - GTimeVal *current_time); -static gboolean g_io_win32_sock_dispatch (gpointer source_data, - GTimeVal *current_time, - gpointer user_data); - -static void g_io_win32_destroy (gpointer source_data); - -static GIOError g_io_win32_msg_read (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written); - -static GIOError g_io_win32_msg_write(GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written); -static GIOError g_io_win32_msg_seek (GIOChannel *channel, - gint offset, - GSeekType type); -static void g_io_win32_msg_close (GIOChannel *channel); -static guint g_io_win32_msg_add_watch (GIOChannel *channel, - gint priority, - GIOCondition condition, - GIOFunc func, - gpointer user_data, - GDestroyNotify notify); - -static GIOError g_io_win32_fd_read (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written); -static GIOError g_io_win32_fd_write(GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written); -static GIOError g_io_win32_fd_seek (GIOChannel *channel, - gint offset, - GSeekType type); -static void g_io_win32_fd_close (GIOChannel *channel); - -static void g_io_win32_free (GIOChannel *channel); - -static guint g_io_win32_fd_add_watch (GIOChannel *channel, - gint priority, - GIOCondition condition, - GIOFunc func, - gpointer user_data, - GDestroyNotify notify); - -static GIOError g_io_win32_no_seek (GIOChannel *channel, - gint offset, - GSeekType type); - -static GIOError g_io_win32_pipe_read (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written); -static GIOError g_io_win32_pipe_write (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written); -static void g_io_win32_pipe_close (GIOChannel *channel); -static guint g_io_win32_pipe_add_watch (GIOChannel *channel, - gint priority, - GIOCondition condition, - GIOFunc func, - gpointer user_data, - GDestroyNotify notify); -static void g_io_win32_pipe_free (GIOChannel *channel); - -static GIOError g_io_win32_sock_read (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written); -static GIOError g_io_win32_sock_write(GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written); -static void g_io_win32_sock_close (GIOChannel *channel); -static guint g_io_win32_sock_add_watch (GIOChannel *channel, - gint priority, - GIOCondition condition, - GIOFunc func, - gpointer user_data, - GDestroyNotify notify); - -GSourceFuncs win32_watch_msg_funcs = { - g_io_win32_msg_prepare, - g_io_win32_msg_check, - g_io_win32_msg_dispatch, - g_io_win32_destroy -}; +static void +create_events (GIOWin32Channel *channel) +{ + SECURITY_ATTRIBUTES sec_attrs; + + sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES); + sec_attrs.lpSecurityDescriptor = NULL; + sec_attrs.bInheritHandle = FALSE; -GSourceFuncs win32_watch_fd_funcs = { - g_io_win32_fd_prepare, - g_io_win32_fd_check, - g_io_win32_fd_dispatch, - g_io_win32_destroy -}; + channel->data_avail_event = NULL; + channel->space_avail_event = NULL; + + /* The data available event is manual reset, the space available event + * is automatic reset. + */ + if (!(channel->data_avail_event = CreateEvent (&sec_attrs, TRUE, FALSE, NULL)) + || !(channel->space_avail_event = CreateEvent (&sec_attrs, FALSE, FALSE, NULL))) + { + gchar *msg = g_win32_error_message (GetLastError ()); + g_error ("Error creating event: %s", msg); + } + InitializeCriticalSection (&channel->mutex); +} -GSourceFuncs win32_watch_pipe_funcs = { - g_io_win32_pipe_prepare, - g_io_win32_pipe_check, - g_io_win32_pipe_dispatch, - g_io_win32_pipe_destroy -}; +static unsigned __stdcall +reader_thread (void *parameter) +{ + GIOWin32Channel *channel = parameter; + guchar *buffer; + guint nbytes; -GSourceFuncs win32_watch_sock_funcs = { - g_io_win32_sock_prepare, - g_io_win32_sock_check, - g_io_win32_sock_dispatch, - g_io_win32_destroy -}; + g_io_channel_ref ((GIOChannel *) channel); -GIOFuncs win32_channel_msg_funcs = { - g_io_win32_msg_read, - g_io_win32_msg_write, - g_io_win32_no_seek, - g_io_win32_msg_close, - g_io_win32_msg_add_watch, - g_io_win32_free -}; + PRINT (("thread %#x: starting. pid:%#x, fd:%d, data_avail:%#x, space_avail:%#x\n", + channel->thread_id, + (guint) GetCurrentProcessId (), + channel->fd, + (guint) channel->data_avail_event, + (guint) channel->space_avail_event)); + + channel->buffer = g_malloc (BUFFER_SIZE); + channel->rdp = channel->wrp = 0; + channel->running = TRUE; -GIOFuncs win32_channel_fd_funcs = { - g_io_win32_fd_read, - g_io_win32_fd_write, - g_io_win32_fd_seek, - g_io_win32_fd_close, - g_io_win32_fd_add_watch, - g_io_win32_free -}; + SetEvent (channel->space_avail_event); + + while (channel->running) + { + LOCK (channel->mutex); + PRINT (("thread %#x: rdp=%d, wrp=%d\n", + channel->thread_id, channel->rdp, channel->wrp)); + if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp) + { + /* Buffer is full */ + PRINT (("thread %#x: resetting space_available\n", + channel->thread_id)); + ResetEvent (channel->space_avail_event); + PRINT (("thread %#x: waiting for space\n", channel->thread_id)); + UNLOCK (channel->mutex); + WaitForSingleObject (channel->space_avail_event, INFINITE); + LOCK (channel->mutex); + PRINT (("thread %#x: rdp=%d, wrp=%d\n", + channel->thread_id, channel->rdp, channel->wrp)); + } + + buffer = channel->buffer + channel->wrp; + + /* Always leave at least one byte unused gap to be able to + * distinguish between the full and empty condition... + */ + nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE, + BUFFER_SIZE - channel->wrp); -GIOFuncs win32_channel_pipe_funcs = { - g_io_win32_pipe_read, - g_io_win32_pipe_write, - g_io_win32_no_seek, - g_io_win32_pipe_close, - g_io_win32_pipe_add_watch, - g_io_win32_pipe_free -}; + UNLOCK (channel->mutex); -GIOFuncs win32_channel_sock_funcs = { - g_io_win32_sock_read, - g_io_win32_sock_write, - g_io_win32_no_seek, - g_io_win32_sock_close, - g_io_win32_sock_add_watch, - g_io_win32_free -}; + nbytes = (*channel->reader) (channel->fd, buffer, nbytes); + + if (nbytes <= 0) + break; -#define N_WATCHED_PIPES 4 + LOCK (channel->mutex); + PRINT (("thread %#x: got %d bytes, rdp=%d, wrp=%d\n", + channel->thread_id, nbytes, channel->rdp, channel->wrp)); + channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE; + PRINT (("thread %#x: rdp=%d, wrp=%d, setting data available\n", + channel->thread_id, channel->rdp, channel->wrp)); + SetEvent (channel->data_avail_event); + UNLOCK (channel->mutex); + } + + LOCK (channel->mutex); + channel->running = FALSE; + PRINT (("thread %#x: got EOF, rdp=%d, wrp=%d, setting data available\n", + channel->thread_id, channel->rdp, channel->wrp)); + SetEvent (channel->data_avail_event); + UNLOCK (channel->mutex); + + g_io_channel_unref((GIOChannel *) channel); + + /* All of the Microsoft docs say we should explicitly + * end the thread... + */ + _endthreadex(1); + + return 0; +} -static struct { - gint fd; - GIOWin32Watch *watch; - GIOWin32Channel *channel; - gpointer user_data; -} watched_pipes[N_WATCHED_PIPES]; +static void +create_reader_thread (GIOWin32Channel *channel, + gpointer reader) +{ + channel->reader = reader; -static gint n_watched_pipes = 0; + if (_beginthreadex (NULL, 0, reader_thread, channel, 0, + &channel->thread_id) == 0) + g_warning ("Error creating reader thread: %s", strerror (errno)); + WaitForSingleObject (channel->space_avail_event, INFINITE); +} -static gboolean -g_io_win32_msg_prepare (gpointer source_data, - GTimeVal *current_time, - gint *timeout) +static int +buffer_read (GIOWin32Channel *channel, + guchar *dest, + guint count, + GIOError *error) { - GIOWin32Watch *data = source_data; - GIOWin32Channel *win32_channel = (GIOWin32Channel *) data->channel; - MSG msg; + guint nbytes; + guint left = count; + + LOCK (channel->mutex); + PRINT (("reading from thread %#x %d bytes, rdp=%d, wrp=%d\n", + channel->thread_id, count, channel->rdp, channel->wrp)); + + while (left) + { + if (channel->rdp == channel->wrp) + { + UNLOCK (channel->mutex); + PRINT (("waiting for data from thread %#x\n", channel->thread_id)); + WaitForSingleObject (channel->data_avail_event, INFINITE); + LOCK (channel->mutex); + if (channel->rdp == channel->wrp && !channel->running) + break; + } + + if (channel->rdp < channel->wrp) + nbytes = channel->wrp - channel->rdp; + else + nbytes = BUFFER_SIZE - channel->rdp; + UNLOCK (channel->mutex); + nbytes = MIN (left, nbytes); + PRINT (("moving %d bytes from thread %#x\n", + nbytes, channel->thread_id)); + memcpy (dest, channel->buffer + channel->rdp, nbytes); + dest += nbytes; + left -= nbytes; + LOCK (channel->mutex); + channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE; + PRINT (("setting space available for thread %#x\n", channel->thread_id)); + SetEvent (channel->space_avail_event); + PRINT (("for thread %#x: rdp=%d, wrp=%d\n", + channel->thread_id, channel->rdp, channel->wrp)); + if (channel->running && channel->rdp == channel->wrp) + { + PRINT (("resetting data_available of thread %#x\n", + channel->thread_id)); + ResetEvent (channel->data_avail_event); + }; + } + UNLOCK (channel->mutex); + + return count - left; +} +static gboolean +g_io_win32_prepare (gpointer source_data, + GTimeVal *current_time, + gint *timeout, + gpointer user_data) +{ *timeout = -1; - - return PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_NOREMOVE) == TRUE; + + return FALSE; } -static gboolean -g_io_win32_msg_check (gpointer source_data, - GTimeVal *current_time) +static gboolean +g_io_win32_check (gpointer source_data, + GTimeVal *current_time, + gpointer user_data) { GIOWin32Watch *data = source_data; - GIOWin32Channel *win32_channel = (GIOWin32Channel *) data->channel; - MSG msg; - - return PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_NOREMOVE) == TRUE; + GIOWin32Channel *channel = (GIOWin32Channel *) data->channel; + + /* If the thread has died, we have encountered EOF. If the buffer + * also is emtpty set the HUP bit. + */ + if (!channel->running && channel->rdp == channel->wrp) + { + PRINT (("g_io_win32_check: setting G_IO_HUP thread %#x rdp=%d wrp=%d\n", channel->thread_id, channel->rdp, channel->wrp)); + data->pollfd.revents |= G_IO_HUP; + return TRUE; + } + + return (data->pollfd.revents & data->condition); } static gboolean -g_io_win32_msg_dispatch (gpointer source_data, - GTimeVal *current_time, - gpointer user_data) - +g_io_win32_dispatch (gpointer source_data, + GTimeVal *current_time, + gpointer user_data) + { GIOWin32Watch *data = source_data; - - return (*data->callback)(data->channel, - data->pollfd.revents & data->condition, - user_data); + + return (*data->callback) (data->channel, + data->pollfd.revents & data->condition, + user_data); } static void g_io_win32_destroy (gpointer source_data) { GIOWin32Watch *data = source_data; - + g_main_remove_poll (&data->pollfd); g_io_channel_unref (data->channel); g_free (data); } -static gboolean -g_io_win32_fd_prepare (gpointer source_data, - GTimeVal *current_time, - gint *timeout) -{ - *timeout = -1; - - return FALSE; -} - -static gboolean -g_io_win32_fd_check (gpointer source_data, - GTimeVal *current_time) -{ - GIOWin32Watch *data = source_data; - - return (data->pollfd.revents & data->condition); -} - -static gboolean -g_io_win32_fd_dispatch (gpointer source_data, - GTimeVal *current_time, - gpointer user_data) - -{ - GIOWin32Watch *data = source_data; - - return (*data->callback)(data->channel, - data->pollfd.revents & data->condition, - user_data); -} +static GSourceFuncs win32_watch_funcs = { + g_io_win32_prepare, + g_io_win32_check, + g_io_win32_dispatch, + g_io_win32_destroy +}; static GIOError -g_io_win32_msg_read (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_read) +g_io_win32_msg_read (GIOChannel *channel, + gchar *buf, + guint count, + guint *bytes_read) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - MSG msg; /* In case of alignment problems */ - + MSG msg; /* In case of alignment problems */ + if (count < sizeof (MSG)) return G_IO_ERROR_INVAL; if (!PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_REMOVE)) return G_IO_ERROR_AGAIN; - + memmove (buf, &msg, sizeof (MSG)); *bytes_read = sizeof (MSG); return G_IO_ERROR_NONE; } - -static GIOError -g_io_win32_msg_write(GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written) + +static GIOError +g_io_win32_msg_write (GIOChannel *channel, + gchar *buf, + guint count, + guint *bytes_written) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; MSG msg; - gint result; - + if (count != sizeof (MSG)) return G_IO_ERROR_INVAL; - + /* In case of alignment problems */ memmove (&msg, buf, sizeof (MSG)); if (!PostMessage (win32_channel->hwnd, msg.message, msg.wParam, msg.lParam)) return G_IO_ERROR_UNKNOWN; - + *bytes_written = sizeof (MSG); - return G_IO_ERROR_NONE; + return G_IO_ERROR_NONE; } -static GIOError +static GIOError g_io_win32_no_seek (GIOChannel *channel, - gint offset, - GSeekType type) + gint offset, + GSeekType type) { - g_warning ("g_io_win32_no_seek: unseekable IO channel type"); return G_IO_ERROR_UNKNOWN; } -static void +static void g_io_win32_msg_close (GIOChannel *channel) { /* Nothing to be done. Or should we set hwnd to some invalid value? */ } -static void +static void g_io_win32_free (GIOChannel *channel) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + + if (win32_channel->buffer) + { + CloseHandle (win32_channel->data_avail_event); + CloseHandle (win32_channel->space_avail_event); + DeleteCriticalSection (&win32_channel->mutex); + } + g_free (win32_channel->buffer); g_free (win32_channel); } -static guint +static guint g_io_win32_msg_add_watch (GIOChannel *channel, gint priority, GIOCondition condition, @@ -426,117 +431,56 @@ g_io_win32_msg_add_watch (GIOChannel *channel, GDestroyNotify notify) { GIOWin32Watch *watch = g_new (GIOWin32Watch, 1); - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; watch->channel = channel; g_io_channel_ref (channel); - + watch->callback = func; watch->condition = condition; - + watch->pollfd.fd = G_WIN32_MSG_HANDLE; watch->pollfd.events = condition; - + g_main_add_poll (&watch->pollfd, priority); - - return g_source_add (priority, TRUE, &win32_watch_msg_funcs, + + return g_source_add (priority, TRUE, &win32_watch_funcs, watch, user_data, notify); } -static gboolean -g_io_win32_pipe_prepare (gpointer source_data, - GTimeVal *current_time, - gint *timeout) -{ - *timeout = -1; - - return FALSE; -} - -static gboolean -g_io_win32_pipe_check (gpointer source_data, - GTimeVal *current_time) -{ - GIOWin32Watch *data = source_data; - return FALSE; -} - -static gboolean -g_io_win32_pipe_dispatch (gpointer source_data, - GTimeVal *current_time, - gpointer user_data) - -{ - GIOWin32Watch *data = source_data; - - return (*data->callback)(data->channel, - data->pollfd.revents & data->condition, - user_data); -} - -static void -g_io_win32_pipe_destroy (gpointer source_data) -{ - GIOWin32Watch *data = source_data; - - g_io_channel_unref (data->channel); - g_free (data); -} - -static gboolean -g_io_win32_sock_prepare (gpointer source_data, - GTimeVal *current_time, - gint *timeout) -{ - *timeout = -1; - - return FALSE; -} - -static gboolean -g_io_win32_sock_check (gpointer source_data, - GTimeVal *current_time) -{ - GIOWin32Watch *data = source_data; - - return (data->pollfd.revents & data->condition); -} - -static gboolean -g_io_win32_sock_dispatch (gpointer source_data, - GTimeVal *current_time, - gpointer user_data) - -{ - GIOWin32Watch *data = source_data; - - return (*data->callback)(data->channel, - data->pollfd.revents & data->condition, - user_data); -} - static GIOError -g_io_win32_fd_read (GIOChannel *channel, - gchar *buf, +g_io_win32_fd_read (GIOChannel *channel, + gchar *buf, guint count, guint *bytes_read) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; gint result; + GIOError error; + + if (win32_channel->thread_id) + { + result = buffer_read (win32_channel, buf, count, &error); + if (result < 0) + { + *bytes_read = 0; + return error; + } + else + { + *bytes_read = result; + return G_IO_ERROR_NONE; + } + } result = read (win32_channel->fd, buf, count); + if (result < 0) { *bytes_read = 0; - switch (errno) - { - case EINVAL: - return G_IO_ERROR_INVAL; - case EAGAIN: - return G_IO_ERROR_AGAIN; - default: - return G_IO_ERROR_UNKNOWN; - } + if (errno == EINVAL) + return G_IO_ERROR_INVAL; + else + return G_IO_ERROR_UNKNOWN; } else { @@ -544,18 +488,19 @@ g_io_win32_fd_read (GIOChannel *channel, return G_IO_ERROR_NONE; } } - -static GIOError -g_io_win32_fd_write(GIOChannel *channel, - gchar *buf, + +static GIOError +g_io_win32_fd_write(GIOChannel *channel, + gchar *buf, guint count, guint *bytes_written) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; gint result; - + result = write (win32_channel->fd, buf, count); - + PRINT (("g_io_win32_fd_write: fd:%d count:%d = %d\n", win32_channel->fd, count, result)); + if (result < 0) { *bytes_written = 0; @@ -576,15 +521,15 @@ g_io_win32_fd_write(GIOChannel *channel, } } -static GIOError +static GIOError g_io_win32_fd_seek (GIOChannel *channel, - gint offset, + gint offset, GSeekType type) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; int whence; off_t result; - + switch (type) { case G_SEEK_SET: @@ -617,16 +562,30 @@ g_io_win32_fd_seek (GIOChannel *channel, return G_IO_ERROR_NONE; } -static void +static void g_io_win32_fd_close (GIOChannel *channel) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - + close (win32_channel->fd); return; } -static guint +static int +fd_reader (int fd, + guchar *buf, + int len) +{ + int value; + + value = read (fd, buf, len); + + PRINT (("fd_reader (%d,%p,%d) = %d\n", fd, buf, len, value)); + + return value; +} + +static guint g_io_win32_fd_add_watch (GIOChannel *channel, gint priority, GIOCondition condition, @@ -636,206 +595,65 @@ g_io_win32_fd_add_watch (GIOChannel *channel, { GIOWin32Watch *watch = g_new (GIOWin32Watch, 1); GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + DWORD nbytes; + char dummy[1]; watch->channel = channel; g_io_channel_ref (channel); - + watch->callback = func; watch->condition = condition; + + create_events (win32_channel); - /* This probably does not work, except for CONIN$. */ - watch->pollfd.fd = _get_osfhandle (win32_channel->fd); + watch->pollfd.fd = (gint) win32_channel->data_avail_event; watch->pollfd.events = condition; + + PRINT (("g_io_win32_fd_add_watch: fd:%d handle:%#x\n", + win32_channel->fd, watch->pollfd.fd)); + + /* Is it readable? (Would be strange to watch it otherwise, but... */ + if (ReadFile ((HANDLE) _get_osfhandle (win32_channel->fd), + dummy, 0, &nbytes, NULL)) + create_reader_thread (win32_channel, fd_reader); g_main_add_poll (&watch->pollfd, priority); - - return g_source_add (priority, TRUE, &win32_watch_fd_funcs, + + return g_source_add (priority, TRUE, &win32_watch_funcs, watch, user_data, notify); } static GIOError -g_io_win32_pipe_read (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_read) +g_io_win32_sock_read (GIOChannel *channel, + gchar *buf, + guint count, + guint *bytes_read) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - HANDLE handle; - DWORD avail; gint result; - - handle = (HANDLE) _get_osfhandle (win32_channel->fd); - if (!PeekNamedPipe (handle, NULL, 0, NULL, &avail, NULL)) - { - return G_IO_ERROR_UNKNOWN; - } - - count = MIN (count, avail); - - count = MAX (count, 1); /* Must read at least one byte, or - * caller will think it's EOF. - */ - /* g_print ("g_io_win32_pipe_read: %d %d\n", win32_channel->fd, count); */ - if (count == 0) - result = 0; - else - result = read (win32_channel->fd, buf, count); - if (result < 0) + GIOError error; + + if (win32_channel->thread_id) { - *bytes_read = 0; - switch (errno) + result = buffer_read (win32_channel, buf, count, &error); + if (result < 0) { - case EINVAL: - return G_IO_ERROR_INVAL; - case EAGAIN: - return G_IO_ERROR_AGAIN; - default: - return G_IO_ERROR_UNKNOWN; + *bytes_read = 0; + return error; } - } - else - { - *bytes_read = result; - win32_channel->offset += result; - /* g_print ("=%d (%d)\n", result, win32_channel->offset); */ - return G_IO_ERROR_NONE; - } -} - -static GIOError -g_io_win32_pipe_write(GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_written) -{ - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - LONG prevcnt; - gint result; - - /* g_print ("g_io_win32_pipe_write: %d %d\n", win32_channel->fd, count); */ - result = write (win32_channel->fd, buf, count); - if (result < 0) - { - *bytes_written = 0; - switch (errno) + else { - case EINVAL: - return G_IO_ERROR_INVAL; - case EAGAIN: - return G_IO_ERROR_AGAIN; - default: - return G_IO_ERROR_UNKNOWN; - } - } - else - { - if (g_pipe_readable_msg == 0) - g_pipe_readable_msg = RegisterWindowMessage ("g-pipe-readable"); - - win32_channel->offset += result; - /* g_print ("=%d (%d)\n", result, win32_channel->offset); */ - if (win32_channel->need_wakeups) - { - PostThreadMessage (win32_channel->peer, - g_pipe_readable_msg, - win32_channel->peer_fd, - win32_channel->offset); + *bytes_read = result; + return G_IO_ERROR_NONE; } - *bytes_written = result; - return G_IO_ERROR_NONE; - } -} - -static void -g_io_win32_pipe_close (GIOChannel *channel) -{ - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - - /* g_print ("g_io_win32_pipe_close: %#x %d\n", channel, win32_channel->fd); */ - - close (win32_channel->fd); - return; -} - -static guint -g_io_win32_pipe_add_watch (GIOChannel *channel, - gint priority, - GIOCondition condition, - GIOFunc func, - gpointer user_data, - GDestroyNotify notify) -{ - GIOWin32Watch *watch = g_new (GIOWin32Watch, 1); - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - gint i; - - /* g_print ("g_io_win32_pipe_add_watch: %d\n", win32_channel->fd); */ - - watch->channel = channel; - g_io_channel_ref (channel); - - watch->callback = func; - watch->condition = condition; - - watch->pollfd.fd = win32_channel->fd; - watch->pollfd.events = condition; - - for (i = 0; i < n_watched_pipes; i++) - if (watched_pipes[i].fd == -1) - break; - if (i == N_WATCHED_PIPES) - g_error ("Too many watched pipes"); - else - { - watched_pipes[i].fd = win32_channel->fd; - watched_pipes[i].watch = watch; - watched_pipes[i].channel = win32_channel; - watched_pipes[i].user_data = user_data; - n_watched_pipes = MAX (i + 1, n_watched_pipes); } - return g_source_add (priority, FALSE, &win32_watch_pipe_funcs, watch, user_data, notify); -} - -static void -g_io_win32_pipe_free (GIOChannel *channel) -{ - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - gint i; - - /* g_print ("g_io_win32_pipe_free: %#x %#x\n", channel, channel->channel_data); */ - - for (i = 0; i < n_watched_pipes; i++) - if (watched_pipes[i].fd == win32_channel->fd) - { - watched_pipes[i].fd = -1; - break; - } - g_io_win32_free (channel); -} - -static GIOError -g_io_win32_sock_read (GIOChannel *channel, - gchar *buf, - guint count, - guint *bytes_read) -{ - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - gint result; result = recv (win32_channel->fd, buf, count, 0); - if (result == SOCKET_ERROR) + + if (result < 0) { *bytes_read = 0; - switch (WSAGetLastError ()) - { - case WSAEINVAL: - return G_IO_ERROR_INVAL; - case WSAEWOULDBLOCK: - case WSAEINTR: - return G_IO_ERROR_AGAIN; - default: - return G_IO_ERROR_UNKNOWN; - } + return G_IO_ERROR_UNKNOWN; } else { @@ -843,18 +661,18 @@ g_io_win32_sock_read (GIOChannel *channel, return G_IO_ERROR_NONE; } } - -static GIOError -g_io_win32_sock_write(GIOChannel *channel, - gchar *buf, + +static GIOError +g_io_win32_sock_write(GIOChannel *channel, + gchar *buf, guint count, guint *bytes_written) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; gint result; - + result = send (win32_channel->fd, buf, count, 0); - + if (result == SOCKET_ERROR) { *bytes_written = 0; @@ -876,16 +694,23 @@ g_io_win32_sock_write(GIOChannel *channel, } } -static void +static void g_io_win32_sock_close (GIOChannel *channel) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; closesocket (win32_channel->fd); - return; } -static guint +static int +sock_reader (int fd, + guchar *buf, + int len) +{ + return recv (fd, buf, len, 0); +} + +static guint g_io_win32_sock_add_watch (GIOChannel *channel, gint priority, GIOCondition condition, @@ -895,21 +720,54 @@ g_io_win32_sock_add_watch (GIOChannel *channel, { GIOWin32Watch *watch = g_new (GIOWin32Watch, 1); GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - + watch->channel = channel; g_io_channel_ref (channel); watch->callback = func; watch->condition = condition; - watch->pollfd.fd = win32_channel->fd; + create_events (win32_channel); + + watch->pollfd.fd = (gint) win32_channel->data_avail_event; watch->pollfd.events = condition; + /* Sockets are always readable, aren't they? */ + create_reader_thread (win32_channel, sock_reader); + g_main_add_poll (&watch->pollfd, priority); - return g_source_add (priority, TRUE, &win32_watch_sock_funcs, watch, user_data, notify); + return g_source_add (priority, TRUE, &win32_watch_funcs, watch, + user_data, notify); } +static GIOFuncs win32_channel_msg_funcs = { + g_io_win32_msg_read, + g_io_win32_msg_write, + g_io_win32_no_seek, + g_io_win32_msg_close, + g_io_win32_msg_add_watch, + g_io_win32_free +}; + +static GIOFuncs win32_channel_fd_funcs = { + g_io_win32_fd_read, + g_io_win32_fd_write, + g_io_win32_fd_seek, + g_io_win32_fd_close, + g_io_win32_fd_add_watch, + g_io_win32_free +}; + +static GIOFuncs win32_channel_sock_funcs = { + g_io_win32_sock_read, + g_io_win32_sock_write, + g_io_win32_no_seek, + g_io_win32_sock_close, + g_io_win32_sock_add_watch, + g_io_win32_free +}; + GIOChannel * g_io_channel_win32_new_messages (guint hwnd) { @@ -918,7 +776,6 @@ g_io_channel_win32_new_messages (guint hwnd) g_io_channel_init (channel); channel->funcs = &win32_channel_msg_funcs; - win32_channel->fd = -1; win32_channel->type = G_IO_WINDOWS_MESSAGES; win32_channel->hwnd = (HWND) hwnd; @@ -926,21 +783,37 @@ g_io_channel_win32_new_messages (guint hwnd) } GIOChannel * -g_io_channel_unix_new (gint fd) +g_io_channel_win32_new_fd (gint fd) { - GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1); - GIOChannel *channel = (GIOChannel *) win32_channel; + GIOWin32Channel *win32_channel; + GIOChannel *channel; + struct stat st; + + if (fstat (fd, &st) == -1) + { + g_warning ("%d isn't a (emulated) file descriptor", fd); + return NULL; + } + + PRINT (("g_io_channel_win32_new_fd: %d\n", fd)); + + win32_channel = g_new (GIOWin32Channel, 1); + channel = (GIOChannel *) win32_channel; g_io_channel_init (channel); + channel->funcs = &win32_channel_fd_funcs; win32_channel->fd = fd; win32_channel->type = G_IO_FILE_DESC; + win32_channel->buffer = NULL; + win32_channel->running = FALSE; + win32_channel->thread_id = 0; return channel; } gint -g_io_channel_unix_get_fd (GIOChannel *channel) +g_io_channel_win32_get_fd (GIOChannel *channel) { GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; @@ -948,88 +821,92 @@ g_io_channel_unix_get_fd (GIOChannel *channel) } GIOChannel * -g_io_channel_win32_new_pipe_with_wakeups (int fd, - guint peer, - int peer_fd) +g_io_channel_win32_new_stream_socket (int socket) { GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1); GIOChannel *channel = (GIOChannel *) win32_channel; - /* g_print ("g_io_channel_win32_new_pipe_with_wakeups %d %#x %d\n", fd, peer, peer_fd); */ - g_io_channel_init (channel); - channel->funcs = &win32_channel_pipe_funcs; - win32_channel->fd = fd; - win32_channel->type = G_IO_PIPE; - win32_channel->peer = peer; - win32_channel->peer_fd = peer_fd; - win32_channel->offset = 0; - win32_channel->need_wakeups = TRUE; + channel->funcs = &win32_channel_sock_funcs; + win32_channel->fd = socket; + win32_channel->type = G_IO_STREAM_SOCKET; + win32_channel->buffer = NULL; + win32_channel->running = FALSE; + win32_channel->thread_id = 0; return channel; } GIOChannel * -g_io_channel_win32_new_pipe (int fd) +g_io_channel_unix_new (gint fd) { - GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1); - GIOChannel *channel = (GIOChannel *) win32_channel; - - g_io_channel_init (channel); - channel->funcs = &win32_channel_pipe_funcs; - win32_channel->fd = fd; - win32_channel->type = G_IO_PIPE; - win32_channel->offset = 0; - win32_channel->need_wakeups = FALSE; + return g_io_channel_win32_new_fd (fd); +} - return channel; +gint +g_io_channel_unix_get_fd (GIOChannel *channel) +{ + return g_io_channel_win32_get_fd (channel); } -GIOChannel * -g_io_channel_win32_new_stream_socket (int socket) +gint +g_io_channel_win32_wait_for_condition (GIOChannel *channel, + GIOCondition condition, + gint timeout) { - GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1); - GIOChannel *channel = (GIOChannel *) win32_channel; + GPollFD pollfd; + GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; + int result; - g_io_channel_init (channel); - channel->funcs = &win32_channel_sock_funcs; - win32_channel->fd = socket; - win32_channel->type = G_IO_STREAM_SOCKET; + pollfd.fd = (gint) win32_channel->data_avail_event; + pollfd.events = condition; - return channel; + PRINT (("g_io_channel_win32_wait_for_condition: fd:%d event:%#x timeout:%d\n", + win32_channel->fd, pollfd.fd, timeout)); + + result = (*g_main_win32_get_poll_func ()) (&pollfd, 1, timeout); + + PRINT (("g_io_channel_win32_wait_for_condition: done:%d\n", result)); + + return result; } -gint -g_io_channel_win32_get_fd (GIOChannel *channel) + +/* This variable and the functions below are present just to be + * binary compatible with old clients... But note that in GIMP, the + * libgimp/gimp.c:gimp_extension_process() function will have to be modified + * anyhow for this new approach. + * + * These will be removed after some weeks. + */ +guint g_pipe_readable_msg = 0; + +GIOChannel * +g_io_channel_win32_new_pipe (int fd) +{ + return g_io_channel_win32_new_fd (fd); +} + +GIOChannel * +g_io_channel_win32_new_pipe_with_wakeups (int fd, + guint peer, + int peer_fd) { - return g_io_channel_unix_get_fd (channel); + return g_io_channel_win32_new_fd (fd); } void g_io_channel_win32_pipe_request_wakeups (GIOChannel *channel, - guint peer, - int peer_fd) + guint peer, + int peer_fd) { - GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel; - - win32_channel->peer = peer; - win32_channel->peer_fd = peer_fd; - win32_channel->need_wakeups = TRUE; + /* Nothing needed now */ } void g_io_channel_win32_pipe_readable (gint fd, - guint offset) + guint offset) { - gint i; - - for (i = 0; i < n_watched_pipes; i++) - if (watched_pipes[i].fd == fd) - { - if ((watched_pipes[i].watch->condition & G_IO_IN) - && watched_pipes[i].channel->offset < offset) - (*watched_pipes[i].watch->callback) (watched_pipes[i].watch->channel, - G_IO_IN, - watched_pipes[i].user_data); - } + /* Nothing needed now */ } + |