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