Coverage Report

Created: 2025-07-11 06:22

/src/libevent/select.c
Line
Count
Source (jump to first uncovered line)
1
/*  $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
2
3
/*
4
 * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
5
 * Copyright 2007-2012 Niels Provos and Nick Mathewson
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 * 3. The name of the author may not be used to endorse or promote products
16
 *    derived from this software without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
#include "event2/event-config.h"
30
#include "evconfig-private.h"
31
32
#ifdef EVENT__HAVE_SELECT
33
34
#ifdef __APPLE__
35
/* Apple wants us to define this if we might ever pass more than
36
 * FD_SETSIZE bits to select(). */
37
#define _DARWIN_UNLIMITED_SELECT
38
#endif
39
40
#include <sys/types.h>
41
#ifdef EVENT__HAVE_SYS_TIME_H
42
#include <sys/time.h>
43
#endif
44
#ifdef EVENT__HAVE_SYS_SELECT_H
45
#include <sys/select.h>
46
#endif
47
#include <sys/queue.h>
48
#include <signal.h>
49
#include <stdio.h>
50
#include <stdlib.h>
51
#include <string.h>
52
#include <unistd.h>
53
#include <errno.h>
54
55
#include "event-internal.h"
56
#include "evsignal-internal.h"
57
#include "event2/thread.h"
58
#include "evthread-internal.h"
59
#include "log-internal.h"
60
#include "evmap-internal.h"
61
62
#ifndef EVENT__HAVE_FD_MASK
63
/* This type is mandatory, but Android doesn't define it. */
64
typedef unsigned long fd_mask;
65
#endif
66
67
#ifndef NFDBITS
68
#define NFDBITS (sizeof(fd_mask)*8)
69
#endif
70
71
/* Divide positive x by y, rounding up. */
72
0
#define DIV_ROUNDUP(x, y)   (((x)+((y)-1))/(y))
73
74
/* How many bytes to allocate for N fds? */
75
#define SELECT_ALLOC_SIZE(n) \
76
0
  (DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask))
77
78
struct selectop {
79
  int event_fds;    /* Highest fd in fd set */
80
  int event_fdsz;
81
  int resize_out_sets;
82
  fd_set *event_readset_in;
83
  fd_set *event_writeset_in;
84
  fd_set *event_readset_out;
85
  fd_set *event_writeset_out;
86
};
87
88
static void *select_init(struct event_base *);
89
static int select_add(struct event_base *, int, short old, short events, void*);
90
static int select_del(struct event_base *, int, short old, short events, void*);
91
static int select_dispatch(struct event_base *, struct timeval *);
92
static void select_dealloc(struct event_base *);
93
94
const struct eventop selectops = {
95
  "select",
96
  select_init,
97
  select_add,
98
  select_del,
99
  select_dispatch,
100
  select_dealloc,
101
  1, /* need_reinit. */
102
  EV_FEATURE_FDS,
103
  0,
104
};
105
106
static int select_resize(struct selectop *sop, int fdsz);
107
static void select_free_selectop(struct selectop *sop);
108
109
static void *
110
select_init(struct event_base *base)
111
0
{
112
0
  struct selectop *sop;
113
114
0
  if (!(sop = mm_calloc(1, sizeof(struct selectop))))
115
0
    return (NULL);
116
117
0
  if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {
118
0
    select_free_selectop(sop);
119
0
    return (NULL);
120
0
  }
121
122
0
  if (sigfd_init_(base) < 0)
123
0
    evsig_init_(base);
124
125
0
  evutil_weakrand_seed_(&base->weakrand_seed, 0);
126
127
0
  return (sop);
128
0
}
129
130
#ifdef CHECK_INVARIANTS
131
static void
132
check_selectop(struct selectop *sop)
133
{
134
  /* nothing to be done here */
135
}
136
#else
137
0
#define check_selectop(sop) do { (void) sop; } while (0)
138
#endif
139
140
static int
141
select_dispatch(struct event_base *base, struct timeval *tv)
142
0
{
143
0
  int res=0, i, j, nfds;
144
0
  struct selectop *sop = base->evbase;
145
146
0
  check_selectop(sop);
147
0
  if (sop->resize_out_sets) {
148
0
    fd_set *readset_out=NULL, *writeset_out=NULL;
149
0
    size_t sz = sop->event_fdsz;
150
0
    if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))
151
0
      return (-1);
152
0
    sop->event_readset_out = readset_out;
153
0
    if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {
154
      /* We don't free readset_out here, since it was
155
       * already successfully reallocated. The next time
156
       * we call select_dispatch, the realloc will be a
157
       * no-op. */
158
0
      return (-1);
159
0
    }
160
0
    sop->event_writeset_out = writeset_out;
161
0
    sop->resize_out_sets = 0;
162
0
  }
163
164
0
  memcpy(sop->event_readset_out, sop->event_readset_in,
165
0
         sop->event_fdsz);
166
0
  memcpy(sop->event_writeset_out, sop->event_writeset_in,
167
0
         sop->event_fdsz);
168
169
0
  nfds = sop->event_fds+1;
170
171
0
  EVBASE_RELEASE_LOCK(base, th_base_lock);
172
173
0
  res = select(nfds, sop->event_readset_out,
174
0
      sop->event_writeset_out, NULL, tv);
