dsm: add missing I/O error handling
[vlc.git] / compat / poll.c
1 /*****************************************************************************
2  * poll.c: poll() emulation
3  *****************************************************************************
4  * Copyright © 2007-2012 Rémi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * 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 #ifndef _WIN32
30 # include <sys/time.h>
31 # include <sys/select.h>
32 # include <fcntl.h>
33
34 int (poll) (struct pollfd *fds, unsigned nfds, int timeout)
35 {
36     fd_set rdset[1], wrset[1], exset[1];
37     struct timeval tv = { 0, 0 };
38     int val = -1;
39
40     FD_ZERO (rdset);
41     FD_ZERO (wrset);
42     FD_ZERO (exset);
43     for (unsigned i = 0; i < nfds; i++)
44     {
45         int fd = fds[i].fd;
46         if (val < fd)
47             val = fd;
48
49         /* With POSIX, FD_SET & FD_ISSET are not defined if fd is negative or
50          * bigger or equal than FD_SETSIZE. That is one of the reasons why VLC
51          * uses poll() rather than select(). Most POSIX systems implement
52          * fd_set has a bit field with no sanity checks. This is especially bad
53          * on systems (such as BSD) that have no process open files limit by
54          * default, such that it is quite feasible to get fd >= FD_SETSIZE.
55          * The next instructions will result in a buffer overflow if run on
56          * a POSIX system, and the later FD_ISSET would perform an undefined
57          * memory read. */
58         if ((unsigned)fd >= FD_SETSIZE)
59         {
60             errno = EINVAL;
61             return -1;
62         }
63         if (fds[i].events & POLLRDNORM)
64             FD_SET (fd, rdset);
65         if (fds[i].events & POLLWRNORM)
66             FD_SET (fd, wrset);
67         if (fds[i].events & POLLPRI)
68             FD_SET (fd, exset);
69     }
70
71     if (timeout >= 0)
72     {
73         div_t d = div (timeout, 1000);
74         tv.tv_sec = d.quot;
75         tv.tv_usec = d.rem * 1000;
76     }
77
78     val = select (val + 1, rdset, wrset, exset,
79                   (timeout >= 0) ? &tv : NULL);
80     if (val == -1)
81     {
82         if (errno != EBADF)
83             return -1;
84
85         val = 0;
86
87         for (unsigned i = 0; i < nfds; i++)
88             if (fcntl (fds[i].fd, F_GETFD) == -1)
89             {
90                 fds[i].revents = POLLNVAL;
91                 val++;
92             }
93             else
94                 fds[i].revents = 0;
95
96         return val ? val : -1;
97     }
98
99     for (unsigned i = 0; i < nfds; i++)
100     {
101         int fd = fds[i].fd;
102         fds[i].revents = (FD_ISSET (fd, rdset) ? POLLRDNORM : 0)
103                        | (FD_ISSET (fd, wrset) ? POLLWRNORM : 0)
104                        | (FD_ISSET (fd, exset) ? POLLPRI : 0);
105     }
106     return val;
107 }
108 #else
109 # include <windows.h>
110 # include <winsock2.h>
111
112 int poll(struct pollfd *fds, unsigned nfds, int timeout)
113 {
114     DWORD to = (timeout >= 0) ? (DWORD)timeout : INFINITE;
115
116     if (nfds == 0)
117     {    /* WSAWaitForMultipleEvents() does not allow zero events */
118         if (SleepEx(to, TRUE))
119         {
120             errno = EINTR;
121             return -1;
122         }
123         return 0;
124     }
125
126     WSAEVENT *evts = malloc(nfds * sizeof (WSAEVENT));
127     if (evts == NULL)
128         return -1; /* ENOMEM */
129
130     DWORD ret = WSA_WAIT_FAILED;
131     for (unsigned i = 0; i < nfds; i++)
132     {
133         SOCKET fd = fds[i].fd;
134         long mask = FD_CLOSE;
135         fd_set rdset, wrset, exset;
136
137         FD_ZERO(&rdset);
138         FD_ZERO(&wrset);
139         FD_ZERO(&exset);
140         FD_SET(fd, &exset);
141
142         if (fds[i].events & POLLRDNORM)
143         {
144             mask |= FD_READ | FD_ACCEPT;
145             FD_SET(fd, &rdset);
146         }
147         if (fds[i].events & POLLWRNORM)
148         {
149             mask |= FD_WRITE | FD_CONNECT;
150             FD_SET(fd, &wrset);
151         }
152         if (fds[i].events & POLLPRI)
153             mask |= FD_OOB;
154
155         fds[i].revents = 0;
156
157         evts[i] = WSACreateEvent();
158         if (evts[i] == WSA_INVALID_EVENT)
159         {
160             while (i > 0)
161                 WSACloseEvent(evts[--i]);
162             free(evts);
163             errno = ENOMEM;
164             return -1;
165         }
166
167         if (WSAEventSelect(fds[i].fd, evts[i], mask)
168          && WSAGetLastError() == WSAENOTSOCK)
169             fds[i].revents |= POLLNVAL;
170
171         struct timeval tv = { 0, 0 };
172         /* By its horrible design, WSAEnumNetworkEvents() only enumerates
173          * events that were not already signaled (i.e. it is edge-triggered).
174          * WSAPoll() would be better in this respect, but worse in others.
175          * So use WSAEnumNetworkEvents() after manually checking for pending
176          * events. */
177         if (select(0, &rdset, &wrset, &exset, &tv) > 0)
178         {
179             if (FD_ISSET(fd, &rdset))
180                 fds[i].revents |= fds[i].events & POLLRDNORM;
181             if (FD_ISSET(fd, &wrset))
182                 fds[i].revents |= fds[i].events & POLLWRNORM;
183             if (FD_ISSET(fd, &exset))
184                 /* To add pain to injury, POLLERR and POLLPRI cannot be
185                  * distinguished here. */
186                 fds[i].revents |= POLLERR | (fds[i].events & POLLPRI);
187         }
188
189         if (fds[i].revents != 0 && ret == WSA_WAIT_FAILED)
190             ret = WSA_WAIT_EVENT_0 + i;
191     }
192
193     if (ret == WSA_WAIT_FAILED)
194         ret = WSAWaitForMultipleEvents(nfds, evts, FALSE, to, TRUE);
195
196     unsigned count = 0;
197     for (unsigned i = 0; i < nfds; i++)
198     {
199         WSANETWORKEVENTS ne;
200
201         if (WSAEnumNetworkEvents(fds[i].fd, evts[i], &ne))
202             memset(&ne, 0, sizeof (ne));
203         WSAEventSelect(fds[i].fd, evts[i], 0);
204         WSACloseEvent(evts[i]);
205
206         if (ne.lNetworkEvents & FD_CONNECT)
207         {
208             fds[i].revents |= POLLWRNORM;
209             if (ne.iErrorCode[FD_CONNECT_BIT] != 0)
210                 fds[i].revents |= POLLERR;
211         }
212         if (ne.lNetworkEvents & FD_CLOSE)
213         {
214             fds[i].revents |= (fds[i].events & POLLRDNORM) | POLLHUP;
215             if (ne.iErrorCode[FD_CLOSE_BIT] != 0)
216                 fds[i].revents |= POLLERR;
217         }
218         if (ne.lNetworkEvents & FD_ACCEPT)
219         {
220             fds[i].revents |= POLLRDNORM;
221             if (ne.iErrorCode[FD_ACCEPT_BIT] != 0)
222                 fds[i].revents |= POLLERR;
223         }
224         if (ne.lNetworkEvents & FD_OOB)
225         {
226             fds[i].revents |= POLLPRI;
227             if (ne.iErrorCode[FD_OOB_BIT] != 0)
228                 fds[i].revents |= POLLERR;
229         }
230         if (ne.lNetworkEvents & FD_READ)
231         {
232             fds[i].revents |= POLLRDNORM;
233             if (ne.iErrorCode[FD_READ_BIT] != 0)
234                 fds[i].revents |= POLLERR;
235         }
236         if (ne.lNetworkEvents & FD_WRITE)
237         {
238             fds[i].revents |= POLLWRNORM;
239             if (ne.iErrorCode[FD_WRITE_BIT] != 0)
240                 fds[i].revents |= POLLERR;
241         }
242         count += fds[i].revents != 0;
243     }
244
245     free(evts);
246
247     if (count == 0 && ret == WSA_WAIT_IO_COMPLETION)
248     {
249         errno = EINTR;
250         return -1;
251     }
252     return count;
253 }
254 #endif