1 /*
2  * libev poll fd activity backend
3  *
4  * Copyright (c) 2007,2008,2009,2010,2011,2016,2019 Marc Alexander Lehmann <libev@schmorp.de>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without modifica-
8  * tion, are permitted provided that the following conditions are met:
9  *
10  *   1.  Redistributions of source code must retain the above copyright notice,
11  *       this list of conditions and the following disclaimer.
12  *
13  *   2.  Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in the
15  *       documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
19  * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
21  * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
25  * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
26  * OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * Alternatively, the contents of this file may be used under the terms of
29  * the GNU General Public License ("GPL") version 2 or any later version,
30  * in which case the provisions of the GPL are applicable instead of
31  * the above. If you wish to allow the use of your version of this file
32  * only under the terms of the GPL and not to allow others to use your
33  * version of this file under the BSD license, indicate your decision
34  * by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL. If you do not delete the
36  * provisions above, a recipient may use your version of this file under
37  * either the BSD or the GPL.
38  */
39 
40 #include <poll.h>
41 
42 inline_size
43 void
array_needsize_pollidx(int * base,int offset,int count)44 array_needsize_pollidx (int *base, int offset, int count)
45 {
46   /* using memset (.., -1, ...) is tempting, we we try
47    * to be ultraportable
48    */
49   base += offset;
50   while (count--)
51     *base++ = -1;
52 }
53 
54 static void
poll_modify(EV_P_ int fd,int oev,int nev)55 poll_modify (EV_P_ int fd, int oev, int nev)
56 {
57   int idx;
58 
59   if (oev == nev)
60     return;
61 
62   array_needsize (int, pollidxs, pollidxmax, fd + 1, array_needsize_pollidx);
63 
64   idx = pollidxs [fd];
65 
66   if (idx < 0) /* need to allocate a new pollfd */
67     {
68       pollidxs [fd] = idx = pollcnt++;
69       array_needsize (struct pollfd, polls, pollmax, pollcnt, array_needsize_noinit);
70       polls [idx].fd = fd;
71     }
72 
73   assert (polls [idx].fd == fd);
74 
75   if (nev)
76     polls [idx].events =
77         (nev & EV_READ ? POLLIN : 0)
78         | (nev & EV_WRITE ? POLLOUT : 0);
79   else /* remove pollfd */
80     {
81       pollidxs [fd] = -1;
82 
83       if (ecb_expect_true (idx < --pollcnt))
84         {
85           polls [idx] = polls [pollcnt];
86           pollidxs [polls [idx].fd] = idx;
87         }
88     }
89 }
90 
91 static void
poll_poll(EV_P_ ev_tstamp timeout)92 poll_poll (EV_P_ ev_tstamp timeout)
93 {
94   struct pollfd *p;
95   int res;
96 
97   EV_RELEASE_CB;
98   res = poll (polls, pollcnt, EV_TS_TO_MSEC (timeout));
99   EV_ACQUIRE_CB;
100 
101   if (ecb_expect_false (res < 0))
102     {
103       if (errno == EBADF)
104         fd_ebadf (EV_A);
105       else if (errno == ENOMEM && !syserr_cb)
106         fd_enomem (EV_A);
107       else if (errno != EINTR)
108         ev_syserr ("(libev) poll");
109     }
110   else
111     for (p = polls; res; ++p)
112       {
113         assert (("libev: poll returned illegal result, broken BSD kernel?", p < polls + pollcnt));
114 
115         if (ecb_expect_false (p->revents)) /* this expect is debatable */
116           {
117             --res;
118 
119             if (ecb_expect_false (p->revents & POLLNVAL))
120               {
121                 assert (("libev: poll found invalid fd in poll set", 0));
122                 fd_kill (EV_A_ p->fd);
123               }
124             else
125               fd_event (
126                 EV_A_
127                 p->fd,
128                 (p->revents & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0)
129                 | (p->revents & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0)
130               );
131           }
132       }
133 }
134 
135 inline_size
136 int
poll_init(EV_P_ int flags)137 poll_init (EV_P_ int flags)
138 {
139   backend_mintime = EV_TS_CONST (1e-3);
140   backend_modify  = poll_modify;
141   backend_poll    = poll_poll;
142 
143   pollidxs = 0; pollidxmax = 0;
144   polls    = 0; pollmax    = 0; pollcnt = 0;
145 
146   return EVBACKEND_POLL;
147 }
148 
149 inline_size
150 void
poll_destroy(EV_P)151 poll_destroy (EV_P)
152 {
153   ev_free (pollidxs);
154   ev_free (polls);
155 }
156 
157