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 : : #include <windows.h>
76 : : #include <process.h>
77 : : #endif /* G_OS_WIN32 */
78 : :
79 : : #include "gpoll.h"
80 : :
81 : : #ifdef G_OS_WIN32
82 : : #include "gprintf.h"
83 : : #endif
84 : :
85 : : #ifdef G_MAIN_POLL_DEBUG
86 : : extern gboolean _g_main_poll_debug;
87 : : #endif
88 : :
89 : : #ifdef HAVE_POLL
90 : :
91 : : /**
92 : : * g_poll:
93 : : * @fds: file descriptors to poll
94 : : * @nfds: the number of file descriptors in @fds
95 : : * @timeout: amount of time to wait, in milliseconds, or -1 to wait forever
96 : : *
97 : : * Polls @fds, as with the poll() system call, but portably. (On
98 : : * systems that don't have poll(), it is emulated using select().)
99 : : * This is used internally by #GMainContext, but it can be called
100 : : * directly if you need to block until a file descriptor is ready, but
101 : : * don't want to run the full main loop.
102 : : *
103 : : * Each element of @fds is a #GPollFD describing a single file
104 : : * descriptor to poll. The @fd field indicates the file descriptor,
105 : : * and the @events field indicates the events to poll for. On return,
106 : : * the @revents fields will be filled with the events that actually
107 : : * occurred.
108 : : *
109 : : * On POSIX systems, the file descriptors in @fds can be any sort of
110 : : * file descriptor, but the situation is much more complicated on
111 : : * Windows. If you need to use g_poll() in code that has to run on
112 : : * Windows, the easiest solution is to construct all of your
113 : : * #GPollFDs with g_io_channel_win32_make_pollfd().
114 : : *
115 : : * Returns: the number of entries in @fds whose @revents fields
116 : : * were filled in, or 0 if the operation timed out, or -1 on error or
117 : : * if the call was interrupted.
118 : : *
119 : : * Since: 2.20
120 : : **/
121 : : gint
122 : 1066439 : g_poll (GPollFD *fds,
123 : : guint nfds,
124 : : gint timeout)
125 : : {
126 : 1066439 : return poll ((struct pollfd *)fds, nfds, timeout);
127 : : }
128 : :
129 : : #else /* !HAVE_POLL */
130 : :
131 : : #ifdef G_OS_WIN32
132 : :
133 : : static int
134 : : poll_rest (GPollFD *msg_fd,
135 : : GPollFD *stop_fd,
136 : : HANDLE *handles,
137 : : GPollFD *handle_to_fd[],
138 : : gint nhandles,
139 : : DWORD timeout_ms)
140 : : {
141 : : DWORD ready;
142 : : GPollFD *f;
143 : : int recursed_result;
144 : :
145 : : if (msg_fd != NULL)
146 : : {
147 : : /* Wait for either messages or handles
148 : : * -> Use MsgWaitForMultipleObjectsEx
149 : : */
150 : : if (_g_main_poll_debug)
151 : : g_print (" MsgWaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms);
152 : :
153 : : ready = MsgWaitForMultipleObjectsEx (nhandles, handles, timeout_ms,
154 : : QS_ALLINPUT, MWMO_ALERTABLE);
155 : :
156 : : if (ready == WAIT_FAILED)
157 : : {
158 : : gchar *emsg = g_win32_error_message (GetLastError ());
159 : : g_warning ("MsgWaitForMultipleObjectsEx failed: %s", emsg);
160 : : g_free (emsg);
161 : : }
162 : : }
163 : : else if (nhandles == 0)
164 : : {
165 : : /* No handles to wait for, just the timeout */
166 : : if (timeout_ms == INFINITE)
167 : : ready = WAIT_FAILED;
168 : : else
169 : : {
170 : : /* Wait for the current process to die, more efficient than SleepEx(). */
171 : : WaitForSingleObjectEx (GetCurrentProcess (), timeout_ms, TRUE);
172 : : ready = WAIT_TIMEOUT;
173 : : }
174 : : }
175 : : else
176 : : {
177 : : /* Wait for just handles
178 : : * -> Use WaitForMultipleObjectsEx
179 : : */
180 : : if (_g_main_poll_debug)
181 : : g_print (" WaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms);
182 : :
183 : : ready = WaitForMultipleObjectsEx (nhandles, handles, FALSE, timeout_ms, TRUE);
184 : : if (ready == WAIT_FAILED)
185 : : {
186 : : gchar *emsg = g_win32_error_message (GetLastError ());
187 : : g_warning ("WaitForMultipleObjectsEx failed: %s", emsg);
188 : : g_free (emsg);
189 : : }
190 : : }
191 : :
192 : : if (_g_main_poll_debug)
193 : : g_print (" wait returns %ld%s\n",
194 : : ready,
195 : : (ready == WAIT_FAILED ? " (WAIT_FAILED)" :
196 : : (ready == WAIT_TIMEOUT ? " (WAIT_TIMEOUT)" :
197 : : (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles ? " (msg)" : ""))));
198 : :
199 : : if (ready == WAIT_FAILED)
200 : : return -1;
201 : : else if (ready == WAIT_TIMEOUT ||
202 : : ready == WAIT_IO_COMPLETION)
203 : : return 0;
204 : : else if (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles)
205 : : {
206 : : msg_fd->revents |= G_IO_IN;
207 : :
208 : : /* If we have a timeout, or no handles to poll, be satisfied
209 : : * with just noticing we have messages waiting.
210 : : */
211 : : if (timeout_ms != 0 || nhandles == 0)
212 : : return 1;
213 : :
214 : : /* If no timeout and handles to poll, recurse to poll them,
215 : : * too.
216 : : */
217 : : recursed_result = poll_rest (NULL, stop_fd, handles, handle_to_fd, nhandles, 0);
218 : : return (recursed_result == -1) ? -1 : 1 + recursed_result;
219 : : }
220 : : else if (ready < WAIT_OBJECT_0 + nhandles)
221 : : {
222 : : int retval;
223 : :
224 : : f = handle_to_fd[ready - WAIT_OBJECT_0];
225 : : f->revents = f->events;
226 : : if (_g_main_poll_debug)
227 : : g_print (" got event %p\n", (HANDLE) f->fd);
228 : :
229 : : /* Do not count the stop_fd */
230 : : retval = (f != stop_fd) ? 1 : 0;
231 : :
232 : : /* If no timeout and polling several handles, recurse to poll
233 : : * the rest of them.
234 : : */
235 : : if (timeout_ms == 0 && nhandles > 1)
236 : : {
237 : : /* Poll the handles with index > ready */
238 : : HANDLE *shorter_handles;
239 : : GPollFD **shorter_handle_to_fd;
240 : : gint shorter_nhandles;
241 : :
242 : : shorter_handles = &handles[ready - WAIT_OBJECT_0 + 1];
243 : : shorter_handle_to_fd = &handle_to_fd[ready - WAIT_OBJECT_0 + 1];
244 : : shorter_nhandles = nhandles - (ready - WAIT_OBJECT_0 + 1);
245 : :
246 : : recursed_result = poll_rest (NULL, stop_fd, shorter_handles, shorter_handle_to_fd, shorter_nhandles, 0);
247 : : return (recursed_result == -1) ? -1 : retval + recursed_result;
248 : : }
249 : : return retval;
250 : : }
251 : :
252 : : return 0;
253 : : }
254 : :
255 : : typedef struct
256 : : {
257 : : HANDLE handles[MAXIMUM_WAIT_OBJECTS];
258 : : GPollFD *handle_to_fd[MAXIMUM_WAIT_OBJECTS];
259 : : GPollFD *msg_fd;
260 : : GPollFD *stop_fd;
261 : : gint retval;
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 : : data->retval = retval;
294 : :
295 : : return data->retval;
296 : : }
297 : :
298 : : static VOID CALLBACK
299 : : poll_single_worker_wrapper (PTP_CALLBACK_INSTANCE instance,
300 : : PVOID context,
301 : : PTP_WORK work)
302 : : {
303 : : UNREFERENCED_PARAMETER (instance);
304 : : UNREFERENCED_PARAMETER (work);
305 : :
306 : : GWin32PollThreadData *data = context;
307 : :
308 : : poll_single_thread (data);
309 : :
310 : : /* Signal the stop in case any of the workers did not stop yet */
311 : : if (!SetEvent ((HANDLE) data->stop_fd->fd))
312 : : {
313 : : gchar *emsg = g_win32_error_message (GetLastError ());
314 : : g_error ("gpoll: failed to signal the stop event: %s", emsg);
315 : : g_free (emsg);
316 : : }
317 : : }
318 : :
319 : : static void
320 : : fill_poll_thread_data (GPollFD *fds,
321 : : guint nfds,
322 : : DWORD timeout_ms,
323 : : GPollFD *stop_fd,
324 : : GWin32PollThreadData *data)
325 : : {
326 : : GPollFD *f;
327 : :
328 : : data->timeout_ms = timeout_ms;
329 : :
330 : : if (stop_fd != NULL)
331 : : {
332 : : if (_g_main_poll_debug)
333 : : g_print (" Stop FD: %p", (HANDLE) stop_fd->fd);
334 : :
335 : : g_assert (data->nhandles < MAXIMUM_WAIT_OBJECTS);
336 : :
337 : : data->stop_fd = stop_fd;
338 : : data->handle_to_fd[data->nhandles] = stop_fd;
339 : : data->handles[data->nhandles++] = (HANDLE) stop_fd->fd;
340 : : }
341 : :
342 : : for (f = fds; f < &fds[nfds]; ++f)
343 : : {
344 : : if ((data->nhandles == MAXIMUM_WAIT_OBJECTS) ||
345 : : (data->msg_fd != NULL && (data->nhandles == MAXIMUM_WAIT_OBJECTS - 1)))
346 : : {
347 : : g_warning ("Too many handles to wait for!");
348 : : break;
349 : : }
350 : :
351 : : if (f->fd == G_WIN32_MSG_HANDLE && (f->events & G_IO_IN))
352 : : {
353 : : if (_g_main_poll_debug && data->msg_fd == NULL)
354 : : g_print (" MSG");
355 : : data->msg_fd = f;
356 : : }
357 : : else if (f->fd > 0)
358 : : {
359 : : if (_g_main_poll_debug)
360 : : g_print (" %p", (HANDLE) f->fd);
361 : : data->handle_to_fd[data->nhandles] = f;
362 : : data->handles[data->nhandles++] = (HANDLE) f->fd;
363 : : }
364 : :
365 : : f->revents = 0;
366 : : }
367 : : }
368 : :
369 : : static void
370 : : cleanup_workers (guint nworkers,
371 : : PTP_WORK *work_handles)
372 : : {
373 : : for (guint i = 0; i < nworkers; i++)
374 : : {
375 : : if (work_handles[i] != NULL)
376 : : CloseThreadpoolWork (work_handles[i]);
377 : : }
378 : : }
379 : :
380 : : /* One slot for a possible msg object or the stop event */
381 : : #define MAXIMUM_WAIT_OBJECTS_PER_THREAD (MAXIMUM_WAIT_OBJECTS - 1)
382 : :
383 : : gint
384 : : g_poll (GPollFD *fds,
385 : : guint nfds,
386 : : gint timeout)
387 : : {
388 : : guint nthreads, threads_remain;
389 : : HANDLE worker_completed_handles[1] = { NULL, };
390 : : GWin32PollThreadData *threads_data;
391 : : GPollFD stop_event = { 0, };
392 : : GPollFD *f;
393 : : guint i, fds_idx = 0;
394 : : DWORD ready;
395 : : DWORD thread_retval;
396 : : int retval;
397 : : GPollFD *msg_fd = NULL;
398 : : PTP_WORK work_handles[MAXIMUM_WAIT_OBJECTS] = { NULL, };
399 : :
400 : : if (timeout == -1)
401 : : timeout = INFINITE;
402 : :
403 : : /* Simple case without extra threads */
404 : : if (nfds <= MAXIMUM_WAIT_OBJECTS)
405 : : {
406 : : GWin32PollThreadData data = { 0, };
407 : :
408 : : if (_g_main_poll_debug)
409 : : g_print ("g_poll: waiting for");
410 : :
411 : : fill_poll_thread_data (fds, nfds, timeout, NULL, &data);
412 : :
413 : : if (_g_main_poll_debug)
414 : : g_print ("\n");
415 : :
416 : : retval = poll_single_thread (&data);
417 : : if (retval == -1)
418 : : for (f = fds; f < &fds[nfds]; ++f)
419 : : f->revents = 0;
420 : :
421 : : return retval;
422 : : }
423 : :
424 : : if (_g_main_poll_debug)
425 : : g_print ("g_poll: polling with threads\n");
426 : :
427 : : nthreads = nfds / MAXIMUM_WAIT_OBJECTS_PER_THREAD;
428 : : threads_remain = nfds % MAXIMUM_WAIT_OBJECTS_PER_THREAD;
429 : : if (threads_remain > 0)
430 : : nthreads++;
431 : :
432 : : if (nthreads > MAXIMUM_WAIT_OBJECTS_PER_THREAD)
433 : : {
434 : : g_warning ("Too many handles to wait for in threads!");
435 : : nthreads = MAXIMUM_WAIT_OBJECTS_PER_THREAD;
436 : : }
437 : :
438 : : #if GLIB_SIZEOF_VOID_P == 8
439 : : stop_event.fd = (gint64)CreateEventW (NULL, TRUE, FALSE, NULL);
440 : : #else
441 : : stop_event.fd = (gint)CreateEventW (NULL, TRUE, FALSE, NULL);
442 : : #endif
443 : : stop_event.events = G_IO_IN;
444 : : worker_completed_handles[0] = (HANDLE) stop_event.fd;
445 : :
446 : : threads_data = g_new0 (GWin32PollThreadData, nthreads);
447 : :
448 : : for (i = 0; i < nthreads; i++)
449 : : {
450 : : guint thread_fds;
451 : :
452 : : if (i == (nthreads - 1) && threads_remain > 0)
453 : : thread_fds = threads_remain;
454 : : else
455 : : thread_fds = MAXIMUM_WAIT_OBJECTS_PER_THREAD;
456 : :
457 : : fill_poll_thread_data (fds + fds_idx, thread_fds, timeout, &stop_event, &threads_data[i]);
458 : : fds_idx += thread_fds;
459 : :
460 : : /* We must poll for messages from the same thread, so poll it along with the threads */
461 : : if (threads_data[i].msg_fd != NULL)
462 : : {
463 : : msg_fd = threads_data[i].msg_fd;
464 : : threads_data[i].msg_fd = NULL;
465 : : }
466 : :
467 : : work_handles[i] = CreateThreadpoolWork (poll_single_worker_wrapper, &threads_data[i],
468 : : NULL);
469 : : if (work_handles[i] == NULL)
470 : : {
471 : : gchar *emsg = g_win32_error_message (GetLastError ());
472 : : g_error ("CreateThreadpoolWork failed: %s", emsg);
473 : : g_free (emsg);
474 : : retval = -1;
475 : : goto cleanup;
476 : : }
477 : :
478 : : SubmitThreadpoolWork (work_handles[i]);
479 : : }
480 : :
481 : : /* Wait for at least one worker to return */
482 : : if (msg_fd != NULL)
483 : : ready = MsgWaitForMultipleObjectsEx (1, worker_completed_handles, timeout,
484 : : QS_ALLINPUT, MWMO_ALERTABLE);
485 : : else
486 : : ready = WaitForMultipleObjects (1, worker_completed_handles, FALSE, timeout);
487 : :
488 : : /* Signal the stop in case any of the workers did not stop yet */
489 : : if (!SetEvent ((HANDLE) stop_event.fd))
490 : : {
491 : : gchar *emsg = g_win32_error_message (GetLastError ());
492 : : g_error ("gpoll: failed to signal the stop event: %s", emsg);
493 : : g_free (emsg);
494 : : retval = -1;
495 : : goto cleanup;
496 : : }
497 : :
498 : : /* Wait for the all workers to finish individually, since we're not using a cleanup group.
499 : : We disable fCancelPendingCallbacks since we share the default process threadpool.
500 : : */
501 : : for (i = 0; i < nthreads; i++)
502 : : WaitForThreadpoolWorkCallbacks (work_handles[i], FALSE);
503 : :
504 : : /* The return value of all the threads give us all the fds that changed state */
505 : : retval = 0;
506 : : if (msg_fd != NULL && ready == WAIT_OBJECT_0 + 1)
507 : : {
508 : : msg_fd->revents |= G_IO_IN;
509 : : retval = 1;
510 : : }
511 : :
512 : : for (i = 0; i < nthreads; i++)
513 : : {
514 : : thread_retval = threads_data[i].retval;
515 : : retval = (retval == -1) ? -1 : ((thread_retval == (DWORD) -1) ? -1 : (int) (retval + thread_retval));
516 : : }
517 : :
518 : : cleanup:
519 : : if (retval == -1)
520 : : {
521 : : for (f = fds; f < &fds[nfds]; ++f)
522 : : f->revents = 0;
523 : : }
524 : : cleanup_workers (nthreads, work_handles);
525 : : g_free (threads_data);
526 : : CloseHandle ((HANDLE) stop_event.fd);
527 : :
528 : : return retval;
529 : : }
530 : :
531 : : #else /* !G_OS_WIN32 */
532 : :
533 : : /* The following implementation of poll() comes from the GNU C Library.
534 : : * Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
535 : : */
536 : :
537 : : #include <string.h> /* for bzero on BSD systems */
538 : :
539 : : #ifdef HAVE_SYS_SELECT_H
540 : : #include <sys/select.h>
541 : : #endif /* HAVE_SYS_SELECT_H */
542 : :
543 : : gint
544 : : g_poll (GPollFD *fds,
545 : : guint nfds,
546 : : gint timeout)
547 : : {
548 : : struct timeval tv;
549 : : fd_set rset, wset, xset;
550 : : GPollFD *f;
551 : : int ready;
552 : : int maxfd = 0;
553 : :
554 : : FD_ZERO (&rset);
555 : : FD_ZERO (&wset);
556 : : FD_ZERO (&xset);
557 : :
558 : : for (f = fds; f < &fds[nfds]; ++f)
559 : : if (f->fd >= 0)
560 : : {
561 : : if (f->events & G_IO_IN)
562 : : FD_SET (f->fd, &rset);
563 : : if (f->events & G_IO_OUT)
564 : : FD_SET (f->fd, &wset);
565 : : if (f->events & G_IO_PRI)
566 : : FD_SET (f->fd, &xset);
567 : : if (f->fd > maxfd && (f->events & (G_IO_IN|G_IO_OUT|G_IO_PRI)))
568 : : maxfd = f->fd;
569 : : }
570 : :
571 : : tv.tv_sec = timeout / 1000;
572 : : tv.tv_usec = (timeout % 1000) * 1000;
573 : :
574 : : ready = select (maxfd + 1, &rset, &wset, &xset,
575 : : timeout == -1 ? NULL : &tv);
576 : : if (ready > 0)
577 : : for (f = fds; f < &fds[nfds]; ++f)
578 : : {
579 : : f->revents = 0;
580 : : if (f->fd >= 0)
581 : : {
582 : : if (FD_ISSET (f->fd, &rset))
583 : : f->revents |= G_IO_IN;
584 : : if (FD_ISSET (f->fd, &wset))
585 : : f->revents |= G_IO_OUT;
586 : : if (FD_ISSET (f->fd, &xset))
587 : : f->revents |= G_IO_PRI;
588 : : }
589 : : }
590 : :
591 : : return ready;
592 : : }
593 : :
594 : : #endif /* !G_OS_WIN32 */
595 : :
596 : : #endif /* !HAVE_POLL */
|