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 : : #if defined(__APPLE__) && !defined(_DARWIN_UNLIMITED_SELECT)
36 : : #define _DARWIN_UNLIMITED_SELECT 1
37 : : #endif
38 : :
39 : : #include "config.h"
40 : : #include "glibconfig.h"
41 : : #include "giochannel.h"
42 : :
43 : : /* Uncomment the next line (and the corresponding line in gmain.c) to
44 : : * enable debugging printouts if the environment variable
45 : : * G_MAIN_POLL_DEBUG is set to some value.
46 : : */
47 : : /* #define G_MAIN_POLL_DEBUG */
48 : :
49 : : #ifdef _WIN32
50 : : /* Always enable debugging printout on Windows, as it is more often
51 : : * needed there...
52 : : */
53 : : #define G_MAIN_POLL_DEBUG
54 : : #endif
55 : :
56 : : #include <limits.h>
57 : : #include <stdlib.h>
58 : : #include <sys/types.h>
59 : : #include <time.h>
60 : : #ifdef HAVE_SYS_TIME_H
61 : : #include <sys/time.h>
62 : : #endif /* HAVE_SYS_TIME_H */
63 : : #ifdef HAVE_POLL
64 : : # include <poll.h>
65 : :
66 : : /* The poll() emulation on OS/X doesn't handle fds=NULL, nfds=0,
67 : : * so we prefer our own poll emulation.
68 : : */
69 : : #if defined(_POLL_EMUL_H_) || defined(BROKEN_POLL)
70 : : #undef HAVE_POLL
71 : : #endif
72 : :
73 : : #endif /* GLIB_HAVE_SYS_POLL_H */
74 : : #ifdef G_OS_UNIX
75 : : #include <unistd.h>
76 : : #endif /* G_OS_UNIX */
77 : : #include <errno.h>
78 : :
79 : : #ifdef G_OS_WIN32
80 : : #include <windows.h>
81 : : #include <process.h>
82 : : #endif /* G_OS_WIN32 */
83 : :
84 : : #include "gpoll.h"
85 : :
86 : : #ifdef G_OS_WIN32
87 : : #include "gprintf.h"
88 : : #endif
89 : :
90 : : #ifdef G_MAIN_POLL_DEBUG
91 : : extern gboolean _g_main_poll_debug;
92 : : #endif
93 : :
94 : : #ifdef HAVE_POLL
95 : :
96 : : /**
97 : : * g_poll:
98 : : * @fds: file descriptors to poll
99 : : * @nfds: the number of file descriptors in @fds
100 : : * @timeout: amount of time to wait, in milliseconds, or -1 to wait forever
101 : : *
102 : : * Polls @fds, as with the poll() system call, but portably. (On
103 : : * systems that don't have poll(), it is emulated using select().)
104 : : * This is used internally by #GMainContext, but it can be called
105 : : * directly if you need to block until a file descriptor is ready, but
106 : : * don't want to run the full main loop.
107 : : *
108 : : * Each element of @fds is a #GPollFD describing a single file
109 : : * descriptor to poll. The @fd field indicates the file descriptor,
110 : : * and the @events field indicates the events to poll for. On return,
111 : : * the @revents fields will be filled with the events that actually
112 : : * occurred.
113 : : *
114 : : * On POSIX systems, the file descriptors in @fds can be any sort of
115 : : * file descriptor, but the situation is much more complicated on
116 : : * Windows. If you need to use g_poll() in code that has to run on
117 : : * Windows, the easiest solution is to construct all of your
118 : : * #GPollFDs with g_io_channel_win32_make_pollfd().
119 : : *
120 : : * Returns: the number of entries in @fds whose @revents fields
121 : : * were filled in, or 0 if the operation timed out, or -1 on error or
122 : : * if the call was interrupted.
123 : : *
124 : : * Since: 2.20
125 : : **/
126 : : gint
127 : 1077296 : g_poll (GPollFD *fds,
128 : : guint nfds,
129 : : gint timeout)
130 : : {
131 : 1077296 : return poll ((struct pollfd *)fds, nfds, timeout);
132 : : }
133 : :
134 : : #else /* !HAVE_POLL */
135 : :
136 : : #ifdef G_OS_WIN32
137 : :
138 : : static int
139 : 718148 : poll_rest (GPollFD *msg_fd,
140 : : GPollFD *stop_fd,
141 : : HANDLE *handles,
142 : : GPollFD *handle_to_fd[],
143 : : gint nhandles,
144 : : DWORD timeout_ms)
145 : : {
146 : : DWORD ready;
147 : : GPollFD *f;
148 : : int recursed_result;
149 : :
150 : 718148 : if (msg_fd != NULL)
151 : : {
152 : : /* Wait for either messages or handles
153 : : * -> Use MsgWaitForMultipleObjectsEx
154 : : */
155 : 81 : if (_g_main_poll_debug)
156 : 0 : g_print (" MsgWaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms);
157 : :
158 : 81 : ready = MsgWaitForMultipleObjectsEx (nhandles, handles, timeout_ms,
159 : : QS_ALLINPUT, MWMO_ALERTABLE);
160 : :
161 : 81 : if (ready == WAIT_FAILED)
162 : : {
163 : 0 : gchar *emsg = g_win32_error_message (GetLastError ());
164 : 0 : g_warning ("MsgWaitForMultipleObjectsEx failed: %s", emsg);
165 : 0 : g_free (emsg);
166 : 0 : }
167 : 81 : }
168 : 718067 : else if (nhandles == 0)
169 : : {
170 : : /* No handles to wait for, just the timeout */
171 : 230 : if (timeout_ms == INFINITE)
172 : 0 : ready = WAIT_FAILED;
173 : : else
174 : : {
175 : : /* Wait for the current process to die, more efficient than SleepEx(). */
176 : 230 : WaitForSingleObjectEx (GetCurrentProcess (), timeout_ms, TRUE);
177 : 230 : ready = WAIT_TIMEOUT;
178 : : }
179 : 230 : }
180 : : else
181 : : {
182 : : /* Wait for just handles
183 : : * -> Use WaitForMultipleObjectsEx
184 : : */
185 : 717837 : if (_g_main_poll_debug)
186 : 0 : g_print (" WaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms);
187 : :
188 : 717837 : ready = WaitForMultipleObjectsEx (nhandles, handles, FALSE, timeout_ms, TRUE);
189 : 717837 : if (ready == WAIT_FAILED)
190 : : {
191 : 0 : gchar *emsg = g_win32_error_message (GetLastError ());
192 : 0 : g_warning ("WaitForMultipleObjectsEx failed: %s", emsg);
193 : 0 : g_free (emsg);
194 : 0 : }
195 : : }
196 : :
197 : 718148 : if (_g_main_poll_debug)
198 : 0 : g_print (" wait returns %ld%s\n",
199 : 0 : ready,
200 : 0 : (ready == WAIT_FAILED ? " (WAIT_FAILED)" :
201 : 0 : (ready == WAIT_TIMEOUT ? " (WAIT_TIMEOUT)" :
202 : 0 : (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles ? " (msg)" : ""))));
203 : :
204 : 718148 : if (ready == WAIT_FAILED)
205 : 0 : return -1;
206 : 718148 : else if (ready == WAIT_TIMEOUT ||
207 : 492266 : ready == WAIT_IO_COMPLETION)
208 : 226124 : return 0;
209 : 492024 : else if (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles)
210 : : {
211 : 4 : msg_fd->revents |= G_IO_IN;
212 : :
213 : : /* If we have a timeout, or no handles to poll, be satisfied
214 : : * with just noticing we have messages waiting.
215 : : */
216 : 4 : if (timeout_ms != 0 || nhandles == 0)
217 : 1 : return 1;
218 : :
219 : : /* If no timeout and handles to poll, recurse to poll them,
220 : : * too.
221 : : */
222 : 3 : recursed_result = poll_rest (NULL, stop_fd, handles, handle_to_fd, nhandles, 0);
223 : 3 : return (recursed_result == -1) ? -1 : 1 + recursed_result;
224 : : }
225 : 492020 : else if (ready < WAIT_OBJECT_0 + nhandles)
226 : : {
227 : : int retval;
228 : :
229 : 492020 : f = handle_to_fd[ready - WAIT_OBJECT_0];
230 : 492020 : f->revents = f->events;
231 : 492020 : if (_g_main_poll_debug)
232 : 0 : g_print (" got event %p\n", (HANDLE) f->fd);
233 : :
234 : : /* Do not count the stop_fd */
235 : 492020 : retval = (f != stop_fd) ? 1 : 0;
236 : :
237 : : /* If no timeout and polling several handles, recurse to poll
238 : : * the rest of them.
239 : : */
240 : 492020 : if (timeout_ms == 0 && nhandles > 1)
241 : : {
242 : : /* Poll the handles with index > ready */
243 : : HANDLE *shorter_handles;
244 : : GPollFD **shorter_handle_to_fd;
245 : : gint shorter_nhandles;
246 : :
247 : 3551 : shorter_handles = &handles[ready - WAIT_OBJECT_0 + 1];
248 : 3551 : shorter_handle_to_fd = &handle_to_fd[ready - WAIT_OBJECT_0 + 1];
249 : 3551 : shorter_nhandles = nhandles - (ready - WAIT_OBJECT_0 + 1);
250 : :
251 : 3551 : recursed_result = poll_rest (NULL, stop_fd, shorter_handles, shorter_handle_to_fd, shorter_nhandles, 0);
252 : 3551 : return (recursed_result == -1) ? -1 : retval + recursed_result;
253 : : }
254 : 488469 : return retval;
255 : : }
256 : :
257 : 0 : return 0;
258 : 718148 : }
259 : :
260 : : typedef struct
261 : : {
262 : : HANDLE handles[MAXIMUM_WAIT_OBJECTS];
263 : : GPollFD *handle_to_fd[MAXIMUM_WAIT_OBJECTS];
264 : : GPollFD *msg_fd;
265 : : GPollFD *stop_fd;
266 : : gint retval;
267 : : gint nhandles;
268 : : DWORD timeout_ms;
269 : : } GWin32PollThreadData;
270 : :
271 : : static gint
272 : 720210 : poll_single_thread (GWin32PollThreadData *data)
273 : : {
274 : : int retval;
275 : :
276 : : /* Polling for several things? */
277 : 720210 : if (data->nhandles > 1 || (data->nhandles > 0 && data->msg_fd != NULL))
278 : : {
279 : : /* First check if one or several of them are immediately
280 : : * available
281 : : */
282 : 2690 : retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, 0);
283 : :
284 : : /* If not, and we have a significant timeout, poll again with
285 : : * timeout then. Note that this will return indication for only
286 : : * one event, or only for messages.
287 : : */
288 : 2690 : if (retval == 0 && (data->timeout_ms == INFINITE || data->timeout_ms > 0))
289 : 1188 : retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, data->timeout_ms);
290 : 2690 : }
291 : : else
292 : : {
293 : : /* Just polling for one thing, so no need to check first if
294 : : * available immediately
295 : : */
296 : 717556 : retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, data->timeout_ms);
297 : : }
298 : 720246 : data->retval = retval;
299 : :
300 : 720246 : return data->retval;
301 : : }
302 : :
303 : : static VOID CALLBACK
304 : 208 : poll_single_worker_wrapper (PTP_CALLBACK_INSTANCE instance,
305 : : PVOID context,
306 : : PTP_WORK work)
307 : : {
308 : 208 : UNREFERENCED_PARAMETER (instance);
309 : 208 : UNREFERENCED_PARAMETER (work);
310 : :
311 : 208 : GWin32PollThreadData *data = context;
312 : :
313 : 208 : poll_single_thread (data);
314 : :
315 : : /* Signal the stop in case any of the workers did not stop yet */
316 : 208 : if (!SetEvent ((HANDLE) data->stop_fd->fd))
317 : : {
318 : 0 : gchar *emsg = g_win32_error_message (GetLastError ());
319 : 0 : g_error ("gpoll: failed to signal the stop event: %s", emsg);
320 : 0 : g_free (emsg);
321 : 0 : }
322 : 208 : }
323 : :
324 : : static void
325 : 723119 : fill_poll_thread_data (GPollFD *fds,
326 : : guint nfds,
327 : : DWORD timeout_ms,
328 : : GPollFD *stop_fd,
329 : : GWin32PollThreadData *data)
330 : : {
331 : : GPollFD *f;
332 : :
333 : 723119 : data->timeout_ms = timeout_ms;
334 : :
335 : 723119 : if (stop_fd != NULL)
336 : : {
337 : 208 : if (_g_main_poll_debug)
338 : 0 : g_print (" Stop FD: %p", (HANDLE) stop_fd->fd);
339 : :
340 : 208 : g_assert (data->nhandles < MAXIMUM_WAIT_OBJECTS);
341 : :
342 : 208 : data->stop_fd = stop_fd;
343 : 208 : data->handle_to_fd[data->nhandles] = stop_fd;
344 : 208 : data->handles[data->nhandles++] = (HANDLE) stop_fd->fd;
345 : 208 : }
346 : :
347 : 1461930 : for (f = fds; f < &fds[nfds]; ++f)
348 : : {
349 : 742727 : if ((data->nhandles == MAXIMUM_WAIT_OBJECTS) ||
350 : 740812 : (data->msg_fd != NULL && (data->nhandles == MAXIMUM_WAIT_OBJECTS - 1)))
351 : : {
352 : 3830 : g_warning ("Too many handles to wait for!");
353 : 3830 : break;
354 : : }
355 : :
356 : 742727 : if (f->fd == G_WIN32_MSG_HANDLE && (f->events & G_IO_IN))
357 : : {
358 : 68 : if (_g_main_poll_debug && data->msg_fd == NULL)
359 : 0 : g_print (" MSG");
360 : 68 : data->msg_fd = f;
361 : 68 : }
362 : 742659 : else if (f->fd > 0)
363 : : {
364 : 740701 : if (_g_main_poll_debug)
365 : 0 : g_print (" %p", (HANDLE) f->fd);
366 : 740701 : data->handle_to_fd[data->nhandles] = f;
367 : 740701 : data->handles[data->nhandles++] = (HANDLE) f->fd;
368 : 740701 : }
369 : :
370 : 738811 : f->revents = 0;
371 : 738811 : }
372 : 719203 : }
373 : :
374 : : static void
375 : 13 : cleanup_workers (guint nworkers,
376 : : PTP_WORK *work_handles)
377 : : {
378 : 221 : for (guint i = 0; i < nworkers; i++)
379 : : {
380 : 208 : if (work_handles[i] != NULL)
381 : 208 : CloseThreadpoolWork (work_handles[i]);
382 : 208 : }
383 : 13 : }
384 : :
385 : : /* One slot for a possible msg object or the stop event */
386 : : #define MAXIMUM_WAIT_OBJECTS_PER_THREAD (MAXIMUM_WAIT_OBJECTS - 1)
387 : :
388 : : gint
389 : 713121 : g_poll (GPollFD *fds,
390 : : guint nfds,
391 : : gint timeout)
392 : : {
393 : : guint nthreads, threads_remain;
394 : 713121 : HANDLE worker_completed_handles[1] = { NULL, };
395 : : GWin32PollThreadData *threads_data;
396 : 713121 : GPollFD stop_event = { 0, };
397 : : GPollFD *f;
398 : 713121 : guint i, fds_idx = 0;
399 : : DWORD ready;
400 : : DWORD thread_retval;
401 : : int retval;
402 : 713121 : GPollFD *msg_fd = NULL;
403 : 713121 : PTP_WORK work_handles[MAXIMUM_WAIT_OBJECTS] = { NULL, };
404 : :
405 : 713121 : if (timeout == -1)
406 : 490782 : timeout = INFINITE;
407 : :
408 : : /* Simple case without extra threads */
409 : 713121 : if (nfds <= MAXIMUM_WAIT_OBJECTS)
410 : : {
411 : 713108 : GWin32PollThreadData data = { 0, };
412 : :
413 : 713108 : if (_g_main_poll_debug)
414 : 0 : g_print ("g_poll: waiting for");
415 : :
416 : 713108 : fill_poll_thread_data (fds, nfds, timeout, NULL, &data);
417 : :
418 : 713108 : if (_g_main_poll_debug)
419 : 0 : g_print ("\n");
420 : :
421 : 713108 : retval = poll_single_thread (&data);
422 : 713108 : if (retval == -1)
423 : 0 : for (f = fds; f < &fds[nfds]; ++f)
424 : 0 : f->revents = 0;
425 : :
426 : 713108 : return retval;
427 : : }
428 : :
429 : 13 : if (_g_main_poll_debug)
430 : 0 : g_print ("g_poll: polling with threads\n");
431 : :
432 : 13 : nthreads = nfds / MAXIMUM_WAIT_OBJECTS_PER_THREAD;
433 : 13 : threads_remain = nfds % MAXIMUM_WAIT_OBJECTS_PER_THREAD;
434 : 13 : if (threads_remain > 0)
435 : 13 : nthreads++;
436 : :
437 : 13 : if (nthreads > MAXIMUM_WAIT_OBJECTS_PER_THREAD)
438 : : {
439 : 0 : g_warning ("Too many handles to wait for in threads!");
440 : 0 : nthreads = MAXIMUM_WAIT_OBJECTS_PER_THREAD;
441 : 0 : }
442 : :
443 : : #if GLIB_SIZEOF_VOID_P == 8
444 : 13 : stop_event.fd = (gint64)CreateEventW (NULL, TRUE, FALSE, NULL);
445 : : #else
446 : : stop_event.fd = (gint)CreateEventW (NULL, TRUE, FALSE, NULL);
447 : : #endif
448 : 13 : stop_event.events = G_IO_IN;
449 : 13 : worker_completed_handles[0] = (HANDLE) stop_event.fd;
450 : :
451 : 13 : threads_data = g_new0 (GWin32PollThreadData, nthreads);
452 : :
453 : 221 : for (i = 0; i < nthreads; i++)
454 : : {
455 : : guint thread_fds;
456 : :
457 : 208 : if (i == (nthreads - 1) && threads_remain > 0)
458 : 13 : thread_fds = threads_remain;
459 : : else
460 : 195 : thread_fds = MAXIMUM_WAIT_OBJECTS_PER_THREAD;
461 : :
462 : 208 : fill_poll_thread_data (fds + fds_idx, thread_fds, timeout, &stop_event, &threads_data[i]);
463 : 208 : fds_idx += thread_fds;
464 : :
465 : : /* We must poll for messages from the same thread, so poll it along with the threads */
466 : 208 : if (threads_data[i].msg_fd != NULL)
467 : : {
468 : 13 : msg_fd = threads_data[i].msg_fd;
469 : 13 : threads_data[i].msg_fd = NULL;
470 : 13 : }
471 : :
472 : 208 : work_handles[i] = CreateThreadpoolWork (poll_single_worker_wrapper, &threads_data[i],
473 : : NULL);
474 : 208 : if (work_handles[i] == NULL)
475 : : {
476 : 0 : gchar *emsg = g_win32_error_message (GetLastError ());
477 : 0 : g_error ("CreateThreadpoolWork failed: %s", emsg);
478 : 0 : g_free (emsg);
479 : 0 : retval = -1;
480 : 0 : goto cleanup;
481 : : }
482 : :
483 : 208 : SubmitThreadpoolWork (work_handles[i]);
484 : 208 : }
485 : :
486 : : /* Wait for at least one worker to return */
487 : 13 : if (msg_fd != NULL)
488 : 13 : ready = MsgWaitForMultipleObjectsEx (1, worker_completed_handles, timeout,
489 : : QS_ALLINPUT, MWMO_ALERTABLE);
490 : : else
491 : 0 : ready = WaitForMultipleObjects (1, worker_completed_handles, FALSE, timeout);
492 : :
493 : : /* Signal the stop in case any of the workers did not stop yet */
494 : 13 : if (!SetEvent ((HANDLE) stop_event.fd))
495 : : {
496 : 0 : gchar *emsg = g_win32_error_message (GetLastError ());
497 : 0 : g_error ("gpoll: failed to signal the stop event: %s", emsg);
498 : 0 : g_free (emsg);
499 : 0 : retval = -1;
500 : 0 : goto cleanup;
501 : : }
502 : :
503 : : /* Wait for the all workers to finish individually, since we're not using a cleanup group.
504 : : We disable fCancelPendingCallbacks since we share the default process threadpool.
505 : : */
506 : 221 : for (i = 0; i < nthreads; i++)
507 : 208 : WaitForThreadpoolWorkCallbacks (work_handles[i], FALSE);
508 : :
509 : : /* The return value of all the threads give us all the fds that changed state */
510 : 13 : retval = 0;
511 : 13 : if (msg_fd != NULL && ready == WAIT_OBJECT_0 + 1)
512 : : {
513 : 3 : msg_fd->revents |= G_IO_IN;
514 : 3 : retval = 1;
515 : 3 : }
516 : :
517 : 221 : for (i = 0; i < nthreads; i++)
518 : : {
519 : 208 : thread_retval = threads_data[i].retval;
520 : 208 : retval = (retval == -1) ? -1 : ((thread_retval == (DWORD) -1) ? -1 : (int) (retval + thread_retval));
521 : 221 : }
522 : :
523 : : cleanup:
524 : 13 : if (retval == -1)
525 : : {
526 : 0 : for (f = fds; f < &fds[nfds]; ++f)
527 : 0 : f->revents = 0;
528 : 0 : }
529 : 13 : cleanup_workers (nthreads, work_handles);
530 : 13 : g_free (threads_data);
531 : 13 : CloseHandle ((HANDLE) stop_event.fd);
532 : :
533 : 13 : return retval;
534 : 713121 : }
535 : :
536 : : #else /* !G_OS_WIN32 */
537 : :
538 : : /* The following implementation of poll() comes from the GNU C Library.
539 : : * Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
540 : : */
541 : :
542 : : #include <string.h> /* for bzero on BSD systems */
543 : :
544 : : #ifdef HAVE_SYS_SELECT_H
545 : : #include <sys/select.h>
546 : : #endif /* HAVE_SYS_SELECT_H */
547 : :
548 : : static gsize
549 : : g_poll_fd_set_alloc_size (int maxfd)
550 : : {
551 : : gsize nfds = (gsize) maxfd + 1;
552 : : #if defined(__APPLE__)
553 : : gsize nelems = __DARWIN_howmany (nfds, __DARWIN_NFDBITS);
554 : : #else
555 : : const gsize bits_per_element = CHAR_BIT * sizeof (((fd_set *) 0)->fds_bits[0]);
556 : : gsize nelems = (nfds + bits_per_element - 1) / bits_per_element;
557 : : #endif
558 : :
559 : : return nelems * sizeof (((fd_set *) 0)->fds_bits[0]);
560 : : }
561 : :
562 : : gint
563 : : g_poll (GPollFD *fds,
564 : : guint nfds,
565 : : gint timeout)
566 : : {
567 : : struct timeval tv;
568 : : fd_set rset_stack, wset_stack, xset_stack;
569 : : fd_set *rset = &rset_stack;
570 : : fd_set *wset = &wset_stack;
571 : : fd_set *xset = &xset_stack;
572 : : GPollFD *f, *f_end;
573 : : int ready;
574 : : int maxfd = 0;
575 : : const gushort poll_events = G_IO_IN | G_IO_OUT | G_IO_PRI;
576 : : gpointer heap_storage = NULL;
577 : : gsize fd_set_bytes = 0;
578 : :
579 : : f_end = fds + nfds;
580 : :
581 : : for (f = fds; f < f_end; ++f)
582 : : if (f->fd >= 0 &&
583 : : (f->events & poll_events) &&
584 : : f->fd > maxfd)
585 : : maxfd = f->fd;
586 : :
587 : : if (G_UNLIKELY (maxfd >= FD_SETSIZE))
588 : : {
589 : : fd_set_bytes = g_poll_fd_set_alloc_size (maxfd);
590 : : heap_storage = g_malloc (fd_set_bytes * 3);
591 : : rset = heap_storage;
592 : : wset = (fd_set *) ((char *) heap_storage + fd_set_bytes);
593 : : xset = (fd_set *) ((char *) heap_storage + (fd_set_bytes * 2));
594 : : }
595 : :
596 : : FD_ZERO (rset);
597 : : FD_ZERO (wset);
598 : : FD_ZERO (xset);
599 : :
600 : : for (f = fds; f < f_end; ++f)
601 : : if (f->fd >= 0)
602 : : {
603 : : if (f->events & G_IO_IN)
604 : : FD_SET (f->fd, rset);
605 : : if (f->events & G_IO_OUT)
606 : : FD_SET (f->fd, wset);
607 : : if (f->events & G_IO_PRI)
608 : : FD_SET (f->fd, xset);
609 : : }
610 : :
611 : : tv.tv_sec = timeout / 1000;
612 : : tv.tv_usec = (timeout % 1000) * 1000;
613 : :
614 : : ready = select (maxfd + 1, rset, wset, xset,
615 : : timeout == -1 ? NULL : &tv);
616 : : if (ready > 0)
617 : : for (f = fds; f < f_end; ++f)
618 : : {
619 : : f->revents = 0;
620 : : if (f->fd >= 0)
621 : : {
622 : : if (FD_ISSET (f->fd, rset))
623 : : f->revents |= G_IO_IN;
624 : : if (FD_ISSET (f->fd, wset))
625 : : f->revents |= G_IO_OUT;
626 : : if (FD_ISSET (f->fd, xset))
627 : : f->revents |= G_IO_PRI;
628 : : }
629 : : }
630 : :
631 : : g_free (heap_storage);
632 : :
633 : : return ready;
634 : : }
635 : :
636 : : #endif /* !G_OS_WIN32 */
637 : :
638 : : #endif /* !HAVE_POLL */
|