1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <errno.h>
17 #include <stdlib.h>
18 #include "iolooper.h"
19 #include "sockets.h"
20 
21 /* An implementation of iolooper.h based on Unix select() */
22 #ifdef _WIN32
23 #  include <winsock2.h>
24 #  include <time.h>
25 #else
26 #  include <sys/types.h>
27 #  include <sys/select.h>
28 #  include <sys/time.h>
29 #endif
30 
31 struct IoLooper {
32     fd_set   reads[1];
33     fd_set   writes[1];
34     fd_set   reads_result[1];
35     fd_set   writes_result[1];
36     int      max_fd;
37     int      max_fd_valid;
38 };
39 
40 IoLooper*
iolooper_new(void)41 iolooper_new(void)
42 {
43     IoLooper*  iol = malloc(sizeof(*iol));
44     iolooper_reset(iol);
45     return iol;
46 }
47 
48 void
iolooper_free(IoLooper * iol)49 iolooper_free( IoLooper*  iol )
50 {
51     free(iol);
52 }
53 
54 void
iolooper_reset(IoLooper * iol)55 iolooper_reset( IoLooper*  iol )
56 {
57     FD_ZERO(iol->reads);
58     FD_ZERO(iol->writes);
59     iol->max_fd = -1;
60     iol->max_fd_valid = 1;
61 }
62 
63 static void
iolooper_add_fd(IoLooper * iol,int fd)64 iolooper_add_fd( IoLooper*  iol, int fd )
65 {
66     if (iol->max_fd_valid && fd > iol->max_fd) {
67         iol->max_fd = fd;
68     }
69 }
70 
71 static void
iolooper_del_fd(IoLooper * iol,int fd)72 iolooper_del_fd( IoLooper*  iol, int fd )
73 {
74     if (iol->max_fd_valid && fd == iol->max_fd)
75         iol->max_fd_valid = 0;
76 }
77 
78 void
iolooper_modify(IoLooper * iol,int fd,int oldflags,int newflags)79 iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags )
80 {
81     if (fd < 0)
82         return;
83 
84     int changed = oldflags ^ newflags;
85 
86     if ((changed & IOLOOPER_READ) != 0) {
87         if ((newflags & IOLOOPER_READ) != 0)
88             iolooper_add_read(iol, fd);
89         else
90             iolooper_del_read(iol, fd);
91     }
92     if ((changed & IOLOOPER_WRITE) != 0) {
93         if ((newflags & IOLOOPER_WRITE) != 0)
94             iolooper_add_write(iol, fd);
95         else
96             iolooper_del_write(iol, fd);
97     }
98 }
99 
100 
101 static int
iolooper_fd_count(IoLooper * iol)102 iolooper_fd_count( IoLooper*  iol )
103 {
104     int  max_fd = iol->max_fd;
105     int  fd;
106 
107     if (iol->max_fd_valid)
108         return max_fd + 1;
109 
110     /* recompute max fd */
111     for (fd = 0; fd < FD_SETSIZE; fd++) {
112         if (!FD_ISSET(fd, iol->reads) && !FD_ISSET(fd, iol->writes))
113             continue;
114 
115         max_fd = fd;
116     }
117     iol->max_fd       = max_fd;
118     iol->max_fd_valid = 1;
119 
120     return max_fd + 1;
121 }
122 
123 void
iolooper_add_read(IoLooper * iol,int fd)124 iolooper_add_read( IoLooper*  iol, int  fd )
125 {
126     if (fd >= 0) {
127         iolooper_add_fd(iol, fd);
128         FD_SET(fd, iol->reads);
129     }
130 }
131 
132 void
iolooper_add_write(IoLooper * iol,int fd)133 iolooper_add_write( IoLooper*  iol, int  fd )
134 {
135     if (fd >= 0) {
136         iolooper_add_fd(iol, fd);
137         FD_SET(fd, iol->writes);
138     }
139 }
140 
141 void
iolooper_del_read(IoLooper * iol,int fd)142 iolooper_del_read( IoLooper*  iol, int  fd )
143 {
144     if (fd >= 0) {
145         iolooper_del_fd(iol, fd);
146         FD_CLR(fd, iol->reads);
147     }
148 }
149 
150 void
iolooper_del_write(IoLooper * iol,int fd)151 iolooper_del_write( IoLooper*  iol, int  fd )
152 {
153     if (fd >= 0) {
154         iolooper_del_fd(iol, fd);
155         FD_CLR(fd, iol->writes);
156     }
157 }
158 
159 int
iolooper_poll(IoLooper * iol)160 iolooper_poll( IoLooper*  iol )
161 {
162     int     count = iolooper_fd_count(iol);
163     int     ret;
164     fd_set  errs;
165 
166     if (count == 0)
167         return 0;
168 
169     FD_ZERO(&errs);
170 
171     do {
172         struct timeval  tv;
173 
174         tv.tv_sec = tv.tv_usec = 0;
175 
176         iol->reads_result[0]  = iol->reads[0];
177         iol->writes_result[0] = iol->writes[0];
178 
179         ret = select( count, iol->reads_result, iol->writes_result, &errs, &tv);
180     } while (ret < 0 && errno == EINTR);
181 
182     return ret;
183 }
184 
185 int
iolooper_wait(IoLooper * iol,int64_t duration)186 iolooper_wait( IoLooper*  iol, int64_t  duration )
187 {
188     int     count = iolooper_fd_count(iol);
189     int     ret;
190     fd_set  errs;
191     struct timeval tm0, *tm = NULL;
192 
193     if (count == 0)
194         return 0;
195 
196     if (duration < 0)
197         tm = NULL;
198     else {
199         tm = &tm0;
200         tm->tv_sec  = duration / 1000;
201         tm->tv_usec = (duration - 1000*tm->tv_sec) * 1000;
202     }
203 
204     FD_ZERO(&errs);
205 
206     do {
207         iol->reads_result[0]  = iol->reads[0];
208         iol->writes_result[0] = iol->writes[0];
209 
210         ret = select( count, iol->reads_result, iol->writes_result, &errs, tm);
211         if (ret == 0) {
212             // Indicates timeout
213             errno = ETIMEDOUT;
214         }
215     } while (ret < 0 && errno == EINTR);
216 
217     return ret;
218 }
219 
220 
221 int
iolooper_is_read(IoLooper * iol,int fd)222 iolooper_is_read( IoLooper*  iol, int  fd )
223 {
224     return FD_ISSET(fd, iol->reads_result);
225 }
226 
227 int
iolooper_is_write(IoLooper * iol,int fd)228 iolooper_is_write( IoLooper*  iol, int  fd )
229 {
230     return FD_ISSET(fd, iol->writes_result);
231 }
232 
233 int
iolooper_has_operations(IoLooper * iol)234 iolooper_has_operations( IoLooper* iol )
235 {
236     return iolooper_fd_count(iol) > 0;
237 }
238 
239 int64_t
iolooper_now(void)240 iolooper_now(void)
241 {
242 #ifdef _WIN32
243     FILETIME now;
244     int64_t now_100ns;
245 
246     GetSystemTimeAsFileTime(&now);
247 
248     /* Get the time as hundreds of nanosecond intervals since
249        12:00 AM January 1t 1601 UTC. We don't really need
250        to compute the value relative to the Posix epoch */
251     now_100ns = ((int64_t)now.dwHighDateTime << 32) | now.dwLowDateTime;
252 
253     /* 100 ns == 0.1 us == 0.0001 ms */
254     return now_100ns / 10000LL;
255 
256 #else /* !_WIN32 */
257     struct timeval time_now;
258     return gettimeofday(&time_now, NULL) ? -1 : (int64_t)time_now.tv_sec * 1000LL +
259                                                 time_now.tv_usec / 1000;
260 #endif /* !_WIN32 */
261 }
262 
263 int
iolooper_wait_absolute(IoLooper * iol,int64_t deadline)264 iolooper_wait_absolute(IoLooper* iol, int64_t deadline)
265 {
266     int64_t timeout = deadline - iolooper_now();
267 
268     /* If the deadline has passed, set the timeout to 0, this allows us
269      * to poll the file descriptor nonetheless */
270     if (timeout < 0)
271         timeout = 0;
272 
273     return iolooper_wait(iol, timeout);
274 }
275