X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgpoll.c;h=5afc7596d14081c9d10b9d9476a12e23416f915d;hb=073339230bc0c4b42387f786c6b0d7aaca3f1a81;hp=940abae38f6d1a1ed00f9df6b2d02c08726d99a3;hpb=d85b722734a6fcfe94032f6113de9e5c190fd7c3;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gpoll.c b/glib/gpoll.c index 940abae..5afc759 100644 --- a/glib/gpoll.c +++ b/glib/gpoll.c @@ -5,10 +5,12 @@ * Copyright 1998 Owen Taylor * Copyright 2008 Red Hat, Inc. * + * SPDX-License-Identifier: LGPL-2.1-or-later + * * 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 of the License, or (at your option) any later version. + * 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 @@ -16,9 +18,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library; if not, see . */ /* @@ -55,10 +55,8 @@ #ifdef HAVE_SYS_TIME_H #include #endif /* HAVE_SYS_TIME_H */ -#ifdef GLIB_HAVE_SYS_POLL_H -# include -# undef events /* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */ -# undef revents /* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */ +#ifdef HAVE_POLL +# include /* The poll() emulation on OS/X doesn't handle fds=NULL, nfds=0, * so we prefer our own poll emulation. @@ -68,14 +66,15 @@ #endif #endif /* GLIB_HAVE_SYS_POLL_H */ -#ifdef HAVE_UNISTD_H +#ifdef G_OS_UNIX #include -#endif /* HAVE_UNISTD_H */ +#endif /* G_OS_UNIX */ #include #ifdef G_OS_WIN32 #define STRICT #include +#include #endif /* G_OS_WIN32 */ #include "gpoll.h" @@ -89,10 +88,6 @@ extern gboolean _g_main_poll_debug; #endif #ifdef HAVE_POLL -/* SunOS has poll, but doesn't provide a prototype. */ -# if defined (sun) && !defined (__SVR4) -extern gint poll (struct pollfd *fds, guint nfsd, gint timeout); -# endif /* !sun */ /** * g_poll: @@ -107,18 +102,18 @@ extern gint poll (struct pollfd *fds, guint nfsd, gint timeout); * don't want to run the full main loop. * * Each element of @fds is a #GPollFD describing a single file - * descriptor to poll. The %fd field indicates the file descriptor, - * and the %events field indicates the events to poll for. On return, - * the %revents fields will be filled with the events that actually + * descriptor to poll. The @fd field indicates the file descriptor, + * and the @events field indicates the events to poll for. On return, + * the @revents fields will be filled with the events that actually * occurred. * * On POSIX systems, the file descriptors in @fds can be any sort of * file descriptor, but the situation is much more complicated on * Windows. If you need to use g_poll() in code that has to run on * Windows, the easiest solution is to construct all of your - * #GPollFDs with g_io_channel_win32_make_pollfd(). + * #GPollFDs with g_io_channel_win32_make_pollfd(). * - * Return value: the number of entries in @fds whose %revents fields + * Returns: the number of entries in @fds whose @revents fields * were filled in, or 0 if the operation timed out, or -1 on error or * if the call was interrupted. * @@ -137,26 +132,26 @@ g_poll (GPollFD *fds, #ifdef G_OS_WIN32 static int -poll_rest (gboolean poll_msgs, - HANDLE *handles, - gint nhandles, - GPollFD *fds, - guint nfds, - gint timeout) +poll_rest (GPollFD *msg_fd, + GPollFD *stop_fd, + HANDLE *handles, + GPollFD *handle_to_fd[], + gint nhandles, + DWORD timeout_ms) { DWORD ready; GPollFD *f; int recursed_result; - if (poll_msgs) + if (msg_fd != NULL) { /* Wait for either messages or handles * -> Use MsgWaitForMultipleObjectsEx */ if (_g_main_poll_debug) - g_print (" MsgWaitForMultipleObjectsEx(%d, %d)\n", nhandles, timeout); + g_print (" MsgWaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms); - ready = MsgWaitForMultipleObjectsEx (nhandles, handles, timeout, + ready = MsgWaitForMultipleObjectsEx (nhandles, handles, timeout_ms, QS_ALLINPUT, MWMO_ALERTABLE); if (ready == WAIT_FAILED) @@ -169,13 +164,14 @@ poll_rest (gboolean poll_msgs, else if (nhandles == 0) { /* No handles to wait for, just the timeout */ - if (timeout == INFINITE) + if (timeout_ms == INFINITE) ready = WAIT_FAILED; else - { - SleepEx (timeout, TRUE); - ready = WAIT_TIMEOUT; - } + { + /* Wait for the current process to die, more efficient than SleepEx(). */ + WaitForSingleObjectEx (GetCurrentProcess (), timeout_ms, TRUE); + ready = WAIT_TIMEOUT; + } } else { @@ -183,9 +179,9 @@ poll_rest (gboolean poll_msgs, * -> Use WaitForMultipleObjectsEx */ if (_g_main_poll_debug) - g_print (" WaitForMultipleObjectsEx(%d, %d)\n", nhandles, timeout); + g_print (" WaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms); - ready = WaitForMultipleObjectsEx (nhandles, handles, FALSE, timeout, TRUE); + ready = WaitForMultipleObjectsEx (nhandles, handles, FALSE, timeout_ms, TRUE); if (ready == WAIT_FAILED) { gchar *emsg = g_win32_error_message (GetLastError ()); @@ -199,151 +195,299 @@ poll_rest (gboolean poll_msgs, ready, (ready == WAIT_FAILED ? " (WAIT_FAILED)" : (ready == WAIT_TIMEOUT ? " (WAIT_TIMEOUT)" : - (poll_msgs && ready == WAIT_OBJECT_0 + nhandles ? " (msg)" : "")))); + (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles ? " (msg)" : "")))); if (ready == WAIT_FAILED) return -1; else if (ready == WAIT_TIMEOUT || ready == WAIT_IO_COMPLETION) return 0; - else if (poll_msgs && ready == WAIT_OBJECT_0 + nhandles) + else if (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles) { - for (f = fds; f < &fds[nfds]; ++f) - if (f->fd == G_WIN32_MSG_HANDLE && f->events & G_IO_IN) - f->revents |= G_IO_IN; + msg_fd->revents |= G_IO_IN; /* If we have a timeout, or no handles to poll, be satisfied * with just noticing we have messages waiting. */ - if (timeout != 0 || nhandles == 0) + if (timeout_ms != 0 || nhandles == 0) return 1; /* If no timeout and handles to poll, recurse to poll them, * too. */ - recursed_result = poll_rest (FALSE, handles, nhandles, fds, nfds, 0); + recursed_result = poll_rest (NULL, stop_fd, handles, handle_to_fd, nhandles, 0); return (recursed_result == -1) ? -1 : 1 + recursed_result; } - else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) + else if (ready < WAIT_OBJECT_0 + nhandles) { - for (f = fds; f < &fds[nfds]; ++f) - { - if ((HANDLE) f->fd == handles[ready - WAIT_OBJECT_0]) - { - f->revents = f->events; - if (_g_main_poll_debug) - g_print (" got event %p\n", (HANDLE) f->fd); - } - } + int retval; + + f = handle_to_fd[ready - WAIT_OBJECT_0]; + f->revents = f->events; + if (_g_main_poll_debug) + g_print (" got event %p\n", (HANDLE) f->fd); + + /* Do not count the stop_fd */ + retval = (f != stop_fd) ? 1 : 0; /* If no timeout and polling several handles, recurse to poll * the rest of them. */ - if (timeout == 0 && nhandles > 1) - { - /* Remove the handle that fired */ - int i; - if (ready < nhandles - 1) - for (i = ready - WAIT_OBJECT_0 + 1; i < nhandles; i++) - handles[i-1] = handles[i]; - nhandles--; - recursed_result = poll_rest (FALSE, handles, nhandles, fds, nfds, 0); - return (recursed_result == -1) ? -1 : 1 + recursed_result; - } - return 1; + if (timeout_ms == 0 && nhandles > 1) + { + /* Poll the handles with index > ready */ + HANDLE *shorter_handles; + GPollFD **shorter_handle_to_fd; + gint shorter_nhandles; + + shorter_handles = &handles[ready - WAIT_OBJECT_0 + 1]; + shorter_handle_to_fd = &handle_to_fd[ready - WAIT_OBJECT_0 + 1]; + shorter_nhandles = nhandles - (ready - WAIT_OBJECT_0 + 1); + + recursed_result = poll_rest (NULL, stop_fd, shorter_handles, shorter_handle_to_fd, shorter_nhandles, 0); + return (recursed_result == -1) ? -1 : retval + recursed_result; + } + return retval; } return 0; } +typedef struct +{ + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + GPollFD *handle_to_fd[MAXIMUM_WAIT_OBJECTS]; + GPollFD *msg_fd; + GPollFD *stop_fd; + gint nhandles; + DWORD timeout_ms; +} GWin32PollThreadData; + +static gint +poll_single_thread (GWin32PollThreadData *data) +{ + int retval; + + /* Polling for several things? */ + if (data->nhandles > 1 || (data->nhandles > 0 && data->msg_fd != NULL)) + { + /* First check if one or several of them are immediately + * available + */ + retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, 0); + + /* If not, and we have a significant timeout, poll again with + * timeout then. Note that this will return indication for only + * one event, or only for messages. + */ + if (retval == 0 && (data->timeout_ms == INFINITE || data->timeout_ms > 0)) + retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, data->timeout_ms); + } + else + { + /* Just polling for one thing, so no need to check first if + * available immediately + */ + retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, data->timeout_ms); + } + + return retval; +} + +static void +fill_poll_thread_data (GPollFD *fds, + guint nfds, + DWORD timeout_ms, + GPollFD *stop_fd, + GWin32PollThreadData *data) +{ + GPollFD *f; + + data->timeout_ms = timeout_ms; + + if (stop_fd != NULL) + { + if (_g_main_poll_debug) + g_print (" Stop FD: %p", (HANDLE) stop_fd->fd); + + g_assert (data->nhandles < MAXIMUM_WAIT_OBJECTS); + + data->stop_fd = stop_fd; + data->handle_to_fd[data->nhandles] = stop_fd; + data->handles[data->nhandles++] = (HANDLE) stop_fd->fd; + } + + for (f = fds; f < &fds[nfds]; ++f) + { + if ((data->nhandles == MAXIMUM_WAIT_OBJECTS) || + (data->msg_fd != NULL && (data->nhandles == MAXIMUM_WAIT_OBJECTS - 1))) + { + g_warning ("Too many handles to wait for!"); + break; + } + + if (f->fd == G_WIN32_MSG_HANDLE && (f->events & G_IO_IN)) + { + if (_g_main_poll_debug && data->msg_fd == NULL) + g_print (" MSG"); + data->msg_fd = f; + } + else if (f->fd > 0) + { + if (_g_main_poll_debug) + g_print (" %p", (HANDLE) f->fd); + data->handle_to_fd[data->nhandles] = f; + data->handles[data->nhandles++] = (HANDLE) f->fd; + } + + f->revents = 0; + } +} + +static guint __stdcall +poll_thread_run (gpointer user_data) +{ + GWin32PollThreadData *data = user_data; + + /* Docs say that it is safer to call _endthreadex by our own: + * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/endthread-endthreadex + */ + _endthreadex (poll_single_thread (data)); + + g_assert_not_reached (); + + return 0; +} + +/* One slot for a possible msg object or the stop event */ +#define MAXIMUM_WAIT_OBJECTS_PER_THREAD (MAXIMUM_WAIT_OBJECTS - 1) + gint g_poll (GPollFD *fds, guint nfds, gint timeout) { - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; - gboolean poll_msgs = FALSE; + guint nthreads, threads_remain; + HANDLE thread_handles[MAXIMUM_WAIT_OBJECTS]; + GWin32PollThreadData *threads_data; + GPollFD stop_event = { 0, }; GPollFD *f; - gint nhandles = 0; + guint i, fds_idx = 0; + DWORD ready; + DWORD thread_retval; int retval; + GPollFD *msg_fd = NULL; - if (_g_main_poll_debug) - g_print ("g_poll: waiting for"); + if (timeout == -1) + timeout = INFINITE; - for (f = fds; f < &fds[nfds]; ++f) - if (f->fd == G_WIN32_MSG_HANDLE && (f->events & G_IO_IN)) - { - if (_g_main_poll_debug && !poll_msgs) - g_print (" MSG"); - poll_msgs = TRUE; - } - else if (f->fd > 0) - { - /* Don't add the same handle several times into the array, as - * docs say that is not allowed, even if it actually does seem - * to work. - */ - gint i; + /* Simple case without extra threads */ + if (nfds <= MAXIMUM_WAIT_OBJECTS) + { + GWin32PollThreadData data = { 0, }; - for (i = 0; i < nhandles; i++) - if (handles[i] == (HANDLE) f->fd) - break; + if (_g_main_poll_debug) + g_print ("g_poll: waiting for"); - if (i == nhandles) - { - if (nhandles == MAXIMUM_WAIT_OBJECTS) - { - g_warning ("Too many handles to wait for!\n"); - break; - } - else - { - if (_g_main_poll_debug) - g_print (" %p", (HANDLE) f->fd); - handles[nhandles++] = (HANDLE) f->fd; - } - } - } + fill_poll_thread_data (fds, nfds, timeout, NULL, &data); + + if (_g_main_poll_debug) + g_print ("\n"); + + retval = poll_single_thread (&data); + if (retval == -1) + for (f = fds; f < &fds[nfds]; ++f) + f->revents = 0; + + return retval; + } if (_g_main_poll_debug) - g_print ("\n"); + g_print ("g_poll: polling with threads\n"); - for (f = fds; f < &fds[nfds]; ++f) - f->revents = 0; + nthreads = nfds / MAXIMUM_WAIT_OBJECTS_PER_THREAD; + threads_remain = nfds % MAXIMUM_WAIT_OBJECTS_PER_THREAD; + if (threads_remain > 0) + nthreads++; - if (timeout == -1) - timeout = INFINITE; + if (nthreads > MAXIMUM_WAIT_OBJECTS_PER_THREAD) + { + g_warning ("Too many handles to wait for in threads!"); + nthreads = MAXIMUM_WAIT_OBJECTS_PER_THREAD; + } - /* Polling for several things? */ - if (nhandles > 1 || (nhandles > 0 && poll_msgs)) +#if GLIB_SIZEOF_VOID_P == 8 + stop_event.fd = (gint64)CreateEventW (NULL, TRUE, FALSE, NULL); +#else + stop_event.fd = (gint)CreateEventW (NULL, TRUE, FALSE, NULL); +#endif + stop_event.events = G_IO_IN; + + threads_data = g_new0 (GWin32PollThreadData, nthreads); + for (i = 0; i < nthreads; i++) { - /* First check if one or several of them are immediately - * available - */ - retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, 0); + guint thread_fds; + guint ignore; - /* If not, and we have a significant timeout, poll again with - * timeout then. Note that this will return indication for only - * one event, or only for messages. We ignore timeouts less than - * ten milliseconds as they are mostly pointless on Windows, the - * MsgWaitForMultipleObjectsEx() call will timeout right away - * anyway. - */ - if (retval == 0 && (timeout == INFINITE || timeout >= 10)) - retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout); + if (i == (nthreads - 1) && threads_remain > 0) + thread_fds = threads_remain; + else + thread_fds = MAXIMUM_WAIT_OBJECTS_PER_THREAD; + + fill_poll_thread_data (fds + fds_idx, thread_fds, timeout, &stop_event, &threads_data[i]); + fds_idx += thread_fds; + + /* We must poll for messages from the same thread, so poll it along with the threads */ + if (threads_data[i].msg_fd != NULL) + { + msg_fd = threads_data[i].msg_fd; + threads_data[i].msg_fd = NULL; + } + + thread_handles[i] = (HANDLE) _beginthreadex (NULL, 0, poll_thread_run, &threads_data[i], 0, &ignore); } + + /* Wait for at least one thread to return */ + if (msg_fd != NULL) + ready = MsgWaitForMultipleObjectsEx (nthreads, thread_handles, timeout, + QS_ALLINPUT, MWMO_ALERTABLE); else + ready = WaitForMultipleObjects (nthreads, thread_handles, FALSE, timeout); + + /* Signal the stop in case any of the threads did not stop yet */ + if (!SetEvent ((HANDLE)stop_event.fd)) { - /* Just polling for one thing, so no need to check first if - * available immediately - */ - retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout); + gchar *emsg = g_win32_error_message (GetLastError ()); + g_warning ("gpoll: failed to signal the stop event: %s", emsg); + g_free (emsg); + } + + /* Wait for the rest of the threads to finish */ + WaitForMultipleObjects (nthreads, thread_handles, TRUE, INFINITE); + + /* The return value of all the threads give us all the fds that changed state */ + retval = 0; + if (msg_fd != NULL && ready == WAIT_OBJECT_0 + nthreads) + { + msg_fd->revents |= G_IO_IN; + retval = 1; + } + + for (i = 0; i < nthreads; i++) + { + if (GetExitCodeThread (thread_handles[i], &thread_retval)) + retval = (retval == -1) ? -1 : ((thread_retval == (DWORD) -1) ? -1 : (int) (retval + thread_retval)); + + CloseHandle (thread_handles[i]); } if (retval == -1) for (f = fds; f < &fds[nfds]; ++f) f->revents = 0; + g_free (threads_data); + CloseHandle ((HANDLE)stop_event.fd); + return retval; } @@ -359,30 +503,13 @@ g_poll (GPollFD *fds, #include #endif /* HAVE_SYS_SELECT_H */ -#ifdef G_OS_BEOS -#undef NO_FD_SET -#endif /* G_OS_BEOS */ - -#ifndef NO_FD_SET -# define SELECT_MASK fd_set -#else /* !NO_FD_SET */ -# ifndef _AIX -typedef long fd_mask; -# endif /* _AIX */ -# ifdef _IBMR2 -# define SELECT_MASK void -# else /* !_IBMR2 */ -# define SELECT_MASK int -# endif /* !_IBMR2 */ -#endif /* !NO_FD_SET */ - gint g_poll (GPollFD *fds, guint nfds, gint timeout) { struct timeval tv; - SELECT_MASK rset, wset, xset; + fd_set rset, wset, xset; GPollFD *f; int ready; int maxfd = 0;