1 /* $NetBSD: evport.c,v 1.5 2020/05/25 20:47:33 christos Exp $ */
2
3 /*
4 * Submitted by David Pacheco (dp.spambait@gmail.com)
5 *
6 * Copyright 2006-2007 Niels Provos
7 * Copyright 2007-2012 Niels Provos and Nick Mathewson
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Copyright (c) 2007 Sun Microsystems. All rights reserved.
34 * Use is subject to license terms.
35 */
36
37 /*
38 * evport.c: event backend using Solaris 10 event ports. See port_create(3C).
39 * This implementation is loosely modeled after the one used for select(2) (in
40 * select.c).
41 *
42 * The outstanding events are tracked in a data structure called evport_data.
43 * Each entry in the ed_fds array corresponds to a file descriptor, and contains
44 * pointers to the read and write events that correspond to that fd. (That is,
45 * when the file is readable, the "read" event should handle it, etc.)
46 *
47 * evport_add and evport_del update this data structure. evport_dispatch uses it
48 * to determine where to callback when an event occurs (which it gets from
49 * port_getn).
50 *
51 * Helper functions are used: grow() grows the file descriptor array as
52 * necessary when large fd's come in. reassociate() takes care of maintaining
53 * the proper file-descriptor/event-port associations.
54 *
55 * As in the select(2) implementation, signals are handled by evsignal.
56 */
57
58 #include "event2/event-config.h"
59 #include "evconfig-private.h"
60
61 #ifdef EVENT__HAVE_EVENT_PORTS
62
63 #include <sys/time.h>
64 #include <sys/queue.h>
65 #include <errno.h>
66 #include <poll.h>
67 #include <port.h>
68 #include <signal.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <time.h>
73 #include <unistd.h>
74
75 #include "event2/thread.h"
76
77 #include "evthread-internal.h"
78 #include "event-internal.h"
79 #include "log-internal.h"
80 #include "evsignal-internal.h"
81 #include "evmap-internal.h"
82
83 #define INITIAL_EVENTS_PER_GETN 8
84 #define MAX_EVENTS_PER_GETN 4096
85
86 /*
87 * Per-file-descriptor information about what events we're subscribed to. These
88 * fields are NULL if no event is subscribed to either of them.
89 */
90
91 struct fd_info {
92 /* combinations of EV_READ and EV_WRITE */
93 short fdi_what;
94 /* Index of this fd within ed_pending, plus 1. Zero if this fd is
95 * not in ed_pending. (The +1 is a hack so that memset(0) will set
96 * it to a nil index. */
97 int pending_idx_plus_1;
98 };
99
100 #define FDI_HAS_READ(fdi) ((fdi)->fdi_what & EV_READ)
101 #define FDI_HAS_WRITE(fdi) ((fdi)->fdi_what & EV_WRITE)
102 #define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi))
103 #define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \
104 (FDI_HAS_WRITE(fdi) ? POLLOUT : 0)
105
106 struct evport_data {
107 int ed_port; /* event port for system events */
108 /* How many elements of ed_pending should we look at? */
109 int ed_npending;
110 /* How many elements are allocated in ed_pending and pevtlist? */
111 int ed_maxevents;
112 /* fdi's that we need to reassoc */
113 int *ed_pending;
114 /* storage space for incoming events. */
115 port_event_t *ed_pevtlist;
116
117 };
118
119 static void* evport_init(struct event_base *);
120 static int evport_add(struct event_base *, int fd, short old, short events, void *);
121 static int evport_del(struct event_base *, int fd, short old, short events, void *);
122 static int evport_dispatch(struct event_base *, struct timeval *);
123 static void evport_dealloc(struct event_base *);
124 static int grow(struct evport_data *, int min_events);
125
126 const struct eventop evportops = {
127 "evport",
128 evport_init,
129 evport_add,
130 evport_del,
131 evport_dispatch,
132 evport_dealloc,
133 1, /* need reinit */
134 0, /* features */
135 sizeof(struct fd_info), /* fdinfo length */
136 };
137
138 /*
139 * Initialize the event port implementation.
140 */
141
142 static void*
evport_init(struct event_base * base)143 evport_init(struct event_base *base)
144 {
145 struct evport_data *evpd;
146
147 if (!(evpd = mm_calloc(1, sizeof(struct evport_data))))
148 return (NULL);
149
150 if ((evpd->ed_port = port_create()) == -1) {
151 mm_free(evpd);
152 return (NULL);
153 }
154
155 if (grow(evpd, INITIAL_EVENTS_PER_GETN) < 0) {
156 close(evpd->ed_port);
157 mm_free(evpd);
158 return NULL;
159 }
160
161 evpd->ed_npending = 0;
162
163 evsig_init_(base);
164
165 return (evpd);
166 }
167
168 static int
grow(struct evport_data * data,int min_events)169 grow(struct evport_data *data, int min_events)
170 {
171 int newsize;
172 int *new_pending;
173 port_event_t *new_pevtlist;
174 if (data->ed_maxevents) {
175 newsize = data->ed_maxevents;
176 do {
177 newsize *= 2;
178 } while (newsize < min_events);
179 } else {
180 newsize = min_events;
181 }
182
183 new_pending = mm_realloc(data->ed_pending, sizeof(int)*newsize);
184 if (new_pending == NULL)
185 return -1;
186 data->ed_pending = new_pending;
187 new_pevtlist = mm_realloc(data->ed_pevtlist, sizeof(port_event_t)*newsize);
188 if (new_pevtlist == NULL)
189 return -1;
190 data->ed_pevtlist = new_pevtlist;
191
192 data->ed_maxevents = newsize;
193 return 0;
194 }
195
196 #ifdef CHECK_INVARIANTS
197 /*
198 * Checks some basic properties about the evport_data structure. Because it
199 * checks all file descriptors, this function can be expensive when the maximum
200 * file descriptor ever used is rather large.
201 */
202
203 static void
check_evportop(struct evport_data * evpd)204 check_evportop(struct evport_data *evpd)
205 {
206 EVUTIL_ASSERT(evpd);
207 EVUTIL_ASSERT(evpd->ed_port > 0);
208 }
209
210 /*
211 * Verifies very basic integrity of a given port_event.
212 */
213 static void
check_event(port_event_t * pevt)214 check_event(port_event_t* pevt)
215 {
216 /*
217 * We've only registered for PORT_SOURCE_FD events. The only
218 * other thing we can legitimately receive is PORT_SOURCE_ALERT,
219 * but since we're not using port_alert either, we can assume
220 * PORT_SOURCE_FD.
221 */
222 EVUTIL_ASSERT(pevt->portev_source == PORT_SOURCE_FD);
223 }
224
225 #else
226 #define check_evportop(epop)
227 #define check_event(pevt)
228 #endif /* CHECK_INVARIANTS */
229
230 /*
231 * (Re)associates the given file descriptor with the event port. The OS events
232 * are specified (implicitly) from the fd_info struct.
233 */
234 static int
reassociate(struct evport_data * epdp,struct fd_info * fdip,int fd)235 reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd)
236 {
237 int sysevents = FDI_TO_SYSEVENTS(fdip);
238
239 if (sysevents != 0) {
240 if (port_associate(epdp->ed_port, PORT_SOURCE_FD,
241 fd, sysevents, fdip) == -1) {
242 event_warn("port_associate");
243 return (-1);
244 }
245 }
246
247 check_evportop(epdp);
248
249 return (0);
250 }
251
252 /*
253 * Main event loop - polls port_getn for some number of events, and processes
254 * them.
255 */
256
257 static int
evport_dispatch(struct event_base * base,struct timeval * tv)258 evport_dispatch(struct event_base *base, struct timeval *tv)
259 {
260 int i, res;
261 struct evport_data *epdp = base->evbase;
262 port_event_t *pevtlist = epdp->ed_pevtlist;
263
264 /*
265 * port_getn will block until it has at least nevents events. It will
266 * also return how many it's given us (which may be more than we asked
267 * for, as long as it's less than our maximum (ed_maxevents)) in
268 * nevents.
269 */
270 int nevents = 1;
271
272 /*
273 * We have to convert a struct timeval to a struct timespec
274 * (only difference is nanoseconds vs. microseconds). If no time-based
275 * events are active, we should wait for I/O (and tv == NULL).
276 */
277 struct timespec ts;
278 struct timespec *ts_p = NULL;
279 if (tv != NULL) {
280 ts.tv_sec = tv->tv_sec;
281 ts.tv_nsec = tv->tv_usec * 1000;
282 ts_p = &ts;
283 }
284
285 /*
286 * Before doing anything else, we need to reassociate the events we hit
287 * last time which need reassociation. See comment at the end of the
288 * loop below.
289 */
290 for (i = 0; i < epdp->ed_npending; ++i) {
291 struct fd_info *fdi = NULL;
292 const int fd = epdp->ed_pending[i];
293 if (fd != -1) {
294 /* We might have cleared out this event; we need
295 * to be sure that it's still set. */
296 fdi = evmap_io_get_fdinfo_(&base->io, fd);
297 }
298
299 if (fdi != NULL && FDI_HAS_EVENTS(fdi)) {
300 reassociate(epdp, fdi, fd);
301 /* epdp->ed_pending[i] = -1; */
302 fdi->pending_idx_plus_1 = 0;
303 }
304 }
305
306 EVBASE_RELEASE_LOCK(base, th_base_lock);
307
308 res = port_getn(epdp->ed_port, pevtlist, epdp->ed_maxevents,
309 (unsigned int *) &nevents, ts_p);
310
311 EVBASE_ACQUIRE_LOCK(base, th_base_lock);
312
313 if (res == -1) {
314 if (errno == EINTR || errno == EAGAIN) {
315 return (0);
316 } else if (errno == ETIME) {
317 if (nevents == 0)
318 return (0);
319 } else {
320 event_warn("port_getn");
321 return (-1);
322 }
323 }
324
325 event_debug(("%s: port_getn reports %d events", __func__, nevents));
326
327 for (i = 0; i < nevents; ++i) {
328 port_event_t *pevt = &pevtlist[i];
329 int fd = (int) pevt->portev_object;
330 struct fd_info *fdi = pevt->portev_user;
331 /*EVUTIL_ASSERT(evmap_io_get_fdinfo_(&base->io, fd) == fdi);*/
332
333 check_evportop(epdp);
334 check_event(pevt);
335 epdp->ed_pending[i] = fd;
336 fdi->pending_idx_plus_1 = i + 1;
337
338 /*
339 * Figure out what kind of event it was
340 * (because we have to pass this to the callback)
341 */
342 res = 0;
343 if (pevt->portev_events & (POLLERR|POLLHUP)) {
344 res = EV_READ | EV_WRITE;
345 } else {
346 if (pevt->portev_events & POLLIN)
347 res |= EV_READ;
348 if (pevt->portev_events & POLLOUT)
349 res |= EV_WRITE;
350 }
351
352 /*
353 * Check for the error situations or a hangup situation
354 */
355 if (pevt->portev_events & (POLLERR|POLLHUP|POLLNVAL))
356 res |= EV_READ|EV_WRITE;
357
358 evmap_io_active_(base, fd, res);
359 } /* end of all events gotten */
360 epdp->ed_npending = nevents;
361
362 if (nevents == epdp->ed_maxevents &&
363 epdp->ed_maxevents < MAX_EVENTS_PER_GETN) {
364 /* we used all the space this time. We should be ready
365 * for more events next time around. */
366 grow(epdp, epdp->ed_maxevents * 2);
367 }
368
369 check_evportop(epdp);
370
371 return (0);
372 }
373
374
375 /*
376 * Adds the given event (so that you will be notified when it happens via
377 * the callback function).
378 */
379
380 static int
evport_add(struct event_base * base,int fd,short old,short events,void * p)381 evport_add(struct event_base *base, int fd, short old, short events, void *p)
382 {
383 struct evport_data *evpd = base->evbase;
384 struct fd_info *fdi = p;
385
386 check_evportop(evpd);
387
388 fdi->fdi_what |= events;
389
390 return reassociate(evpd, fdi, fd);
391 }
392
393 /*
394 * Removes the given event from the list of events to wait for.
395 */
396
397 static int
evport_del(struct event_base * base,int fd,short old,short events,void * p)398 evport_del(struct event_base *base, int fd, short old, short events, void *p)
399 {
400 struct evport_data *evpd = base->evbase;
401 struct fd_info *fdi = p;
402 int associated = ! fdi->pending_idx_plus_1;
403
404 check_evportop(evpd);
405
406 fdi->fdi_what &= ~(events &(EV_READ|EV_WRITE));
407
408 if (associated) {
409 if (!FDI_HAS_EVENTS(fdi) &&
410 port_dissociate(evpd->ed_port, PORT_SOURCE_FD, fd) == -1) {
411 /*
412 * Ignore EBADFD error the fd could have been closed
413 * before event_del() was called.
414 */
415 if (errno != EBADFD) {
416 event_warn("port_dissociate");
417 return (-1);
418 }
419 } else {
420 if (FDI_HAS_EVENTS(fdi)) {
421 return (reassociate(evpd, fdi, fd));
422 }
423 }
424 } else {
425 if ((fdi->fdi_what & (EV_READ|EV_WRITE)) == 0) {
426 const int i = fdi->pending_idx_plus_1 - 1;
427 EVUTIL_ASSERT(evpd->ed_pending[i] == fd);
428 evpd->ed_pending[i] = -1;
429 fdi->pending_idx_plus_1 = 0;
430 }
431 }
432 return 0;
433 }
434
435
436 static void
evport_dealloc(struct event_base * base)437 evport_dealloc(struct event_base *base)
438 {
439 struct evport_data *evpd = base->evbase;
440
441 evsig_dealloc_(base);
442
443 close(evpd->ed_port);
444
445 if (evpd->ed_pending)
446 mm_free(evpd->ed_pending);
447 if (evpd->ed_pevtlist)
448 mm_free(evpd->ed_pevtlist);
449
450 mm_free(evpd);
451 }
452
453 #endif /* EVENT__HAVE_EVENT_PORTS */
454