Branch data Line data Source code
1 : : /* GLIB - Library of useful routines for C programming
2 : : * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 : : *
4 : : * gpoll.c: poll(2) abstraction
5 : : * Copyright 1998 Owen Taylor
6 : : * Copyright 2008 Red Hat, Inc.
7 : : *
8 : : * SPDX-License-Identifier: LGPL-2.1-or-later
9 : : *
10 : : * This library is free software; you can redistribute it and/or
11 : : * modify it under the terms of the GNU Lesser General Public
12 : : * License as published by the Free Software Foundation; either
13 : : * version 2.1 of the License, or (at your option) any later version.
14 : : *
15 : : * This library is distributed in the hope that it will be useful,
16 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 : : * Lesser General Public License for more details.
19 : : *
20 : : * You should have received a copy of the GNU Lesser General Public
21 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 : : */
23 : :
24 : : /*
25 : : * Modified by the GLib Team and others 1997-2000. See the AUTHORS
26 : : * file for a list of people on the GLib Team. See the ChangeLog
27 : : * files for a list of changes. These files are distributed with
28 : : * GLib at ftp://ftp.gtk.org/pub/gtk/.
29 : : */
30 : :
31 : : /*
32 : : * MT safe
33 : : */
34 : :
35 : : #include "config.h"
36 : : #include "glibconfig.h"
37 : : #include "giochannel.h"
38 : :
39 : : /* Uncomment the next line (and the corresponding line in gmain.c) to
40 : : * enable debugging printouts if the environment variable
41 : : * G_MAIN_POLL_DEBUG is set to some value.
42 : : */
43 : : /* #define G_MAIN_POLL_DEBUG */
44 : :
45 : : #ifdef _WIN32
46 : : /* Always enable debugging printout on Windows, as it is more often
47 : : * needed there...
48 : : */
49 : : #define G_MAIN_POLL_DEBUG
50 : : #endif
51 : :
52 : : #include <sys/types.h>
53 : : #include <time.h>
54 : : #include <stdlib.h>
55 : : #ifdef HAVE_SYS_TIME_H
56 : : #include <sys/time.h>
57 : : #endif /* HAVE_SYS_TIME_H */
58 : : #ifdef HAVE_POLL
59 : : # include <poll.h>
60 : :
61 : : /* The poll() emulation on OS/X doesn't handle fds=NULL, nfds=0,
62 : : * so we prefer our own poll emulation.
63 : : */
64 : : #if defined(_POLL_EMUL_H_) || defined(BROKEN_POLL)
65 : : #undef HAVE_POLL
66 : : #endif
67 : :
68 : : #endif /* GLIB_HAVE_SYS_POLL_H */
69 : : #ifdef G_OS_UNIX
70 : : #include <unistd.h>
71 : : #endif /* G_OS_UNIX */
72 : : #include <errno.h>
73 : :
74 : : #ifdef G_OS_WIN32
75 : : #define STRICT
76 : : #include <windows.h>
77 : : #include <process.h>
78 : : #endif /* G_OS_WIN32 */
79 : :
80 : : #include "gpoll.h"
81 : :
82 : : #ifdef G_OS_WIN32
83 : : #include "gprintf.h"
84 : : #endif
85 : :
86 : : #ifdef G_MAIN_POLL_DEBUG
87 : : extern gboolean _g_main_poll_debug;
88 : : #endif
89 : :
90 : : #ifdef HAVE_POLL
91 : :
92 : : /**
93 : : * g_poll:
94 : : * @fds: file descriptors to poll
95 : : * @nfds: the number of file descriptors in @fds
96 : : * @timeout: amount of time to wait, in milliseconds, or -1 to wait forever
97 : : *
98 : : * Polls @fds, as with the poll() system call, but portably. (On
99 : : * systems that don't have poll(), it is emulated using select().)
100 : : * This is used internally by #GMainContext, but it can be called
101 : : * directly if you need to block until a file descriptor is ready, but
102 : : * don't want to run the full main loop.
103 : : *
104 : : * Each element of @fds is a #GPollFD describing a single file
105 : : * descriptor to poll. The @fd field indicates the file descriptor,
106 : : * and the @events field indicates the events to poll for. On return,
107 : : * the @revents fields will be filled with the events that actually
108 : : * occurred.
109 : : *
110 : : * On POSIX systems, the file descriptors in @fds can be any sort of
111 : : * file descriptor, but the situation is much more complicated on
112 : : * Windows. If you need to use g_poll() in code that has to run on
113 : : * Windows, the easiest solution is to construct all of your
114 : : * #GPollFDs with g_io_channel_win32_make_pollfd().
115 : : *
116 : : * Returns: the number of entries in @fds whose @revents fields
117 : : * were filled in, or 0 if the operation timed out, or -1 on error or
118 : : * if the call was interrupted.
119 : : *
120 : : * Since: 2.20
121 : : **/
122 : : gint
123 : 1092530 : g_poll (GPollFD *fds,
124 : : guint nfds,
125 : : gint timeout)
126 : : {
127 : 1092530 : return poll ((struct pollfd *)fds, nfds, timeout);
128 : : }
129 : :
130 : : #else /* !HAVE_POLL */
131 : :
132 : : #ifdef G_OS_WIN32
133 : :
134 : : static int
135 : : poll_rest (GPollFD *msg_fd,
136 : : GPollFD *stop_fd,
137 : : HANDLE *handles,
138 : : GPollFD *handle_to_fd[],
139 : : gint nhandles,
140 : : DWORD timeout_ms)
141 : : {
142 : : DWORD ready;
143 : : GPollFD *f;
144 : : int recursed_result;
145 : :
146 : : if (msg_fd != NULL)
147 : : {
148 : : /* Wait for either messages or handles
149 : : * -> Use MsgWaitForMultipleObjectsEx
150 : : */
151 : : if (_g_main_poll_debug)
152 : : g_print (" MsgWaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms);
153 : :
154 : : ready = MsgWaitForMultipleObjectsEx (nhandles, handles, timeout_ms,
155 : : QS_ALLINPUT, MWMO_ALERTABLE);
156 : :
157 : : if (ready == WAIT_FAILED)
158 : : {
159 : : gchar *emsg = g_win32_error_message (GetLastError ());
160 : : g_warning ("MsgWaitForMultipleObjectsEx failed: %s", emsg);
161 : : g_free (emsg);
162 : : }
163 : : }
164 : : else if (nhandles == 0)
165 : : {
166 : : /* No handles to wait for, just the timeout */
167 : : if (timeout_ms == INFINITE)
168 : : ready = WAIT_FAILED;
169 : : else
170 : : {
171 : : /* Wait for the current process to die, more efficient than SleepEx(). */
172 : : WaitForSingleObjectEx (GetCurrentProcess (), timeout_ms, TRUE);
173 : : ready = WAIT_TIMEOUT;
174 : : }
175 : : }
176 : : else
177 : : {
178 : : /* Wait for just handles
179 : : * -> Use WaitForMultipleObjectsEx
180 : : */
181 : : if (_g_main_poll_debug)
182 : : g_print (" WaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms);
183 : :
184 : : ready = WaitForMultipleObjectsEx (nhandles, handles, FALSE, timeout_ms, TRUE);
185 : : if (ready == WAIT_FAILED)
186 : : {
187 : : gchar *emsg = g_win32_error_message (GetLastError ());
188 : : g_warning ("WaitForMultipleObjectsEx failed: %s", emsg);
189 : : g_free (emsg);
190 : : }
191 : : }
192 : :
193 : : if (_g_main_poll_debug)
194 : : g_print (" wait returns %ld%s\n",
195 : : ready,
196 : : (ready == WAIT_FAILED ? " (WAIT_FAILED)" :
197 : : (ready == WAIT_TIMEOUT ? " (WAIT_TIMEOUT)" :
198 : : (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles ? " (msg)" : ""))));
199 : :
200 : : if (ready == WAIT_FAILED)
201 : : return -1;
202 : : else if (ready == WAIT_TIMEOUT ||
203 : : ready == WAIT_IO_COMPLETION)
204 : : return 0;
205 : : else if (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles)
206 : : {
207 : : msg_fd->revents |= G_IO_IN;
208 : :
209 : : /* If we have a timeout, or no handles to poll, be satisfied
210 : : * with just noticing we have messages waiting.
211 : : */
212 : : if (timeout_ms != 0 || nhandles == 0)
213 : : return 1;
214 : :
215 : : /* If no timeout and handles to poll, recurse to poll them,
216 : : * too.
217 : : */
218 : : recursed_result = poll_rest (NULL, stop_fd, handles, handle_to_fd, nhandles, 0);
219 : : return (recursed_result == -1) ? -1 : 1 + recursed_result;
220 : : }
221 : : else if (ready < WAIT_OBJECT_0 + nhandles)
222 : : {
223 : : int retval;
224 : :
225 : : f = handle_to_fd[ready - WAIT_OBJECT_0];
226 : : f->revents = f->events;
227 : : if (_g_main_poll_debug)
228 : : g_print (" got event %p\n", (HANDLE) f->fd);
229 : :
230 : : /* Do not count the stop_fd */
231 : : retval = (f != stop_fd) ? 1 : 0;
232 : :
233 : : /* If no timeout and polling several handles, recurse to poll
234 : : * the rest of them.
235 : : */
236 : : if (timeout_ms == 0 && nhandles > 1)
237 : : {
238 : : /* Poll the handles with index > ready */
239 : : HANDLE *shorter_handles;
240 : : GPollFD **shorter_handle_to_fd;
241 : : gint shorter_nhandles;
242 : :
243 : : shorter_handles = &handles[ready - WAIT_OBJECT_0 + 1];
244 : : shorter_handle_to_fd = &handle_to_fd[ready - WAIT_OBJECT_0 + 1];
245 : : shorter_nhandles = nhandles - (ready - WAIT_OBJECT_0 + 1);
246 : :
247 : : recursed_result = poll_rest (NULL, stop_fd, shorter_handles, shorter_handle_to_fd, shorter_nhandles, 0);
248 : : return (recursed_result == -1) ? -1 : retval + recursed_result;
249 : : }
250 : : return retval;
251 : : }
252 : :
253 : : return 0;
254 : : }
255 : :
256 : : typedef struct
257 : : {
258 : : HANDLE handles[MAXIMUM_WAIT_OBJECTS];
259 : : GPollFD *handle_to_fd[MAXIMUM_WAIT_OBJECTS];
260 : : GPollFD *msg_fd;
261 : : GPollFD *stop_fd;
262 : : gint nhandles;
263 : : DWORD timeout_ms;
264 : : } GWin32PollThreadData;
265 : :
266 : : static gint
267 : : poll_single_thread (GWin32PollThreadData *data)
268 : : {
269 : : int retval;
270 : :
271 : : /* Polling for several things? */
272 : : if (data->nhandles > 1 || (data->nhandles > 0 && data->msg_fd != NULL))
273 : : {
274 : : /* First check if one or several of them are immediately
275 : : * available
276 : : */
277 : : retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, 0);
278 : :
279 : : /* If not, and we have a significant timeout, poll again with
280 : : * timeout then. Note that this will return indication for only
281 : : * one event, or only for messages.
282 : : */
283 : : if (retval == 0 && (data->timeout_ms == INFINITE || data->timeout_ms > 0))
284 : : retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, data->timeout_ms);
285 : : }
286 : : else
287 : : {
288 : : /* Just polling for one thing, so no need to check first if
289 : : * available immediately
290 : : */
291 : : retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, data->timeout_ms);
292 : : }
293 : :
294 : : return retval;
295 : : }
296 : :
297 : : static void
298 : : fill_poll_thread_data (GPollFD *fds,
299 : : guint nfds,
300 : : DWORD timeout_ms,
301 : : GPollFD *stop_fd,
302 : : GWin32PollThreadData *data)
303 : : {
304 : : GPollFD *f;
305 : :
306 : : data->timeout_ms = timeout_ms;
307 : :
308 : : if (stop_fd != NULL)
309 : : {
310 : : if (_g_main_poll_debug)
311 : : g_print (" Stop FD: %p", (HANDLE) stop_fd->fd);
312 : :
313 : : g_assert (data->nhandles < MAXIMUM_WAIT_OBJECTS);
314 : :
315 : : data->stop_fd = stop_fd;
316 : : data->handle_to_fd[data->nhandles] = stop_fd;
317 : : data->handles[data->nhandles++] = (HANDLE) stop_fd->fd;
318 : : }
319 : :
320 : : for (f = fds; f < &fds[nfds]; ++f)
321 : : {
322 : : if ((data->nhandles == MAXIMUM_WAIT_OBJECTS) ||
323 : : (data->msg_fd != NULL && (data->nhandles == MAXIMUM_WAIT_OBJECTS - 1)))
324 : : {
325 : : g_warning ("Too many handles to wait for!");
326 : : break;
327 : : }
328 : :
329 : : if (f->fd == G_WIN32_MSG_HANDLE && (f->events & G_IO_IN))
330 : : {
331 : : if (_g_main_poll_debug && data->msg_fd == NULL)
332 : : g_print (" MSG");
333 : : data->msg_fd = f;
334 : : }
335 : : else if (f->fd > 0)
336 : : {
337 : : if (_g_main_poll_debug)
338 : : g_print (" %p", (HANDLE) f->fd);
339 : : data->handle_to_fd[data->nhandles] = f;
340 : : data->handles[data->nhandles++] = (HANDLE) f->fd;
341 : : }
342 : :
343 : : f->revents = 0;
344 : : }
345 : : }
346 : :
347 : : static guint __stdcall
348 : : poll_thread_run (gpointer user_data)
349 : : {
350 : : GWin32PollThreadData *data = user_data;
351 : :
352 : : /* Docs say that it is safer to call _endthreadex by our own:
353 : : * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/endthread-endthreadex
354 : : */
355 : : _endthreadex (poll_single_thread (data));
356 : :
357 : : g_assert_not_reached ();
358 : :
359 : : return 0;
360 : : }
361 : :
362 : : /* One slot for a possible msg object or the stop event */
363 : : #define MAXIMUM_WAIT_OBJECTS_PER_THREAD (MAXIMUM_WAIT_OBJECTS - 1)
364 : :
365 : : gint
366 : : g_poll (GPollFD *fds,
367 : : guint nfds,
368 : : gint timeout)
369 : : {
370 : : guint nthreads, threads_remain;
371 : : HANDLE thread_handles[MAXIMUM_WAIT_OBJECTS];
372 : : GWin32PollThreadData *threads_data;
373 : : GPollFD stop_event = { 0, };
374 : : GPollFD *f;
375 : : guint i, fds_idx = 0;
376 : : DWORD ready;
377 : : DWORD thread_retval;
378 : : int retval;
379 : : GPollFD *msg_fd = NULL;
380 : :
381 : : if (timeout == -1)
382 : : timeout = INFINITE;
383 : :
384 : : /* Simple case without extra threads */
385 : : if (nfds <= MAXIMUM_WAIT_OBJECTS)
386 : : {
387 : : GWin32PollThreadData data = { 0, };
388 : :
389 : : if (_g_main_poll_debug)
390 : : g_print ("g_poll: waiting for");
391 : :
392 : : fill_poll_thread_data (fds, nfds, timeout, NULL, &data);
393 : :
394 : : if (_g_main_poll_debug)
395 : : g_print ("\n");
396 : :
397 : : retval = poll_single_thread (&data);
398 : : if (retval == -1)
399 : : for (f = fds; f < &fds[nfds]; ++f)
400 : : f->revents = 0;
401 : :
402 : : return retval;
403 : : }
404 : :
405 : : if (_g_main_poll_debug)
406 : : g_print ("g_poll: polling with threads\n");
407 : :
408 : : nthreads = nfds / MAXIMUM_WAIT_OBJECTS_PER_THREAD;
409 : : threads_remain = nfds % MAXIMUM_WAIT_OBJECTS_PER_THREAD;
410 : : if (threads_remain > 0)
411 : : nthreads++;
412 : :
413 : : if (nthreads > MAXIMUM_WAIT_OBJECTS_PER_THREAD)
414 : : {
415 : : g_warning ("Too many handles to wait for in threads!");
416 : : nthreads = MAXIMUM_WAIT_OBJECTS_PER_THREAD;
417 : : }
418 : :
419 : : #if GLIB_SIZEOF_VOID_P == 8
420 : : stop_event.fd = (gint64)CreateEventW (NULL, TRUE, FALSE, NULL);
421 : : #else
422 : : stop_event.fd = (gint)CreateEventW (NULL, TRUE, FALSE, NULL);
423 : : #endif
424 : : stop_event.events = G_IO_IN;
425 : :
426 : : threads_data = g_new0 (GWin32PollThreadData, nthreads);
427 : : for (i = 0; i < nthreads; i++)
428 : : {
429 : : guint thread_fds;
430 : : guint ignore;
431 : :
432 : : if (i == (nthreads - 1) && threads_remain > 0)
433 : : thread_fds = threads_remain;
434 : : else
435 : : thread_fds = MAXIMUM_WAIT_OBJECTS_PER_THREAD;
436 : :
437 : : fill_poll_thread_data (fds + fds_idx, thread_fds, timeout, &stop_event, &threads_data[i]);
438 : : fds_idx += thread_fds;
439 : :
440 : : /* We must poll for messages from the same thread, so poll it along with the threads */
441 : : if (threads_data[i].msg_fd != NULL)
442 : : {
443 : : msg_fd = threads_data[i].msg_fd;
444 : : threads_data[i].msg_fd = NULL;
445 : : }
446 : :
447 : : thread_handles[i] = (HANDLE) _beginthreadex (NULL, 0, poll_thread_run, &threads_data[i], 0, &ignore);
448 : : }
449 : :
450 : : /* Wait for at least one thread to return */
451 : : if (msg_fd != NULL)
452 : : ready = MsgWaitForMultipleObjectsEx (nthreads, thread_handles, timeout,
453 : : QS_ALLINPUT, MWMO_ALERTABLE);
454 : : else
455 : : ready = WaitForMultipleObjects (nthreads, thread_handles, FALSE, timeout);
456 : :
457 : : /* Signal the stop in case any of the threads did not stop yet */
458 : : if (!SetEvent ((HANDLE)stop_event.fd))
459 : : {
460 : : gchar *emsg = g_win32_error_message (GetLastError ());
461 : : g_warning ("gpoll: failed to signal the stop event: %s", emsg);
462 : : g_free (emsg);
463 : : }
464 : :
465 : : /* Wait for the rest of the threads to finish */
466 : : WaitForMultipleObjects (nthreads, thread_handles, TRUE, INFINITE);
467 : :
468 : : /* The return value of all the threads give us all the fds that changed state */
469 : : retval = 0;
470 : : if (msg_fd != NULL && ready == WAIT_OBJECT_0 + nthreads)
471 : : {
472 : : msg_fd->revents |= G_IO_IN;
473 : : retval = 1;
474 : : }
475 : :
476 : : for (i = 0; i < nthreads; i++)
477 : : {
478 : : if (GetExitCodeThread (thread_handles[i], &thread_retval))
479 : : retval = (retval == -1) ? -1 : ((thread_retval == (DWORD) -1) ? -1 : (int) (retval + thread_retval));
480 : :
481 : : CloseHandle (thread_handles[i]);
482 : : }
483 : :
484 : : if (retval == -1)
485 : : for (f = fds; f < &fds[nfds]; ++f)
486 : : f->revents = 0;
487 : :
488 : : g_free (threads_data);
489 : : CloseHandle ((HANDLE)stop_event.fd);
490 : :
491 : : return retval;
492 : : }
493 : :
494 : : #else /* !G_OS_WIN32 */
495 : :
496 : : /* The following implementation of poll() comes from the GNU C Library.
497 : : * Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
498 : : */
499 : :
500 : : #include <string.h> /* for bzero on BSD systems */
501 : :
502 : : #ifdef HAVE_SYS_SELECT_H
503 : : #include <sys/select.h>
504 : : #endif /* HAVE_SYS_SELECT_H */
505 : :
506 : : gint
507 : : g_poll (GPollFD *fds,
508 : : guint nfds,
509 : : gint timeout)
510 : : {
511 : : struct timeval tv;
512 : : fd_set rset, wset, xset;
513 : : GPollFD *f;
514 : : int ready;
515 : : int maxfd = 0;
516 : :
517 : : FD_ZERO (&rset);
518 : : FD_ZERO (&wset);
519 : : FD_ZERO (&xset);
520 : :
521 : : for (f = fds; f < &fds[nfds]; ++f)
522 : : if (f->fd >= 0)
523 : : {
524 : : if (f->events & G_IO_IN)
525 : : FD_SET (f->fd, &rset);
526 : : if (f->events & G_IO_OUT)
527 : : FD_SET (f->fd, &wset);
528 : : if (f->events & G_IO_PRI)
529 : : FD_SET (f->fd, &xset);
530 : : if (f->fd > maxfd && (f->events & (G_IO_IN|G_IO_OUT|G_IO_PRI)))
531 : : maxfd = f->fd;
532 : : }
533 : :
534 : : tv.tv_sec = timeout / 1000;
535 : : tv.tv_usec = (timeout % 1000) * 1000;
536 : :
537 : : ready = select (maxfd + 1, &rset, &wset, &xset,
538 : : timeout == -1 ? NULL : &tv);
539 : : if (ready > 0)
540 : : for (f = fds; f < &fds[nfds]; ++f)
541 : : {
542 : : f->revents = 0;
543 : : if (f->fd >= 0)
544 : : {
545 : : if (FD_ISSET (f->fd, &rset))
546 : : f->revents |= G_IO_IN;
547 : : if (FD_ISSET (f->fd, &wset))
548 : : f->revents |= G_IO_OUT;
549 : : if (FD_ISSET (f->fd, &xset))
550 : : f->revents |= G_IO_PRI;
551 : : }
552 : : }
553 : :
554 : : return ready;
555 : : }
556 : :
557 : : #endif /* !G_OS_WIN32 */
558 : :
559 : : #endif /* !HAVE_POLL */
|