xref: /libev/ev_select.c (revision 93823e6c)
1e3a38431SPaul Bohm /*
2e3a38431SPaul Bohm  * libev select fd activity backend
3e3a38431SPaul Bohm  *
4*93823e6cSPaul Bohm  * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <[email protected]>
5e3a38431SPaul Bohm  * All rights reserved.
6e3a38431SPaul Bohm  *
7e3a38431SPaul Bohm  * Redistribution and use in source and binary forms, with or without modifica-
8e3a38431SPaul Bohm  * tion, are permitted provided that the following conditions are met:
9e3a38431SPaul Bohm  *
10e3a38431SPaul Bohm  *   1.  Redistributions of source code must retain the above copyright notice,
11e3a38431SPaul Bohm  *       this list of conditions and the following disclaimer.
12e3a38431SPaul Bohm  *
13e3a38431SPaul Bohm  *   2.  Redistributions in binary form must reproduce the above copyright
14e3a38431SPaul Bohm  *       notice, this list of conditions and the following disclaimer in the
15e3a38431SPaul Bohm  *       documentation and/or other materials provided with the distribution.
16e3a38431SPaul Bohm  *
17e3a38431SPaul Bohm  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18e3a38431SPaul Bohm  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
19e3a38431SPaul Bohm  * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
20e3a38431SPaul Bohm  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
21e3a38431SPaul Bohm  * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22e3a38431SPaul Bohm  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23e3a38431SPaul Bohm  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24e3a38431SPaul Bohm  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
25e3a38431SPaul Bohm  * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
26e3a38431SPaul Bohm  * OF THE POSSIBILITY OF SUCH DAMAGE.
27e3a38431SPaul Bohm  *
28e3a38431SPaul Bohm  * Alternatively, the contents of this file may be used under the terms of
29e3a38431SPaul Bohm  * the GNU General Public License ("GPL") version 2 or any later version,
30e3a38431SPaul Bohm  * in which case the provisions of the GPL are applicable instead of
31e3a38431SPaul Bohm  * the above. If you wish to allow the use of your version of this file
32e3a38431SPaul Bohm  * only under the terms of the GPL and not to allow others to use your
33e3a38431SPaul Bohm  * version of this file under the BSD license, indicate your decision
34e3a38431SPaul Bohm  * by deleting the provisions above and replace them with the notice
35e3a38431SPaul Bohm  * and other provisions required by the GPL. If you do not delete the
36e3a38431SPaul Bohm  * provisions above, a recipient may use your version of this file under
37e3a38431SPaul Bohm  * either the BSD or the GPL.
38e3a38431SPaul Bohm  */
39e3a38431SPaul Bohm 
40e3a38431SPaul Bohm #ifndef _WIN32
41e3a38431SPaul Bohm /* for unix systems */
42e3a38431SPaul Bohm # include <inttypes.h>
43e3a38431SPaul Bohm # ifndef __hpux
44e3a38431SPaul Bohm /* for REAL unix systems */
45e3a38431SPaul Bohm #  include <sys/select.h>
46e3a38431SPaul Bohm # endif
47e3a38431SPaul Bohm #endif
48e3a38431SPaul Bohm 
49e3a38431SPaul Bohm #ifndef EV_SELECT_USE_FD_SET
50e3a38431SPaul Bohm # ifdef NFDBITS
51e3a38431SPaul Bohm #  define EV_SELECT_USE_FD_SET 0
52e3a38431SPaul Bohm # else
53e3a38431SPaul Bohm #  define EV_SELECT_USE_FD_SET 1
54e3a38431SPaul Bohm # endif
55e3a38431SPaul Bohm #endif
56e3a38431SPaul Bohm 
57e3a38431SPaul Bohm #if EV_SELECT_IS_WINSOCKET
58e3a38431SPaul Bohm # undef EV_SELECT_USE_FD_SET
59e3a38431SPaul Bohm # define EV_SELECT_USE_FD_SET 1
60e3a38431SPaul Bohm # undef NFDBITS
61e3a38431SPaul Bohm # define NFDBITS 0
62e3a38431SPaul Bohm #endif
63e3a38431SPaul Bohm 
64e3a38431SPaul Bohm #if !EV_SELECT_USE_FD_SET
65e3a38431SPaul Bohm # define NFDBYTES (NFDBITS / 8)
66e3a38431SPaul Bohm #endif
67e3a38431SPaul Bohm 
68e3a38431SPaul Bohm #include <string.h>
69e3a38431SPaul Bohm 
70e3a38431SPaul Bohm static void
select_modify(EV_P_ int fd,int oev,int nev)71e3a38431SPaul Bohm select_modify (EV_P_ int fd, int oev, int nev)
72e3a38431SPaul Bohm {
73e3a38431SPaul Bohm   if (oev == nev)
74e3a38431SPaul Bohm     return;
75e3a38431SPaul Bohm 
76e3a38431SPaul Bohm   {
77e3a38431SPaul Bohm #if EV_SELECT_USE_FD_SET
78e3a38431SPaul Bohm 
79e3a38431SPaul Bohm     #if EV_SELECT_IS_WINSOCKET
80e3a38431SPaul Bohm     SOCKET handle = anfds [fd].handle;
81e3a38431SPaul Bohm     #else
82e3a38431SPaul Bohm     int handle = fd;
83e3a38431SPaul Bohm     #endif
84e3a38431SPaul Bohm 
85e3a38431SPaul Bohm     assert (("libev: fd >= FD_SETSIZE passed to fd_set-based select backend", fd < FD_SETSIZE));
86e3a38431SPaul Bohm 
87e3a38431SPaul Bohm     /* FD_SET is broken on windows (it adds the fd to a set twice or more,
88e3a38431SPaul Bohm      * which eventually leads to overflows). Need to call it only on changes.
89e3a38431SPaul Bohm      */
90e3a38431SPaul Bohm     #if EV_SELECT_IS_WINSOCKET
91e3a38431SPaul Bohm     if ((oev ^ nev) & EV_READ)
92e3a38431SPaul Bohm     #endif
93e3a38431SPaul Bohm       if (nev & EV_READ)
94e3a38431SPaul Bohm         FD_SET (handle, (fd_set *)vec_ri);
95e3a38431SPaul Bohm       else
96e3a38431SPaul Bohm         FD_CLR (handle, (fd_set *)vec_ri);
97e3a38431SPaul Bohm 
98e3a38431SPaul Bohm     #if EV_SELECT_IS_WINSOCKET
99e3a38431SPaul Bohm     if ((oev ^ nev) & EV_WRITE)
100e3a38431SPaul Bohm     #endif
101e3a38431SPaul Bohm       if (nev & EV_WRITE)
102e3a38431SPaul Bohm         FD_SET (handle, (fd_set *)vec_wi);
103e3a38431SPaul Bohm       else
104e3a38431SPaul Bohm         FD_CLR (handle, (fd_set *)vec_wi);
105e3a38431SPaul Bohm 
106e3a38431SPaul Bohm #else
107e3a38431SPaul Bohm 
108e3a38431SPaul Bohm     int     word = fd / NFDBITS;
109e3a38431SPaul Bohm     fd_mask mask = 1UL << (fd % NFDBITS);
110e3a38431SPaul Bohm 
111e3a38431SPaul Bohm     if (expect_false (vec_max <= word))
112e3a38431SPaul Bohm       {
113e3a38431SPaul Bohm         int new_max = word + 1;
114e3a38431SPaul Bohm 
115e3a38431SPaul Bohm         vec_ri = ev_realloc (vec_ri, new_max * NFDBYTES);
116e3a38431SPaul Bohm         vec_ro = ev_realloc (vec_ro, new_max * NFDBYTES); /* could free/malloc */
117e3a38431SPaul Bohm         vec_wi = ev_realloc (vec_wi, new_max * NFDBYTES);
118e3a38431SPaul Bohm         vec_wo = ev_realloc (vec_wo, new_max * NFDBYTES); /* could free/malloc */
119e3a38431SPaul Bohm         #ifdef _WIN32
120e3a38431SPaul Bohm         vec_eo = ev_realloc (vec_eo, new_max * NFDBYTES); /* could free/malloc */
121e3a38431SPaul Bohm         #endif
122e3a38431SPaul Bohm 
123e3a38431SPaul Bohm         for (; vec_max < new_max; ++vec_max)
124e3a38431SPaul Bohm           ((fd_mask *)vec_ri) [vec_max] =
125e3a38431SPaul Bohm           ((fd_mask *)vec_wi) [vec_max] = 0;
126e3a38431SPaul Bohm       }
127e3a38431SPaul Bohm 
128e3a38431SPaul Bohm     ((fd_mask *)vec_ri) [word] |= mask;
129e3a38431SPaul Bohm     if (!(nev & EV_READ))
130e3a38431SPaul Bohm       ((fd_mask *)vec_ri) [word] &= ~mask;
131e3a38431SPaul Bohm 
132e3a38431SPaul Bohm     ((fd_mask *)vec_wi) [word] |= mask;
133e3a38431SPaul Bohm     if (!(nev & EV_WRITE))
134e3a38431SPaul Bohm       ((fd_mask *)vec_wi) [word] &= ~mask;
135e3a38431SPaul Bohm #endif
136e3a38431SPaul Bohm   }
137e3a38431SPaul Bohm }
138e3a38431SPaul Bohm 
139e3a38431SPaul Bohm static void
select_poll(EV_P_ ev_tstamp timeout)140e3a38431SPaul Bohm select_poll (EV_P_ ev_tstamp timeout)
141e3a38431SPaul Bohm {
142e3a38431SPaul Bohm   struct timeval tv;
143e3a38431SPaul Bohm   int res;
144e3a38431SPaul Bohm   int fd_setsize;
145e3a38431SPaul Bohm 
146e3a38431SPaul Bohm   EV_RELEASE_CB;
147e3a38431SPaul Bohm   EV_TV_SET (tv, timeout);
148e3a38431SPaul Bohm 
149e3a38431SPaul Bohm #if EV_SELECT_USE_FD_SET
150e3a38431SPaul Bohm   fd_setsize = sizeof (fd_set);
151e3a38431SPaul Bohm #else
152e3a38431SPaul Bohm   fd_setsize = vec_max * NFDBYTES;
153e3a38431SPaul Bohm #endif
154e3a38431SPaul Bohm 
155e3a38431SPaul Bohm   memcpy (vec_ro, vec_ri, fd_setsize);
156e3a38431SPaul Bohm   memcpy (vec_wo, vec_wi, fd_setsize);
157e3a38431SPaul Bohm 
158e3a38431SPaul Bohm #ifdef _WIN32
159e3a38431SPaul Bohm   /* pass in the write set as except set.
160e3a38431SPaul Bohm    * the idea behind this is to work around a windows bug that causes
161e3a38431SPaul Bohm    * errors to be reported as an exception and not by setting
162e3a38431SPaul Bohm    * the writable bit. this is so uncontrollably lame.
163e3a38431SPaul Bohm    */
164e3a38431SPaul Bohm   memcpy (vec_eo, vec_wi, fd_setsize);
165e3a38431SPaul Bohm   res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, (fd_set *)vec_eo, &tv);
166e3a38431SPaul Bohm #elif EV_SELECT_USE_FD_SET
167e3a38431SPaul Bohm   fd_setsize = anfdmax < FD_SETSIZE ? anfdmax : FD_SETSIZE;
168e3a38431SPaul Bohm   res = select (fd_setsize, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv);
169e3a38431SPaul Bohm #else
170e3a38431SPaul Bohm   res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv);
171e3a38431SPaul Bohm #endif
172e3a38431SPaul Bohm   EV_ACQUIRE_CB;
173e3a38431SPaul Bohm 
174e3a38431SPaul Bohm   if (expect_false (res < 0))
175e3a38431SPaul Bohm     {
176e3a38431SPaul Bohm       #if EV_SELECT_IS_WINSOCKET
177e3a38431SPaul Bohm       errno = WSAGetLastError ();
178e3a38431SPaul Bohm       #endif
179e3a38431SPaul Bohm       #ifdef WSABASEERR
180e3a38431SPaul Bohm       /* on windows, select returns incompatible error codes, fix this */
181e3a38431SPaul Bohm       if (errno >= WSABASEERR && errno < WSABASEERR + 1000)
182e3a38431SPaul Bohm         if (errno == WSAENOTSOCK)
183e3a38431SPaul Bohm           errno = EBADF;
184e3a38431SPaul Bohm         else
185e3a38431SPaul Bohm           errno -= WSABASEERR;
186e3a38431SPaul Bohm       #endif
187e3a38431SPaul Bohm 
188e3a38431SPaul Bohm       #ifdef _WIN32
189e3a38431SPaul Bohm       /* select on windows erroneously returns EINVAL when no fd sets have been
190e3a38431SPaul Bohm        * provided (this is documented). what microsoft doesn't tell you that this bug
191e3a38431SPaul Bohm        * exists even when the fd sets _are_ provided, so we have to check for this bug
192e3a38431SPaul Bohm        * here and emulate by sleeping manually.
193e3a38431SPaul Bohm        * we also get EINVAL when the timeout is invalid, but we ignore this case here
194e3a38431SPaul Bohm        * and assume that EINVAL always means: you have to wait manually.
195e3a38431SPaul Bohm        */
196e3a38431SPaul Bohm       if (errno == EINVAL)
197e3a38431SPaul Bohm         {
198*93823e6cSPaul Bohm           if (timeout)
199*93823e6cSPaul Bohm             {
200*93823e6cSPaul Bohm               unsigned long ms = timeout * 1e3;
201*93823e6cSPaul Bohm               Sleep (ms ? ms : 1);
202*93823e6cSPaul Bohm             }
203*93823e6cSPaul Bohm 
204e3a38431SPaul Bohm           return;
205e3a38431SPaul Bohm         }
206e3a38431SPaul Bohm       #endif
207e3a38431SPaul Bohm 
208e3a38431SPaul Bohm       if (errno == EBADF)
209e3a38431SPaul Bohm         fd_ebadf (EV_A);
210e3a38431SPaul Bohm       else if (errno == ENOMEM && !syserr_cb)
211e3a38431SPaul Bohm         fd_enomem (EV_A);
212e3a38431SPaul Bohm       else if (errno != EINTR)
213e3a38431SPaul Bohm         ev_syserr ("(libev) select");
214e3a38431SPaul Bohm 
215e3a38431SPaul Bohm       return;
216e3a38431SPaul Bohm     }
217e3a38431SPaul Bohm 
218e3a38431SPaul Bohm #if EV_SELECT_USE_FD_SET
219e3a38431SPaul Bohm 
220e3a38431SPaul Bohm   {
221e3a38431SPaul Bohm     int fd;
222e3a38431SPaul Bohm 
223e3a38431SPaul Bohm     for (fd = 0; fd < anfdmax; ++fd)
224e3a38431SPaul Bohm       if (anfds [fd].events)
225e3a38431SPaul Bohm         {
226e3a38431SPaul Bohm           int events = 0;
227e3a38431SPaul Bohm           #if EV_SELECT_IS_WINSOCKET
228e3a38431SPaul Bohm           SOCKET handle = anfds [fd].handle;
229e3a38431SPaul Bohm           #else
230e3a38431SPaul Bohm           int handle = fd;
231e3a38431SPaul Bohm           #endif
232e3a38431SPaul Bohm 
233e3a38431SPaul Bohm           if (FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ;
234e3a38431SPaul Bohm           if (FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE;
235e3a38431SPaul Bohm           #ifdef _WIN32
236e3a38431SPaul Bohm           if (FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE;
237e3a38431SPaul Bohm           #endif
238e3a38431SPaul Bohm 
239e3a38431SPaul Bohm           if (expect_true (events))
240e3a38431SPaul Bohm             fd_event (EV_A_ fd, events);
241e3a38431SPaul Bohm         }
242e3a38431SPaul Bohm   }
243e3a38431SPaul Bohm 
244e3a38431SPaul Bohm #else
245e3a38431SPaul Bohm 
246e3a38431SPaul Bohm   {
247e3a38431SPaul Bohm     int word, bit;
248e3a38431SPaul Bohm     for (word = vec_max; word--; )
249e3a38431SPaul Bohm       {
250e3a38431SPaul Bohm         fd_mask word_r = ((fd_mask *)vec_ro) [word];
251e3a38431SPaul Bohm         fd_mask word_w = ((fd_mask *)vec_wo) [word];
252e3a38431SPaul Bohm         #ifdef _WIN32
253e3a38431SPaul Bohm         word_w |= ((fd_mask *)vec_eo) [word];
254e3a38431SPaul Bohm         #endif
255e3a38431SPaul Bohm 
256e3a38431SPaul Bohm         if (word_r || word_w)
257e3a38431SPaul Bohm           for (bit = NFDBITS; bit--; )
258e3a38431SPaul Bohm             {
259e3a38431SPaul Bohm               fd_mask mask = 1UL << bit;
260e3a38431SPaul Bohm               int events = 0;
261e3a38431SPaul Bohm 
262e3a38431SPaul Bohm               events |= word_r & mask ? EV_READ  : 0;
263e3a38431SPaul Bohm               events |= word_w & mask ? EV_WRITE : 0;
264e3a38431SPaul Bohm 
265e3a38431SPaul Bohm               if (expect_true (events))
266e3a38431SPaul Bohm                 fd_event (EV_A_ word * NFDBITS + bit, events);
267e3a38431SPaul Bohm             }
268e3a38431SPaul Bohm       }
269e3a38431SPaul Bohm   }
270e3a38431SPaul Bohm 
271e3a38431SPaul Bohm #endif
272e3a38431SPaul Bohm }
273e3a38431SPaul Bohm 
274e3a38431SPaul Bohm int inline_size
select_init(EV_P_ int flags)275e3a38431SPaul Bohm select_init (EV_P_ int flags)
276e3a38431SPaul Bohm {
277*93823e6cSPaul Bohm   backend_mintime = 1e-6;
278e3a38431SPaul Bohm   backend_modify  = select_modify;
279e3a38431SPaul Bohm   backend_poll    = select_poll;
280e3a38431SPaul Bohm 
281e3a38431SPaul Bohm #if EV_SELECT_USE_FD_SET
282e3a38431SPaul Bohm   vec_ri  = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri);
283e3a38431SPaul Bohm   vec_ro  = ev_malloc (sizeof (fd_set));
284e3a38431SPaul Bohm   vec_wi  = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi);
285e3a38431SPaul Bohm   vec_wo  = ev_malloc (sizeof (fd_set));
286e3a38431SPaul Bohm   #ifdef _WIN32
287e3a38431SPaul Bohm   vec_eo  = ev_malloc (sizeof (fd_set));
288e3a38431SPaul Bohm   #endif
289e3a38431SPaul Bohm #else
290e3a38431SPaul Bohm   vec_max = 0;
291e3a38431SPaul Bohm   vec_ri  = 0;
292e3a38431SPaul Bohm   vec_ro  = 0;
293e3a38431SPaul Bohm   vec_wi  = 0;
294e3a38431SPaul Bohm   vec_wo  = 0;
295e3a38431SPaul Bohm   #ifdef _WIN32
296e3a38431SPaul Bohm   vec_eo  = 0;
297e3a38431SPaul Bohm   #endif
298e3a38431SPaul Bohm #endif
299e3a38431SPaul Bohm 
300e3a38431SPaul Bohm   return EVBACKEND_SELECT;
301e3a38431SPaul Bohm }
302e3a38431SPaul Bohm 
303e3a38431SPaul Bohm void inline_size
select_destroy(EV_P)304e3a38431SPaul Bohm select_destroy (EV_P)
305e3a38431SPaul Bohm {
306e3a38431SPaul Bohm   ev_free (vec_ri);
307e3a38431SPaul Bohm   ev_free (vec_ro);
308e3a38431SPaul Bohm   ev_free (vec_wi);
309e3a38431SPaul Bohm   ev_free (vec_wo);
310e3a38431SPaul Bohm   #ifdef _WIN32
311e3a38431SPaul Bohm   ev_free (vec_eo);
312e3a38431SPaul Bohm   #endif
313e3a38431SPaul Bohm }
314e3a38431SPaul Bohm 
315