1 #include "iolooper.h"
2 #include "qemu-common.h"
3
4 /* An implementation of iolooper.h based on Unix select() */
5 #ifdef _WIN32
6 # include <winsock2.h>
7 #else
8 # include <sys/types.h>
9 # include <sys/select.h>
10 #endif
11 #include "sockets.h"
12
13 struct IoLooper {
14 fd_set reads[1];
15 fd_set writes[1];
16 fd_set reads_result[1];
17 fd_set writes_result[1];
18 int max_fd;
19 int max_fd_valid;
20 };
21
22 IoLooper*
iolooper_new(void)23 iolooper_new(void)
24 {
25 IoLooper* iol = malloc(sizeof(*iol));
26 iolooper_reset(iol);
27 return iol;
28 }
29
30 void
iolooper_free(IoLooper * iol)31 iolooper_free( IoLooper* iol )
32 {
33 free(iol);
34 }
35
36 void
iolooper_reset(IoLooper * iol)37 iolooper_reset( IoLooper* iol )
38 {
39 FD_ZERO(iol->reads);
40 FD_ZERO(iol->writes);
41 iol->max_fd = -1;
42 iol->max_fd_valid = 1;
43 }
44
45 static void
iolooper_add_fd(IoLooper * iol,int fd)46 iolooper_add_fd( IoLooper* iol, int fd )
47 {
48 if (iol->max_fd_valid && fd > iol->max_fd) {
49 iol->max_fd = fd;
50 }
51 }
52
53 static void
iolooper_del_fd(IoLooper * iol,int fd)54 iolooper_del_fd( IoLooper* iol, int fd )
55 {
56 if (iol->max_fd_valid && fd == iol->max_fd)
57 iol->max_fd_valid = 0;
58 }
59
60 void
iolooper_modify(IoLooper * iol,int fd,int oldflags,int newflags)61 iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags )
62 {
63 if (fd < 0)
64 return;
65
66 int changed = oldflags ^ newflags;
67
68 if ((changed & IOLOOPER_READ) != 0) {
69 if ((newflags & IOLOOPER_READ) != 0)
70 iolooper_add_read(iol, fd);
71 else
72 iolooper_del_read(iol, fd);
73 }
74 if ((changed & IOLOOPER_WRITE) != 0) {
75 if ((newflags & IOLOOPER_WRITE) != 0)
76 iolooper_add_write(iol, fd);
77 else
78 iolooper_del_write(iol, fd);
79 }
80 }
81
82
83 static int
iolooper_fd_count(IoLooper * iol)84 iolooper_fd_count( IoLooper* iol )
85 {
86 int max_fd = iol->max_fd;
87 int fd;
88
89 if (iol->max_fd_valid)
90 return max_fd + 1;
91
92 /* recompute max fd */
93 for (fd = 0; fd < FD_SETSIZE; fd++) {
94 if (!FD_ISSET(fd, iol->reads) && !FD_ISSET(fd, iol->writes))
95 continue;
96
97 max_fd = fd;
98 }
99 iol->max_fd = max_fd;
100 iol->max_fd_valid = 1;
101
102 return max_fd + 1;
103 }
104
105 void
iolooper_add_read(IoLooper * iol,int fd)106 iolooper_add_read( IoLooper* iol, int fd )
107 {
108 if (fd >= 0) {
109 iolooper_add_fd(iol, fd);
110 FD_SET(fd, iol->reads);
111 }
112 }
113
114 void
iolooper_add_write(IoLooper * iol,int fd)115 iolooper_add_write( IoLooper* iol, int fd )
116 {
117 if (fd >= 0) {
118 iolooper_add_fd(iol, fd);
119 FD_SET(fd, iol->writes);
120 }
121 }
122
123 void
iolooper_del_read(IoLooper * iol,int fd)124 iolooper_del_read( IoLooper* iol, int fd )
125 {
126 if (fd >= 0) {
127 iolooper_del_fd(iol, fd);
128 FD_CLR(fd, iol->reads);
129 }
130 }
131
132 void
iolooper_del_write(IoLooper * iol,int fd)133 iolooper_del_write( IoLooper* iol, int fd )
134 {
135 if (fd >= 0) {
136 iolooper_del_fd(iol, fd);
137 FD_CLR(fd, iol->writes);
138 }
139 }
140
141 int
iolooper_poll(IoLooper * iol)142 iolooper_poll( IoLooper* iol )
143 {
144 int count = iolooper_fd_count(iol);
145 int ret;
146 fd_set errs;
147
148 if (count == 0)
149 return 0;
150
151 FD_ZERO(&errs);
152
153 do {
154 struct timeval tv;
155
156 tv.tv_sec = tv.tv_usec = 0;
157
158 iol->reads_result[0] = iol->reads[0];
159 iol->writes_result[0] = iol->writes[0];
160
161 ret = select( count, iol->reads_result, iol->writes_result, &errs, &tv);
162 } while (ret < 0 && errno == EINTR);
163
164 return ret;
165 }
166
167 int
iolooper_wait(IoLooper * iol,int64_t duration)168 iolooper_wait( IoLooper* iol, int64_t duration )
169 {
170 int count = iolooper_fd_count(iol);
171 int ret;
172 fd_set errs;
173 struct timeval tm0, *tm = NULL;
174
175 if (count == 0)
176 return 0;
177
178 CLAMP_MAC_TIMEOUT(duration);
179
180 if (duration < 0)
181 tm = NULL;
182 else {
183 tm = &tm0;
184 tm->tv_sec = duration / 1000;
185 tm->tv_usec = (duration - 1000*tm->tv_sec) * 1000;
186 }
187
188 FD_ZERO(&errs);
189
190 do {
191 iol->reads_result[0] = iol->reads[0];
192 iol->writes_result[0] = iol->writes[0];
193
194 ret = select( count, iol->reads_result, iol->writes_result, &errs, tm);
195 if (ret == 0) {
196 // Indicates timeout
197 errno = ETIMEDOUT;
198 }
199 } while (ret < 0 && errno == EINTR);
200
201 return ret;
202 }
203
204
205 int
iolooper_is_read(IoLooper * iol,int fd)206 iolooper_is_read( IoLooper* iol, int fd )
207 {
208 return FD_ISSET(fd, iol->reads_result);
209 }
210
211 int
iolooper_is_write(IoLooper * iol,int fd)212 iolooper_is_write( IoLooper* iol, int fd )
213 {
214 return FD_ISSET(fd, iol->writes_result);
215 }
216
217 int
iolooper_has_operations(IoLooper * iol)218 iolooper_has_operations( IoLooper* iol )
219 {
220 return iolooper_fd_count(iol) > 0;
221 }
222
223 int64_t
iolooper_now(void)224 iolooper_now(void)
225 {
226 struct timeval time_now;
227 return gettimeofday(&time_now, NULL) ? -1 : (int64_t)time_now.tv_sec * 1000LL +
228 time_now.tv_usec / 1000;
229 }
230
231 int
iolooper_wait_absolute(IoLooper * iol,int64_t deadline)232 iolooper_wait_absolute(IoLooper* iol, int64_t deadline)
233 {
234 int64_t timeout = deadline - iolooper_now();
235
236 /* If the deadline has passed, set the timeout to 0, this allows us
237 * to poll the file descriptor nonetheless */
238 if (timeout < 0)
239 timeout = 0;
240
241 return iolooper_wait(iol, timeout);
242 }
243