]> git.sesse.net Git - vlc/blob - src/win32/poll.c
Add a mailmap
[vlc] / src / win32 / poll.c
1 /*****************************************************************************
2  * poll.c: poll() emulation for Winsock
3  *****************************************************************************
4  * Copyright © 2007 Rémi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28
29 #include <vlc_common.h>
30
31 #ifdef FD_SETSIZE
32 /* Too late for #undef FD_SETSIZE to work: fd_set is already defined. */
33 # error Header inclusion order compromised!
34 #endif
35 #define FD_SETSIZE 0
36 #include <vlc_network.h>
37
38 #ifdef __OS2__
39 #include <sys/time.h>
40 #include <sys/select.h>
41 #define SOCKET unsigned
42 #endif
43
44 int vlc_poll (struct pollfd *fds, unsigned nfds, int timeout)
45 {
46     size_t setsize = sizeof (fd_set) + nfds * sizeof (SOCKET);
47     fd_set *rdset = malloc (setsize);
48     fd_set *wrset = malloc (setsize);
49     fd_set *exset = malloc (setsize);
50     struct timeval tv = { 0, 0 };
51     int val;
52
53     if (unlikely(rdset == NULL || wrset == NULL || exset == NULL))
54     {
55         free (rdset);
56         free (wrset);
57         free (exset);
58         errno = ENOMEM;
59         return -1;
60     }
61
62 /* Winsock FD_SET uses FD_SETSIZE in its expansion */
63 #undef FD_SETSIZE
64 #define FD_SETSIZE (nfds)
65
66 resume:
67     val = -1;
68     vlc_testcancel ();
69
70     FD_ZERO (rdset);
71     FD_ZERO (wrset);
72     FD_ZERO (exset);
73     for (unsigned i = 0; i < nfds; i++)
74     {
75         int fd = fds[i].fd;
76         if (val < fd)
77             val = fd;
78
79         /* With POSIX, FD_SET & FD_ISSET are not defined if fd is negative or
80          * bigger or equal than FD_SETSIZE. That is one of the reasons why VLC
81          * uses poll() rather than select(). Most POSIX systems implement
82          * fd_set has a bit field with no sanity checks. This is especially bad
83          * on systems (such as BSD) that have no process open files limit by
84          * default, such that it is quite feasible to get fd >= FD_SETSIZE.
85          * The next instructions will result in a buffer overflow if run on
86          * a POSIX system, and the later FD_ISSET would perform an undefined
87          * memory read.
88          *
89          * With Winsock, fd_set is a table of integers. This is awfully slow.
90          * However, FD_SET and FD_ISSET silently and safely discard excess
91          * entries. Here, overflow cannot happen anyway: fd_set of adequate
92          * size are allocated.
93          * Note that Vista has a much nicer WSAPoll(), but Mingw does not
94          * support it yet.
95          */
96         if (fds[i].events & POLLIN)
97             FD_SET ((SOCKET)fd, rdset);
98         if (fds[i].events & POLLOUT)
99             FD_SET ((SOCKET)fd, wrset);
100         if (fds[i].events & POLLPRI)
101             FD_SET ((SOCKET)fd, exset);
102     }
103
104 #ifndef HAVE_ALERTABLE_SELECT
105 # warning FIXME! Fix cancellation and remove this crap.
106     if ((timeout < 0) || (timeout > 50))
107     {
108         tv.tv_sec = 0;
109         tv.tv_usec = 50000;
110     }
111     else
112 #endif
113     if (timeout >= 0)
114     {
115         div_t d = div (timeout, 1000);
116         tv.tv_sec = d.quot;
117         tv.tv_usec = d.rem * 1000;
118     }
119
120     val = select (val + 1, rdset, wrset, exset,
121                   /*(timeout >= 0) ?*/ &tv /*: NULL*/);
122
123 #ifndef HAVE_ALERTABLE_SELECT
124     if (val == 0)
125     {
126         if (timeout > 0)
127             timeout -= (timeout > 50) ? 50 : timeout;
128         if (timeout != 0)
129             goto resume;
130     }
131 #endif
132
133     if (val == -1)
134         return -1;
135
136     for (unsigned i = 0; i < nfds; i++)
137     {
138         int fd = fds[i].fd;
139         fds[i].revents = (FD_ISSET (fd, rdset) ? POLLIN : 0)
140                        | (FD_ISSET (fd, wrset) ? POLLOUT : 0)
141                        | (FD_ISSET (fd, exset) ? POLLPRI : 0);
142     }
143     free (exset);
144     free (wrset);
145     free (rdset);
146     return val;
147 }