diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk index 46f546615f..d43fa501aa 100644 --- a/build-aux/syntax-check.mk +++ b/build-aux/syntax-check.mk @@ -2339,3 +2339,6 @@ exclude_file_name_regexp--sc_prohibit_backslash_alignment = \ exclude_file_name_regexp--sc_prohibit_always_true_header_tests = \ ^src/util/(virfile|virnetdev|virnetdevip)\.[c,h]|$$ + +exclude_file_name_regexp--sc_prohibit_select = \ + ^build-aux/syntax-check\.mk|src/util/vireventglibwatch\.c$$ diff --git a/src/util/Makefile.inc.am b/src/util/Makefile.inc.am index 2abdca548c..a51cba736b 100644 --- a/src/util/Makefile.inc.am +++ b/src/util/Makefile.inc.am @@ -61,6 +61,8 @@ UTIL_SOURCES = \ util/virerrorpriv.h \ util/virevent.c \ util/virevent.h \ + util/vireventglibwatch.c \ + util/vireventglibwatch.h \ util/vireventpoll.c \ util/vireventpoll.h \ util/virfcp.c \ diff --git a/src/util/vireventglibwatch.c b/src/util/vireventglibwatch.c new file mode 100644 index 0000000000..7694e74f23 --- /dev/null +++ b/src/util/vireventglibwatch.c @@ -0,0 +1,249 @@ +/* + * vireventglibwatch.c: GSource impl for sockets + * + * Copyright (C) 2015-2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include "vireventglibwatch.h" + +#ifndef WIN32 +typedef struct virEventGLibFDSource virEventGLibFDSource; +struct virEventGLibFDSource { + GSource parent; + GPollFD pollfd; + int fd; + GIOCondition condition; +}; + + +static gboolean +virEventGLibFDSourcePrepare(GSource *source G_GNUC_UNUSED, + gint *timeout) +{ + *timeout = -1; + + return FALSE; +} + + +static gboolean +virEventGLibFDSourceCheck(GSource *source) +{ + virEventGLibFDSource *ssource = (virEventGLibFDSource *)source; + + return ssource->pollfd.revents & ssource->condition; +} + + +static gboolean +virEventGLibFDSourceDispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + virEventGLibSocketFunc func = (virEventGLibSocketFunc)callback; + virEventGLibFDSource *ssource = (virEventGLibFDSource *)source; + + return (*func)(ssource->fd, + ssource->pollfd.revents & ssource->condition, + user_data); +} + + +static void +virEventGLibFDSourceFinalize(GSource *source G_GNUC_UNUSED) +{ +} + + +GSourceFuncs virEventGLibFDSourceFuncs = { + .prepare = virEventGLibFDSourcePrepare, + .check = virEventGLibFDSourceCheck, + .dispatch = virEventGLibFDSourceDispatch, + .finalize = virEventGLibFDSourceFinalize +}; + + +GSource *virEventGLibCreateSocketWatch(int fd, + GIOCondition condition) +{ + GSource *source; + virEventGLibFDSource *ssource; + + source = g_source_new(&virEventGLibFDSourceFuncs, + sizeof(virEventGLibFDSource)); + ssource = (virEventGLibFDSource *)source; + + ssource->condition = condition; + ssource->fd = fd; + + ssource->pollfd.fd = fd; + ssource->pollfd.events = condition; + + g_source_add_poll(source, &ssource->pollfd); + + return source; +} + +#else /* WIN32 */ + +# define WIN32_LEAN_AND_MEAN +# include + +typedef struct virEventGLibSocketSource virEventGLibSocketSource; +struct virEventGLibSocketSource { + GSource parent; + GPollFD pollfd; + int fd; + SOCKET socket; + HANDLE event; + int revents; + GIOCondition condition; +}; + + +static gboolean +virEventGLibSocketSourcePrepare(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 +virEventGLibSocketSourceCheck(GSource *source) +{ + static struct timeval tv0; + + virEventGLibSocketSource *ssource = (virEventGLibSocketSource *)source; + WSANETWORKEVENTS ev; + fd_set rfds, wfds, xfds; + + if (!ssource->condition) + return 0; + + WSAEnumNetworkEvents(ssource->socket, ssource->event, &ev); + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + if (ssource->condition & G_IO_IN) + FD_SET(ssource->socket, &rfds); + if (ssource->condition & G_IO_OUT) + FD_SET(ssource->socket, &wfds); + if (ssource->condition & G_IO_PRI) + FD_SET(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 +virEventGLibSocketSourceDispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + virEventGLibSocketFunc func = (virEventGLibSocketFunc)callback; + virEventGLibSocketSource *ssource = (virEventGLibSocketSource *)source; + + return (*func)(ssource->fd, ssource->revents, user_data); +} + + +static void +virEventGLibSocketSourceFinalize(GSource *source) +{ + virEventGLibSocketSource *ssource = (virEventGLibSocketSource *)source; + + WSAEventSelect(ssource->socket, NULL, 0); + CloseHandle(ssource->event); +} + + +GSourceFuncs virEventGLibSocketSourceFuncs = { + .prepare = virEventGLibSocketSourcePrepare, + .check = virEventGLibSocketSourceCheck, + .dispatch = virEventGLibSocketSourceDispatch, + .finalize = virEventGLibSocketSourceFinalize +}; + + +GSource *virEventGLibCreateSocketWatch(int fd, + GIOCondition condition) +{ + GSource *source; + virEventGLibSocketSource *ssource; + + source = g_source_new(&virEventGLibSocketSourceFuncs, + sizeof(virEventGLibSocketSource)); + ssource = (virEventGLibSocketSource *)source; + + ssource->condition = condition; + ssource->fd = fd; + ssource->socket = _get_osfhandle(fd); + ssource->event = CreateEvent(NULL, FALSE, FALSE, NULL); + ssource->revents = 0; + + ssource->pollfd.fd = (gintptr)ssource->event; + ssource->pollfd.events = G_IO_IN; + + WSAEventSelect(ssource->socket, ssource->event, + FD_READ | FD_ACCEPT | FD_CLOSE | + FD_CONNECT | FD_WRITE | FD_OOB); + + g_source_add_poll(source, &ssource->pollfd); + + return source; +} + +#endif /* WIN32 */ + + +guint virEventGLibAddSocketWatch(int fd, + GIOCondition condition, + GMainContext *context, + virEventGLibSocketFunc func, + gpointer opaque, + GDestroyNotify notify) +{ + g_autoptr(GSource) source = NULL; + + source = virEventGLibCreateSocketWatch(fd, condition); + g_source_set_callback(source, (GSourceFunc)func, opaque, notify); + + return g_source_attach(source, context); +} diff --git a/src/util/vireventglibwatch.h b/src/util/vireventglibwatch.h new file mode 100644 index 0000000000..2f7e61cfba --- /dev/null +++ b/src/util/vireventglibwatch.h @@ -0,0 +1,48 @@ +/* + * vireventglibwatch.h: GSource impl for sockets + * + * Copyright (C) 2015-2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#pragma once + +#include "internal.h" + +/** + * virEventGLibCreateSocketWatch: + * @fd: the file descriptor + * @condition: the I/O condition + * + * Create a new main loop source that is able to + * monitor the file descriptor @fd for the + * I/O conditions in @condition. + * + * Returns: the new main loop source + */ +GSource *virEventGLibCreateSocketWatch(int fd, + GIOCondition condition); + +typedef gboolean (*virEventGLibSocketFunc)(int fd, + GIOCondition condition, + gpointer data); + +guint virEventGLibAddSocketWatch(int fd, + GIOCondition condition, + GMainContext *context, + virEventGLibSocketFunc func, + gpointer opaque, + GDestroyNotify notify);