@@ -78,6 +78,9 @@ typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc,
struct QIOChannel {
Object parent;
unsigned int features; /* bitmask of QIOChannelFeatures */
+#ifdef _WIN32
+ HANDLE event; /* For use with GSource on Win32 */
+#endif
};
/**
@@ -55,6 +55,10 @@ qio_channel_socket_new(void)
ioc = QIO_CHANNEL(sioc);
ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN);
+#ifdef WIN32
+ ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+
trace_qio_channel_socket_new(sioc);
return sioc;
@@ -341,6 +345,11 @@ qio_channel_socket_accept(QIOChannelSocket *ioc,
cioc->remoteAddrLen = sizeof(ioc->remoteAddr);
cioc->localAddrLen = sizeof(ioc->localAddr);
+#ifdef WIN32
+ QIO_CHANNEL(cioc)->event = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+
+
retry:
trace_qio_channel_socket_accept(ioc);
cioc->fd = qemu_accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr,
@@ -384,7 +393,10 @@ static void qio_channel_socket_finalize(Object *obj)
{
QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj);
if (ioc->fd != -1) {
- close(ioc->fd);
+#ifdef WIN32
+ WSAEventSelect(ioc->fd, NULL, 0);
+#endif
+ closesocket(ioc->fd);
ioc->fd = -1;
}
}
@@ -634,6 +646,11 @@ qio_channel_socket_set_blocking(QIOChannel *ioc,
qemu_set_block(sioc->fd);
} else {
qemu_set_nonblock(sioc->fd);
+#ifdef WIN32
+ WSAEventSelect(sioc->fd, ioc->event,
+ FD_READ | FD_ACCEPT | FD_CLOSE |
+ FD_CONNECT | FD_WRITE | FD_OOB);
+#endif
}
return 0;
}
@@ -669,13 +686,18 @@ qio_channel_socket_close(QIOChannel *ioc,
{
QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
- if (closesocket(sioc->fd) < 0) {
+ if (sioc->fd != -1) {
+#ifdef WIN32
+ WSAEventSelect(sioc->fd, NULL, 0);
+#endif
+ if (closesocket(sioc->fd) < 0) {
+ sioc->fd = -1;
+ error_setg_errno(errp, socket_error(),
+ "Unable to close socket");
+ return -1;
+ }
sioc->fd = -1;
- error_setg_errno(errp, socket_error(),
- "Unable to close socket");
- return -1;
}
- sioc->fd = -1;
return 0;
}
@@ -30,6 +30,20 @@ struct QIOChannelFDSource {
};
+#ifdef CONFIG_WIN32
+typedef struct QIOChannelSocketSource QIOChannelSocketSource;
+struct QIOChannelSocketSource {
+ GSource parent;
+ GPollFD fd;
+ QIOChannel *ioc;
+ SOCKET socket;
+ int revents;
+ GIOCondition condition;
+};
+
+#endif
+
+
typedef struct QIOChannelFDPairSource QIOChannelFDPairSource;
struct QIOChannelFDPairSource {
GSource parent;
@@ -82,6 +96,97 @@ qio_channel_fd_source_finalize(GSource *source)
}
+#ifdef CONFIG_WIN32
+static gboolean
+qio_channel_socket_source_prepare(GSource *source G_GNUC_UNUSED,
+ gint *timeout)
+{
+ *timeout = -1;
+
+ return FALSE;
+}
+
+
+/*
+ * NB, this impl only works when the socket is in non-blocking
+ * mode on Win32
+ */
+static gboolean
+qio_channel_socket_source_check(GSource *source)
+{
+ static struct timeval tv0;
+
+ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
+ WSANETWORKEVENTS ev;
+ fd_set rfds, wfds, xfds;
+
+ if (!ssource->condition) {
+ return 0;
+ }
+
+ WSAEnumNetworkEvents(ssource->socket, ssource->ioc->event, &ev);
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+ if (ssource->condition & G_IO_IN) {
+ FD_SET((SOCKET)ssource->socket, &rfds);
+ }
+ if (ssource->condition & G_IO_OUT) {
+ FD_SET((SOCKET)ssource->socket, &wfds);
+ }
+ if (ssource->condition & G_IO_PRI) {
+ FD_SET((SOCKET)ssource->socket, &xfds);
+ }
+ ssource->revents = 0;
+ if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) {
+ return 0;
+ }
+
+ if (FD_ISSET(ssource->socket, &rfds)) {
+ ssource->revents |= G_IO_IN;
+ }
+ if (FD_ISSET(ssource->socket, &wfds)) {
+ ssource->revents |= G_IO_OUT;
+ }
+ if (FD_ISSET(ssource->socket, &xfds)) {
+ ssource->revents |= G_IO_PRI;
+ }
+
+ return ssource->revents;
+}
+
+
+static gboolean
+qio_channel_socket_source_dispatch(GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ QIOChannelFunc func = (QIOChannelFunc)callback;
+ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
+
+ return (*func)(ssource->ioc, ssource->revents, user_data);
+}
+
+
+static void
+qio_channel_socket_source_finalize(GSource *source)
+{
+ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
+
+ object_unref(OBJECT(ssource->ioc));
+}
+
+
+GSourceFuncs qio_channel_socket_source_funcs = {
+ qio_channel_socket_source_prepare,
+ qio_channel_socket_source_check,
+ qio_channel_socket_source_dispatch,
+ qio_channel_socket_source_finalize
+};
+#endif
+
+
static gboolean
qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED,
gint *timeout)
@@ -177,7 +282,26 @@ GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
int socket,
GIOCondition condition)
{
- abort();
+ GSource *source;
+ QIOChannelSocketSource *ssource;
+
+ source = g_source_new(&qio_channel_socket_source_funcs,
+ sizeof(QIOChannelSocketSource));
+ ssource = (QIOChannelSocketSource *)source;
+
+ ssource->ioc = ioc;
+ object_ref(OBJECT(ioc));
+
+ ssource->condition = condition;
+ ssource->socket = socket;
+ ssource->revents = 0;
+
+ ssource->fd.fd = (gintptr)ioc->event;
+ ssource->fd.events = G_IO_IN;
+
+ g_source_add_poll(source, &ssource->fd);
+
+ return source;
}
#else
GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
@@ -274,10 +274,24 @@ void qio_channel_wait(QIOChannel *ioc,
}
+#ifdef _WIN32
+static void qio_channel_finalize(Object *obj)
+{
+ QIOChannel *ioc = QIO_CHANNEL(obj);
+
+ if (ioc->event) {
+ CloseHandle(ioc->event);
+ }
+}
+#endif
+
static const TypeInfo qio_channel_info = {
.parent = TYPE_OBJECT,
.name = TYPE_QIO_CHANNEL,
.instance_size = sizeof(QIOChannel),
+#ifdef _WIN32
+ .instance_finalize = qio_channel_finalize,
+#endif
.abstract = true,
.class_size = sizeof(QIOChannelClass),
};