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