175
176
0
  EVBASE_ACQUIRE_LOCK(base, th_base_lock);
177
178
0
  check_selectop(sop);
179
180
0
  if (res == -1) {
181
0
    if (errno != EINTR) {
182
0
      event_warn("select");
183
0
      return (-1);
184
0
    }
185
186
0
    return (0);
187
0
  }
188
189
0
  event_debug(("%s: select reports %d", __func__, res));
190
191
0
  check_selectop(sop);
192
0
  i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
193
0
  for (j = 0; j < nfds; ++j) {
194
0
    if (++i >= nfds)
195
0
      i = 0;
196
0
    res = 0;
197
0
    if (FD_ISSET(i, sop->event_readset_out))
198
0
      res |= EV_READ;
199
0
    if (FD_ISSET(i, sop->event_writeset_out))
200
0
      res |= EV_WRITE;
201
202
0
    if (res == 0)
203
0
      continue;
204
205
0
    evmap_io_active_(base, i, res);
206
0
  }
207
0
  check_selectop(sop);
208
209
0
  return (0);
210
0
}
211
212
static int
213
select_resize(struct selectop *sop, int fdsz)
214
0
{
215
0
  fd_set *readset_in = NULL;
216
0
  fd_set *writeset_in = NULL;
217
218
0
  if (sop->event_readset_in)
219
0
    check_selectop(sop);
220
221
0
  if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL)
222
0
    goto error;
223
0
  sop->event_readset_in = readset_in;
224
0
  if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) {
225
    /* Note that this will leave event_readset_in expanded.
226
     * That's okay; we wouldn't want to free it, since that would
227
     * change the semantics of select_resize from "expand the
228
     * readset_in and writeset_in, or return -1" to "expand the
229
     * *set_in members, or trash them and return -1."
230
     */
231
0
    goto error;
232
0
  }
233
0
  sop->event_writeset_in = writeset_in;
234
0
  sop->resize_out_sets = 1;
235
236
0
  memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
237
0
      fdsz - sop->event_fdsz);
238
0
  memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
239
0
      fdsz - sop->event_fdsz);
240
241
0
  sop->event_fdsz = fdsz;
242
0
  check_selectop(sop);
243
244
0
  return (0);
245
246
0
 error:
247
0
  event_warn("malloc");
248
0
  return (-1);
249
0
}
250
251
252
static int
253
select_add(struct event_base *base, int fd, short old, short events, void *p)
254
0
{
255
0
  struct selectop *sop = base->evbase;
256
0
  (void) p;
257
258
0
  EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
259
0
  check_selectop(sop);
260
  /*
261
   * Keep track of the highest fd, so that we can calculate the size
262
   * of the fd_sets for select(2)
263
   */
264
0
  if (sop->event_fds < fd) {
265
0
    int fdsz = sop->event_fdsz;
266
267
0
    if (fdsz < (int)sizeof(fd_mask))
268
0
      fdsz = (int)sizeof(fd_mask);
269
270
    /* In theory we should worry about overflow here.  In
271
     * reality, though, the highest fd on a unixy system will
272
     * not overflow here. XXXX */
273
0
    while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))
274
0
      fdsz *= 2;
275
276
0
    if (fdsz != sop->event_fdsz) {
277
0
      if (select_resize(sop, fdsz)) {
278
0
        check_selectop(sop);
279
0
        return (-1);
280
0
      }
281
0
    }
282
283
0
    sop->event_fds = fd;
284
0
  }
285
286
0
  if (events & EV_READ)
287
0
    FD_SET(fd, sop->event_readset_in);
288
0
  if (events & EV_WRITE)
289
0
    FD_SET(fd, sop->event_writeset_in);
290
0
  check_selectop(sop);
291
292
0
  return (0);
293
0
}
294
295
/*
296
 * Nothing to be done here.
297
 */
298
299
static int
300
select_del(struct event_base *base, int fd, short old, short events, void *p)
301
0
{
302
0
  struct selectop *sop = base->evbase;
303
0
  (void)p;
304
305
0
  EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
306
0
  check_selectop(sop);
307
308
0
  if (sop->event_fds < fd) {
309
0
    check_selectop(sop);
310
0
    return (0);
311
0
  }
312
313
0
  if (events & EV_READ)
314
0
    FD_CLR(fd, sop->event_readset_in);
315
316
0
  if (events & EV_WRITE)
317
0
    FD_CLR(fd, sop->event_writeset_in);
318
319
0
  check_selectop(sop);
320
0
  return (0);
321
0
}
322
323
static void
324
select_free_selectop(struct selectop *sop)
325
0
{
326
0
  if (sop->event_readset_in)
327
0
    mm_free(sop->event_readset_in);
328
0
  if (sop->event_writeset_in)
329
0
    mm_free(sop->event_writeset_in);
330
0
  if (sop->event_readset_out)
331
0
    mm_free(sop->event_readset_out);
332
0
  if (sop->event_writeset_out)
333
0
    mm_free(sop->event_writeset_out);
334
335
0
  memset(sop, 0, sizeof(struct selectop));
336
0
  mm_free(sop);
337
0
}
338
339
static void
340
select_dealloc(struct event_base *base)
341
0
{
342
0
  evsig_dealloc_(base);
343
344
0
  select_free_selectop(base->evbase);
345
0
}
346
347
#endif /* EVENT__HAVE_SELECT */