1 /* $NetBSD: evport.c,v 1.4 2016/01/08 21:35:40 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* 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 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 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 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 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 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 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 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 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