-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathev_select.c
More file actions
300 lines (253 loc) · 7.79 KB
/
Copy pathev_select.c
File metadata and controls
300 lines (253 loc) · 7.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
* libev select fd activity backend
*
* SPDX-License-Identifier: MIT
* Copyright (c) 2011-2026 qb - isndev (cpp.actor).
*
* Part of qev, a modernized cross-platform fork of libev.
* Based on libev by Marc Alexander Lehmann <libev@schmorp.de>.
* Upstream: http://software.schmorp.de/pkg/libev.html
*
* Released under the MIT License (see LICENSE). Portions derived from libev
* remain Copyright (c) Marc Alexander Lehmann under the BSD-2-Clause license;
* see THIRD-PARTY-NOTICES.
*/
/*
* qb-io: bounded EINTR retry around select() (re-copies fdsets and restores tv);
* same timeout-not-recomputed tradeoff as kqueue EINTR cap.
*/
#ifndef _WIN32
/* for unix systems */
#include <inttypes.h>
#ifndef __hpux
/* for REAL unix systems */
#include <sys/select.h>
#endif
#endif
#ifndef EV_SELECT_USE_FD_SET
#ifdef NFDBITS
#define EV_SELECT_USE_FD_SET 0
#else
#define EV_SELECT_USE_FD_SET 1
#endif
#endif
#if EV_SELECT_IS_WINSOCKET
#undef EV_SELECT_USE_FD_SET
#define EV_SELECT_USE_FD_SET 1
#undef NFDBITS
#define NFDBITS 0
#endif
#if !EV_SELECT_USE_FD_SET
#define NFDBYTES (NFDBITS / 8)
#endif
#include <string.h>
static void
select_modify(EV_P_ int fd, int oev, int nev) {
if (oev == nev)
return;
{
#if EV_SELECT_USE_FD_SET
#if EV_SELECT_IS_WINSOCKET
SOCKET handle = anfds[fd].handle;
#else
int handle = fd;
#endif
EV_ASSERT_MSG(fd < FD_SETSIZE, "libev: fd >= FD_SETSIZE passed to fd_set-based select backend");
/* FD_SET is broken on windows (it adds the fd to a set twice or more,
* which eventually leads to overflows). Need to call it only on changes.
*/
#if EV_SELECT_IS_WINSOCKET
if ((oev ^ nev) & EV_READ)
#endif
if (nev & EV_READ)
FD_SET(handle, (fd_set *) vec_ri);
else
FD_CLR(handle, (fd_set *) vec_ri);
#if EV_SELECT_IS_WINSOCKET
if ((oev ^ nev) & EV_WRITE)
#endif
if (nev & EV_WRITE)
FD_SET(handle, (fd_set *) vec_wi);
else
FD_CLR(handle, (fd_set *) vec_wi);
#else
int word = fd / NFDBITS;
fd_mask mask = 1UL << (fd % NFDBITS);
if (ecb_expect_false(vec_max <= word)) {
int new_max = word + 1;
vec_ri = ev_realloc(vec_ri, new_max * NFDBYTES);
vec_ro = ev_realloc(vec_ro, new_max * NFDBYTES); /* could free/malloc */
vec_wi = ev_realloc(vec_wi, new_max * NFDBYTES);
vec_wo = ev_realloc(vec_wo, new_max * NFDBYTES); /* could free/malloc */
#ifdef _WIN32
vec_eo = ev_realloc(vec_eo, new_max * NFDBYTES); /* could free/malloc */
#endif
for (; vec_max < new_max; ++vec_max)
((fd_mask *) vec_ri)[vec_max] = ((fd_mask *) vec_wi)[vec_max] = 0;
}
((fd_mask *) vec_ri)[word] |= mask;
if (!(nev & EV_READ))
((fd_mask *) vec_ri)[word] &= ~mask;
((fd_mask *) vec_wi)[word] |= mask;
if (!(nev & EV_WRITE))
((fd_mask *) vec_wi)[word] &= ~mask;
#endif
}
}
static void
select_poll(EV_P_ ev_tstamp timeout) {
struct timeval tv;
int res;
int fd_setsize;
int eintr;
#if EV_SELECT_USE_FD_SET
fd_setsize = sizeof(fd_set);
#else
fd_setsize = vec_max * NFDBYTES;
#endif
for (eintr = 0;; ++eintr) {
EV_RELEASE_CB;
EV_TV_SET(tv, timeout);
memcpy(vec_ro, vec_ri, fd_setsize);
memcpy(vec_wo, vec_wi, fd_setsize);
#ifdef _WIN32
/* pass in the write set as except set.
* the idea behind this is to work around a windows bug that causes
* errors to be reported as an exception and not by setting
* the writable bit. this is so uncontrollably lame.
*/
memcpy(vec_eo, vec_wi, fd_setsize);
res = select(vec_max * NFDBITS, (fd_set *) vec_ro, (fd_set *) vec_wo, (fd_set *) vec_eo, &tv);
#elif EV_SELECT_USE_FD_SET
{
int select_nfds = anfdmax < FD_SETSIZE ? anfdmax : FD_SETSIZE;
res = select(select_nfds, (fd_set *) vec_ro, (fd_set *) vec_wo, 0, &tv);
}
#else
res = select(vec_max * NFDBITS, (fd_set *) vec_ro, (fd_set *) vec_wo, 0, &tv);
#endif
EV_ACQUIRE_CB;
if (ecb_expect_true(res >= 0))
break;
#if EV_SELECT_IS_WINSOCKET
errno = WSAGetLastError();
#endif
#ifdef WSABASEERR
/* on windows, select returns incompatible error codes, fix this */
if (errno >= WSABASEERR && errno < WSABASEERR + 1000)
if (errno == WSAENOTSOCK)
errno = EBADF;
else
errno -= WSABASEERR;
#endif
#ifdef _WIN32
/* select on windows erroneously returns EINVAL when no fd sets have been
* provided (this is documented). what microsoft doesn't tell you that this bug
* exists even when the fd sets _are_ provided, so we have to check for this bug
* here and emulate by sleeping manually.
* we also get EINVAL when the timeout is invalid, but we ignore this case here
* and assume that EINVAL always means: you have to wait manually.
*/
if (errno == EINVAL) {
if (timeout) {
unsigned long ms = EV_TS_TO_MSEC(timeout);
Sleep(ms ? ms : 1);
}
return;
}
#endif
if (errno == EBADF)
fd_ebadf(EV_A);
else if (errno == ENOMEM && !syserr_cb)
fd_enomem(EV_A);
else if (errno == EINTR) {
if (ecb_expect_false(eintr >= 255))
return;
continue;
} else
ev_syserr("(libev) select");
return;
}
#if EV_SELECT_USE_FD_SET
{
int fd;
for (fd = 0; fd < anfdmax; ++fd)
if (anfds[fd].events) {
int events = 0;
#if EV_SELECT_IS_WINSOCKET
SOCKET handle = anfds[fd].handle;
#else
int handle = fd;
#endif
if (FD_ISSET(handle, (fd_set *) vec_ro))
events |= EV_READ;
if (FD_ISSET(handle, (fd_set *) vec_wo))
events |= EV_WRITE;
#ifdef _WIN32
if (FD_ISSET(handle, (fd_set *) vec_eo))
events |= EV_WRITE;
#endif
if (ecb_expect_true(events))
fd_event(EV_A_ fd, events);
}
}
#else
{
int word, bit;
for (word = vec_max; word--;) {
fd_mask word_r = ((fd_mask *) vec_ro)[word];
fd_mask word_w = ((fd_mask *) vec_wo)[word];
#ifdef _WIN32
word_w |= ((fd_mask *) vec_eo)[word];
#endif
if (word_r || word_w)
for (bit = NFDBITS; bit--;) {
fd_mask mask = 1UL << bit;
int events = 0;
events |= word_r & mask ? EV_READ : 0;
events |= word_w & mask ? EV_WRITE : 0;
if (ecb_expect_true(events))
fd_event(EV_A_ word * NFDBITS + bit, events);
}
}
}
#endif
}
inline_size int
select_init(EV_P_ int flags) {
(void) flags;
backend_mintime = EV_TS_CONST(1e-6);
backend_modify = select_modify;
backend_poll = select_poll;
#if EV_SELECT_USE_FD_SET
vec_ri = ev_malloc(sizeof(fd_set));
FD_ZERO((fd_set *) vec_ri);
vec_ro = ev_malloc(sizeof(fd_set));
vec_wi = ev_malloc(sizeof(fd_set));
FD_ZERO((fd_set *) vec_wi);
vec_wo = ev_malloc(sizeof(fd_set));
#ifdef _WIN32
vec_eo = ev_malloc(sizeof(fd_set));
#endif
#else
vec_max = 0;
vec_ri = 0;
vec_ro = 0;
vec_wi = 0;
vec_wo = 0;
#ifdef _WIN32
vec_eo = 0;
#endif
#endif
return EVBACKEND_SELECT;
}
inline_size void
select_destroy(EV_P) {
ev_free(vec_ri);
ev_free(vec_ro);
ev_free(vec_wi);
ev_free(vec_wo);
#ifdef _WIN32
ev_free(vec_eo);
#endif
}