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
13struct 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
22IoLooper*
23iolooper_new(void)
24{
25    IoLooper*  iol = malloc(sizeof(*iol));
26    iolooper_reset(iol);
27    return iol;
28}
29
30void
31iolooper_free( IoLooper*  iol )
32{
33    free(iol);
34}
35
36void
37iolooper_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
45static void
46iolooper_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
53static void
54iolooper_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
60void
61iolooper_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
83static int
84iolooper_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
105void
106iolooper_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
114void
115iolooper_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
123void
124iolooper_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
132void
133iolooper_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
141int
142iolooper_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
167int
168iolooper_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
205int
206iolooper_is_read( IoLooper*  iol, int  fd )
207{
208    return FD_ISSET(fd, iol->reads_result);
209}
210
211int
212iolooper_is_write( IoLooper*  iol, int  fd )
213{
214    return FD_ISSET(fd, iol->writes_result);
215}
216
217int
218iolooper_has_operations( IoLooper* iol )
219{
220    return iolooper_fd_count(iol) > 0;
221}
222
223int64_t
224iolooper_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
231int
232iolooper_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