1 /* $NetBSD: evport.c,v 1.1.1.1 2013/04/11 16:43:25 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.1 2013/04/11 16:43:25 christos Exp $"); 60 61 #include <sys/time.h> 62 #include <sys/queue.h> 63 #include <errno.h> 64 #include <poll.h> 65 #include <port.h> 66 #include <signal.h> 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <string.h> 70 #include <time.h> 71 #include <unistd.h> 72 73 #include "event2/thread.h" 74 75 #include "evthread-internal.h" 76 #include "event-internal.h" 77 #include "log-internal.h" 78 #include "evsignal-internal.h" 79 #include "evmap-internal.h" 80 81 /* 82 * Default value for ed_nevents, which is the maximum file descriptor number we 83 * can handle. If an event comes in for a file descriptor F > nevents, we will 84 * grow the array of file descriptors, doubling its size. 85 */ 86 #define DEFAULT_NFDS 16 87 88 89 /* 90 * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on 91 * any particular call. You can speed things up by increasing this, but it will 92 * (obviously) require more memory. 93 */ 94 #define EVENTS_PER_GETN 8 95 96 /* 97 * Per-file-descriptor information about what events we're subscribed to. These 98 * fields are NULL if no event is subscribed to either of them. 99 */ 100 101 struct fd_info { 102 short fdi_what; /* combinations of EV_READ and EV_WRITE */ 103 }; 104 105 #define FDI_HAS_READ(fdi) ((fdi)->fdi_what & EV_READ) 106 #define FDI_HAS_WRITE(fdi) ((fdi)->fdi_what & EV_WRITE) 107 #define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi)) 108 #define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \ 109 (FDI_HAS_WRITE(fdi) ? POLLOUT : 0) 110 111 struct evport_data { 112 int ed_port; /* event port for system events */ 113 int ed_nevents; /* number of allocated fdi's */ 114 struct fd_info *ed_fds; /* allocated fdi table */ 115 /* fdi's that we need to reassoc */ 116 int ed_pending[EVENTS_PER_GETN]; /* fd's with pending events */ 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 125 const struct eventop evportops = { 126 "evport", 127 evport_init, 128 evport_add, 129 evport_del, 130 evport_dispatch, 131 evport_dealloc, 132 1, /* need reinit */ 133 0, /* features */ 134 0, /* fdinfo length */ 135 }; 136 137 /* 138 * Initialize the event port implementation. 139 */ 140 141 static void* 142 evport_init(struct event_base *base) 143 { 144 struct evport_data *evpd; 145 int i; 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 /* 156 * Initialize file descriptor structure 157 */ 158 evpd->ed_fds = mm_calloc(DEFAULT_NFDS, sizeof(struct fd_info)); 159 if (evpd->ed_fds == NULL) { 160 close(evpd->ed_port); 161 mm_free(evpd); 162 return (NULL); 163 } 164 evpd->ed_nevents = DEFAULT_NFDS; 165 for (i = 0; i < EVENTS_PER_GETN; i++) 166 evpd->ed_pending[i] = -1; 167 168 evsig_init(base); 169 170 return (evpd); 171 } 172 173 #ifdef CHECK_INVARIANTS 174 /* 175 * Checks some basic properties about the evport_data structure. Because it 176 * checks all file descriptors, this function can be expensive when the maximum 177 * file descriptor ever used is rather large. 178 */ 179 180 static void 181 check_evportop(struct evport_data *evpd) 182 { 183 EVUTIL_ASSERT(evpd); 184 EVUTIL_ASSERT(evpd->ed_nevents > 0); 185 EVUTIL_ASSERT(evpd->ed_port > 0); 186 EVUTIL_ASSERT(evpd->ed_fds > 0); 187 } 188 189 /* 190 * Verifies very basic integrity of a given port_event. 191 */ 192 static void 193 check_event(port_event_t* pevt) 194 { 195 /* 196 * We've only registered for PORT_SOURCE_FD events. The only 197 * other thing we can legitimately receive is PORT_SOURCE_ALERT, 198 * but since we're not using port_alert either, we can assume 199 * PORT_SOURCE_FD. 200 */ 201 EVUTIL_ASSERT(pevt->portev_source == PORT_SOURCE_FD); 202 EVUTIL_ASSERT(pevt->portev_user == NULL); 203 } 204 205 #else 206 #define check_evportop(epop) 207 #define check_event(pevt) 208 #endif /* CHECK_INVARIANTS */ 209 210 /* 211 * Doubles the size of the allocated file descriptor array. 212 */ 213 static int 214 grow(struct evport_data *epdp, int factor) 215 { 216 struct fd_info *tmp; 217 int oldsize = epdp->ed_nevents; 218 int newsize = factor * oldsize; 219 EVUTIL_ASSERT(factor > 1); 220 221 check_evportop(epdp); 222 223 tmp = mm_realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize); 224 if (NULL == tmp) 225 return -1; 226 epdp->ed_fds = tmp; 227 memset((char*) (epdp->ed_fds + oldsize), 0, 228 (newsize - oldsize)*sizeof(struct fd_info)); 229 epdp->ed_nevents = newsize; 230 231 check_evportop(epdp); 232 233 return 0; 234 } 235 236 237 /* 238 * (Re)associates the given file descriptor with the event port. The OS events 239 * are specified (implicitly) from the fd_info struct. 240 */ 241 static int 242 reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd) 243 { 244 int sysevents = FDI_TO_SYSEVENTS(fdip); 245 246 if (sysevents != 0) { 247 if (port_associate(epdp->ed_port, PORT_SOURCE_FD, 248 fd, sysevents, NULL) == -1) { 249 event_warn("port_associate"); 250 return (-1); 251 } 252 } 253 254 check_evportop(epdp); 255 256 return (0); 257 } 258 259 /* 260 * Main event loop - polls port_getn for some number of events, and processes 261 * them. 262 */ 263 264 static int 265 evport_dispatch(struct event_base *base, struct timeval *tv) 266 { 267 int i, res; 268 struct evport_data *epdp = base->evbase; 269 port_event_t pevtlist[EVENTS_PER_GETN]; 270 271 /* 272 * port_getn will block until it has at least nevents events. It will 273 * also return how many it's given us (which may be more than we asked 274 * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in 275 * nevents. 276 */ 277 int nevents = 1; 278 279 /* 280 * We have to convert a struct timeval to a struct timespec 281 * (only difference is nanoseconds vs. microseconds). If no time-based 282 * events are active, we should wait for I/O (and tv == NULL). 283 */ 284 struct timespec ts; 285 struct timespec *ts_p = NULL; 286 if (tv != NULL) { 287 ts.tv_sec = tv->tv_sec; 288 ts.tv_nsec = tv->tv_usec * 1000; 289 ts_p = &ts; 290 } 291 292 /* 293 * Before doing anything else, we need to reassociate the events we hit 294 * last time which need reassociation. See comment at the end of the 295 * loop below. 296 */ 297 for (i = 0; i < EVENTS_PER_GETN; ++i) { 298 struct fd_info *fdi = NULL; 299 if (epdp->ed_pending[i] != -1) { 300 fdi = &(epdp->ed_fds[epdp->ed_pending[i]]); 301 } 302 303 if (fdi != NULL && FDI_HAS_EVENTS(fdi)) { 304 int fd = epdp->ed_pending[i]; 305 reassociate(epdp, fdi, fd); 306 epdp->ed_pending[i] = -1; 307 } 308 } 309 310 EVBASE_RELEASE_LOCK(base, th_base_lock); 311 312 res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN, 313 (unsigned int *) &nevents, ts_p); 314 315 EVBASE_ACQUIRE_LOCK(base, th_base_lock); 316 317 if (res == -1) { 318 if (errno == EINTR || errno == EAGAIN) { 319 return (0); 320 } else if (errno == ETIME) { 321 if (nevents == 0) 322 return (0); 323 } else { 324 event_warn("port_getn"); 325 return (-1); 326 } 327 } 328 329 event_debug(("%s: port_getn reports %d events", __func__, nevents)); 330 331 for (i = 0; i < nevents; ++i) { 332 struct fd_info *fdi; 333 port_event_t *pevt = &pevtlist[i]; 334 int fd = (int) pevt->portev_object; 335 336 check_evportop(epdp); 337 check_event(pevt); 338 epdp->ed_pending[i] = fd; 339 340 /* 341 * Figure out what kind of event it was 342 * (because we have to pass this to the callback) 343 */ 344 res = 0; 345 if (pevt->portev_events & (POLLERR|POLLHUP)) { 346 res = EV_READ | EV_WRITE; 347 } else { 348 if (pevt->portev_events & POLLIN) 349 res |= EV_READ; 350 if (pevt->portev_events & POLLOUT) 351 res |= EV_WRITE; 352 } 353 354 /* 355 * Check for the error situations or a hangup situation 356 */ 357 if (pevt->portev_events & (POLLERR|POLLHUP|POLLNVAL)) 358 res |= EV_READ|EV_WRITE; 359 360 EVUTIL_ASSERT(epdp->ed_nevents > fd); 361 fdi = &(epdp->ed_fds[fd]); 362 363 evmap_io_active(base, fd, res); 364 } /* end of all events gotten */ 365 366 check_evportop(epdp); 367 368 return (0); 369 } 370 371 372 /* 373 * Adds the given event (so that you will be notified when it happens via 374 * the callback function). 375 */ 376 377 static int 378 evport_add(struct event_base *base, int fd, short old, short events, void *p) 379 { 380 struct evport_data *evpd = base->evbase; 381 struct fd_info *fdi; 382 int factor; 383 (void)p; 384 385 check_evportop(evpd); 386 387 /* 388 * If necessary, grow the file descriptor info table 389 */ 390 391 factor = 1; 392 while (fd >= factor * evpd->ed_nevents) 393 factor *= 2; 394 395 if (factor > 1) { 396 if (-1 == grow(evpd, factor)) { 397 return (-1); 398 } 399 } 400 401 fdi = &evpd->ed_fds[fd]; 402 fdi->fdi_what |= events; 403 404 return reassociate(evpd, fdi, fd); 405 } 406 407 /* 408 * Removes the given event from the list of events to wait for. 409 */ 410 411 static int 412 evport_del(struct event_base *base, int fd, short old, short events, void *p) 413 { 414 struct evport_data *evpd = base->evbase; 415 struct fd_info *fdi; 416 int i; 417 int associated = 1; 418 (void)p; 419 420 check_evportop(evpd); 421 422 if (evpd->ed_nevents < fd) { 423 return (-1); 424 } 425 426 for (i = 0; i < EVENTS_PER_GETN; ++i) { 427 if (evpd->ed_pending[i] == fd) { 428 associated = 0; 429 break; 430 } 431 } 432 433 fdi = &evpd->ed_fds[fd]; 434 if (events & EV_READ) 435 fdi->fdi_what &= ~EV_READ; 436 if (events & EV_WRITE) 437 fdi->fdi_what &= ~EV_WRITE; 438 439 if (associated) { 440 if (!FDI_HAS_EVENTS(fdi) && 441 port_dissociate(evpd->ed_port, PORT_SOURCE_FD, fd) == -1) { 442 /* 443 * Ignore EBADFD error the fd could have been closed 444 * before event_del() was called. 445 */ 446 if (errno != EBADFD) { 447 event_warn("port_dissociate"); 448 return (-1); 449 } 450 } else { 451 if (FDI_HAS_EVENTS(fdi)) { 452 return (reassociate(evpd, fdi, fd)); 453 } 454 } 455 } else { 456 if ((fdi->fdi_what & (EV_READ|EV_WRITE)) == 0) { 457 evpd->ed_pending[i] = -1; 458 } 459 } 460 return 0; 461 } 462 463 464 static void 465 evport_dealloc(struct event_base *base) 466 { 467 struct evport_data *evpd = base->evbase; 468 469 evsig_dealloc(base); 470 471 close(evpd->ed_port); 472 473 if (evpd->ed_fds) 474 mm_free(evpd->ed_fds); 475 mm_free(evpd); 476 } 477