Line | Count | Source |
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 | | g_poll (GPollFD *fds, |
128 | | guint nfds, |
129 | | gint timeout) |
130 | 0 | { |
131 | 0 | return poll ((struct pollfd *)fds, nfds, timeout); |
132 | 0 | } |
133 | | |
134 | | #else /* !HAVE_POLL */ |
135 | | |
136 | | #ifdef G_OS_WIN32 |
137 | | |
138 | | static int |
139 | | 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 | | if (msg_fd != NULL) |
151 | | { |
152 | | /* Wait for either messages or handles |
153 | | * -> Use MsgWaitForMultipleObjectsEx |
154 | | */ |
155 | | if (_g_main_poll_debug) |
156 | | g_print (" MsgWaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms); |
157 | | |
158 | | ready = MsgWaitForMultipleObjectsEx (nhandles, handles, timeout_ms, |
159 | | QS_ALLINPUT, MWMO_ALERTABLE); |
160 | | |
161 | | if (ready == WAIT_FAILED) |
162 | | { |
163 | | gchar *emsg = g_win32_error_message (GetLastError ()); |
164 | | g_warning ("MsgWaitForMultipleObjectsEx failed: %s", emsg); |
165 | | g_free (emsg); |
166 | | } |
167 | | } |
168 | | else if (nhandles == 0) |
169 | | { |
170 | | /* No handles to wait for, just the timeout */ |
171 | | if (timeout_ms == INFINITE) |
172 | | ready = WAIT_FAILED; |
173 | | else |
174 | | { |
175 | | /* Wait for the current process to die, more efficient than SleepEx(). */ |
176 | | WaitForSingleObjectEx (GetCurrentProcess (), timeout_ms, TRUE); |
177 | | ready = WAIT_TIMEOUT; |
178 | | } |
179 | | } |
180 | | else |
181 | | { |
182 | | /* Wait for just handles |
183 | | * -> Use WaitForMultipleObjectsEx |
184 | | */ |
185 | | if (_g_main_poll_debug) |
186 | | g_print (" WaitForMultipleObjectsEx(%d, %lu)\n", nhandles, timeout_ms); |
187 | | |
188 | | ready = WaitForMultipleObjectsEx (nhandles, handles, FALSE, timeout_ms, TRUE); |
189 | | if (ready == WAIT_FAILED) |
190 | | { |
191 | | gchar *emsg = g_win32_error_message (GetLastError ()); |
192 | | g_warning ("WaitForMultipleObjectsEx failed: %s", emsg); |
193 | | g_free (emsg); |
194 | | } |
195 | | } |
196 | | |
197 | | if (_g_main_poll_debug) |
198 | | g_print (" wait returns %ld%s\n", |
199 | | ready, |
200 | | (ready == WAIT_FAILED ? " (WAIT_FAILED)" : |
201 | | (ready == WAIT_TIMEOUT ? " (WAIT_TIMEOUT)" : |
202 | | (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles ? " (msg)" : "")))); |
203 | | |
204 | | if (ready == WAIT_FAILED) |
205 | | return -1; |
206 | | else if (ready == WAIT_TIMEOUT || |
207 | | ready == WAIT_IO_COMPLETION) |
208 | | return 0; |
209 | | else if (msg_fd != NULL && ready == WAIT_OBJECT_0 + nhandles) |
210 | | { |
211 | | 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 | | if (timeout_ms != 0 || nhandles == 0) |
217 | | return 1; |
218 | | |
219 | | /* If no timeout and handles to poll, recurse to poll them, |
220 | | * too. |
221 | | */ |
222 | | recursed_result = poll_rest (NULL, stop_fd, handles, handle_to_fd, nhandles, 0); |
223 | | return (recursed_result == -1) ? -1 : 1 + recursed_result; |
224 | | } |
225 | | else if (ready < WAIT_OBJECT_0 + nhandles) |
226 | | { |
227 | | int retval; |
228 | | |
229 | | f = handle_to_fd[ready - WAIT_OBJECT_0]; |
230 | | f->revents = f->events; |
231 | | if (_g_main_poll_debug) |
232 | | g_print (" got event %p\n", (HANDLE) f->fd); |
233 | | |
234 | | /* Do not count the stop_fd */ |
235 | | 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 | | 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 | | shorter_handles = &handles[ready - WAIT_OBJECT_0 + 1]; |
248 | | shorter_handle_to_fd = &handle_to_fd[ready - WAIT_OBJECT_0 + 1]; |
249 | | shorter_nhandles = nhandles - (ready - WAIT_OBJECT_0 + 1); |
250 | | |
251 | | recursed_result = poll_rest (NULL, stop_fd, shorter_handles, shorter_handle_to_fd, shorter_nhandles, 0); |
252 | | return (recursed_result == -1) ? -1 : retval + recursed_result; |
253 | | } |
254 | | return retval; |
255 | | } |
256 | | |
257 | | return 0; |
258 | | } |
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 | | poll_single_thread (GWin32PollThreadData *data) |
273 | | { |
274 | | int retval; |
275 | | |
276 | | /* Polling for several things? */ |
277 | | 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 | | 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 | | if (retval == 0 && (data->timeout_ms == INFINITE || data->timeout_ms > 0)) |
289 | | retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, data->timeout_ms); |
290 | | } |
291 | | else |
292 | | { |
293 | | /* Just polling for one thing, so no need to check first if |
294 | | * available immediately |
295 | | */ |
296 | | retval = poll_rest (data->msg_fd, data->stop_fd, data->handles, data->handle_to_fd, data->nhandles, data->timeout_ms); |
297 | | } |
298 | | data->retval = retval; |
299 | | |
300 | | return data->retval; |
301 | | } |
302 | | |
303 | | static VOID CALLBACK |
304 | | poll_single_worker_wrapper (PTP_CALLBACK_INSTANCE instance, |
305 | | PVOID context, |
306 | | PTP_WORK work) |
307 | | { |
308 | | UNREFERENCED_PARAMETER (instance); |
309 | | UNREFERENCED_PARAMETER (work); |
310 | | |
311 | | GWin32PollThreadData *data = context; |
312 | | |
313 | | poll_single_thread (data); |
314 | | |
315 | | /* Signal the stop in case any of the workers did not stop yet */ |
316 | | if (!SetEvent ((HANDLE) data->stop_fd->fd)) |
317 | | { |
318 | | gchar *emsg = g_win32_error_message (GetLastError ()); |
319 | | g_error ("gpoll: failed to signal the stop event: %s", emsg); |
320 | | g_free (emsg); |
321 | | } |
322 | | } |
323 | | |
324 | | static void |
325 | | 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 | | data->timeout_ms = timeout_ms; |
334 | | |
335 | | if (stop_fd != NULL) |
336 | | { |
337 | | if (_g_main_poll_debug) |
338 | | g_print (" Stop FD: %p", (HANDLE) stop_fd->fd); |
339 | | |
340 | | g_assert (data->nhandles < MAXIMUM_WAIT_OBJECTS); |
341 | | |
342 | | data->stop_fd = stop_fd; |
343 | | data->handle_to_fd[data->nhandles] = stop_fd; |
344 | | data->handles[data->nhandles++] = (HANDLE) stop_fd->fd; |
345 | | } |
346 | | |
347 | | for (f = fds; f < &fds[nfds]; ++f) |
348 | | { |
349 | | if ((data->nhandles == MAXIMUM_WAIT_OBJECTS) || |
350 | | (data->msg_fd != NULL && (data->nhandles == MAXIMUM_WAIT_OBJECTS - 1))) |
351 | | { |
352 | | g_warning ("Too many handles to wait for!"); |
353 | | break; |
354 | | } |
355 | | |
356 | | if (f->fd == G_WIN32_MSG_HANDLE && (f->events & G_IO_IN)) |
357 | | { |
358 | | if (_g_main_poll_debug && data->msg_fd == NULL) |
359 | | g_print (" MSG"); |
360 | | data->msg_fd = f; |
361 | | } |
362 | | else if (f->fd > 0) |
363 | | { |
364 | | if (_g_main_poll_debug) |
365 | | g_print (" %p", (HANDLE) f->fd); |
366 | | data->handle_to_fd[data->nhandles] = f; |
367 | | data->handles[data->nhandles++] = (HANDLE) f->fd; |
368 | | } |
369 | | |
370 | | f->revents = 0; |
371 | | } |
372 | | } |
373 | | |
374 | | static void |
375 | | cleanup_workers (guint nworkers, |
376 | | PTP_WORK *work_handles) |
377 | | { |
378 | | for (guint i = 0; i < nworkers; i++) |
379 | | { |
380 | | if (work_handles[i] != NULL) |
381 | | CloseThreadpoolWork (work_handles[i]); |
382 | | } |
383 | | } |
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 | | g_poll (GPollFD *fds, |
390 | | guint nfds, |
391 | | gint timeout) |
392 | | { |
393 | | guint nthreads, threads_remain; |
394 | | HANDLE worker_completed_handles[1] = { NULL, }; |
395 | | GWin32PollThreadData *threads_data; |
396 | | GPollFD stop_event = { 0, }; |
397 | | GPollFD *f; |
398 | | guint i, fds_idx = 0; |
399 | | DWORD ready; |
400 | | DWORD thread_retval; |
401 | | int retval; |
402 | | GPollFD *msg_fd = NULL; |
403 | | PTP_WORK work_handles[MAXIMUM_WAIT_OBJECTS] = { NULL, }; |
404 | | |
405 | | if (timeout == -1) |
406 | | timeout = INFINITE; |
407 | | |
408 | | /* Simple case without extra threads */ |
409 | | if (nfds <= MAXIMUM_WAIT_OBJECTS) |
410 | | { |
411 | | GWin32PollThreadData data = { 0, }; |
412 | | |
413 | | if (_g_main_poll_debug) |
414 | | g_print ("g_poll: waiting for"); |
415 | | |
416 | | fill_poll_thread_data (fds, nfds, timeout, NULL, &data); |
417 | | |
418 | | if (_g_main_poll_debug) |
419 | | g_print ("\n"); |
420 | | |
421 | | retval = poll_single_thread (&data); |
422 | | if (retval == -1) |
423 | | for (f = fds; f < &fds[nfds]; ++f) |
424 | | f->revents = 0; |
425 | | |
426 | | return retval; |
427 | | } |
428 | | |
429 | | if (_g_main_poll_debug) |
430 | | g_print ("g_poll: polling with threads\n"); |
431 | | |
432 | | nthreads = nfds / MAXIMUM_WAIT_OBJECTS_PER_THREAD; |
433 | | threads_remain = nfds % MAXIMUM_WAIT_OBJECTS_PER_THREAD; |
434 | | if (threads_remain > 0) |
435 | | nthreads++; |
436 | | |
437 | | if (nthreads > MAXIMUM_WAIT_OBJECTS_PER_THREAD) |
438 | | { |
439 | | g_warning ("Too many handles to wait for in threads!"); |
440 | | nthreads = MAXIMUM_WAIT_OBJECTS_PER_THREAD; |
441 | | } |
442 | | |
443 | | #if GLIB_SIZEOF_VOID_P == 8 |
444 | | stop_event.fd = (gint64)CreateEventW (NULL, TRUE, FALSE, NULL); |
445 | | #else |
446 | | stop_event.fd = (gint)CreateEventW (NULL, TRUE, FALSE, NULL); |
447 | | #endif |
448 | | stop_event.events = G_IO_IN; |
449 | | worker_completed_handles[0] = (HANDLE) stop_event.fd; |
450 | | |
451 | | threads_data = g_new0 (GWin32PollThreadData, nthreads); |
452 | | |
453 | | for (i = 0; i < nthreads; i++) |
454 | | { |
455 | | guint thread_fds; |
456 | | |
457 | | if (i == (nthreads - 1) && threads_remain > 0) |
458 | | thread_fds = threads_remain; |
459 | | else |
460 | | thread_fds = MAXIMUM_WAIT_OBJECTS_PER_THREAD; |
461 | | |
462 | | fill_poll_thread_data (fds + fds_idx, thread_fds, timeout, &stop_event, &threads_data[i]); |
463 | | fds_idx += thread_fds; |
464 | | |
465 | | /* We must poll for messages from the same thread, so poll it along with the threads */ |
466 | | if (threads_data[i].msg_fd != NULL) |
467 | | { |
468 | | msg_fd = threads_data[i].msg_fd; |
469 | | threads_data[i].msg_fd = NULL; |
470 | | } |
471 | | |
472 | | work_handles[i] = CreateThreadpoolWork (poll_single_worker_wrapper, &threads_data[i], |
473 | | NULL); |
474 | | if (work_handles[i] == NULL) |
475 | | { |
476 | | gchar *emsg = g_win32_error_message (GetLastError ()); |
477 | | g_error ("CreateThreadpoolWork failed: %s", emsg); |
478 | | g_free (emsg); |
479 | | retval = -1; |
480 | | goto cleanup; |
481 | | } |
482 | | |
483 | | SubmitThreadpoolWork (work_handles[i]); |
484 | | } |
485 | | |
486 | | /* Wait for at least one worker to return */ |
487 | | if (msg_fd != NULL) |
488 | | ready = MsgWaitForMultipleObjectsEx (1, worker_completed_handles, timeout, |
489 | | QS_ALLINPUT, MWMO_ALERTABLE); |
490 | | else |
491 | | ready = WaitForMultipleObjects (1, worker_completed_handles, FALSE, timeout); |
492 | | |
493 | | /* Signal the stop in case any of the workers did not stop yet */ |
494 | | if (!SetEvent ((HANDLE) stop_event.fd)) |
495 | | { |
496 | | gchar *emsg = g_win32_error_message (GetLastError ()); |
497 | | g_error ("gpoll: failed to signal the stop event: %s", emsg); |
498 | | g_free (emsg); |
499 | | retval = -1; |
500 | | 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 | | for (i = 0; i < nthreads; i++) |
507 | | WaitForThreadpoolWorkCallbacks (work_handles[i], FALSE); |
508 | | |
509 | | /* The return value of all the threads give us all the fds that changed state */ |
510 | | retval = 0; |
511 | | if (msg_fd != NULL && ready == WAIT_OBJECT_0 + 1) |
512 | | { |
513 | | msg_fd->revents |= G_IO_IN; |
514 | | retval = 1; |
515 | | } |
516 | | |
517 | | for (i = 0; i < nthreads; i++) |
518 | | { |
519 | | thread_retval = threads_data[i].retval; |
520 | | retval = (retval == -1) ? -1 : ((thread_retval == (DWORD) -1) ? -1 : (int) (retval + thread_retval)); |
521 | | } |
522 | | |
523 | | cleanup: |
524 | | if (retval == -1) |
525 | | { |
526 | | for (f = fds; f < &fds[nfds]; ++f) |
527 | | f->revents = 0; |
528 | | } |
529 | | cleanup_workers (nthreads, work_handles); |
530 | | g_free (threads_data); |
531 | | CloseHandle ((HANDLE) stop_event.fd); |
532 | | |
533 | | return retval; |
534 | | } |
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 */ |