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