1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apr.h" 18#include "apr_poll.h" 19#include "apr_time.h" 20#include "apr_portable.h" 21#include "apr_atomic.h" 22#include "apr_arch_file_io.h" 23#include "apr_arch_networkio.h" 24#include "apr_arch_poll_private.h" 25#include "apr_arch_inherit.h" 26 27#if defined(HAVE_PORT_CREATE) 28 29static apr_int16_t get_event(apr_int16_t event) 30{ 31 apr_int16_t rv = 0; 32 33 if (event & APR_POLLIN) 34 rv |= POLLIN; 35 if (event & APR_POLLPRI) 36 rv |= POLLPRI; 37 if (event & APR_POLLOUT) 38 rv |= POLLOUT; 39 /* POLLERR, POLLHUP, and POLLNVAL aren't valid as requested events */ 40 41 return rv; 42} 43 44static apr_int16_t get_revent(apr_int16_t event) 45{ 46 apr_int16_t rv = 0; 47 48 if (event & POLLIN) 49 rv |= APR_POLLIN; 50 if (event & POLLPRI) 51 rv |= APR_POLLPRI; 52 if (event & POLLOUT) 53 rv |= APR_POLLOUT; 54 if (event & POLLERR) 55 rv |= APR_POLLERR; 56 if (event & POLLHUP) 57 rv |= APR_POLLHUP; 58 if (event & POLLNVAL) 59 rv |= APR_POLLNVAL; 60 61 return rv; 62} 63 64 65struct apr_pollset_private_t 66{ 67 int port_fd; 68 port_event_t *port_set; 69 apr_pollfd_t *result_set; 70#if APR_HAS_THREADS 71 /* A thread mutex to protect operations on the rings */ 72 apr_thread_mutex_t *ring_lock; 73#endif 74 /* A ring containing all of the pollfd_t that are active */ 75 APR_RING_HEAD(pfd_query_ring_t, pfd_elem_t) query_ring; 76 /* A ring containing the pollfd_t that will be added on the 77 * next call to apr_pollset_poll(). 78 */ 79 APR_RING_HEAD(pfd_add_ring_t, pfd_elem_t) add_ring; 80 /* A ring of pollfd_t that have been used, and then _remove'd */ 81 APR_RING_HEAD(pfd_free_ring_t, pfd_elem_t) free_ring; 82 /* A ring of pollfd_t where rings that have been _remove'd but 83 might still be inside a _poll */ 84 APR_RING_HEAD(pfd_dead_ring_t, pfd_elem_t) dead_ring; 85 /* number of threads in poll */ 86 volatile apr_uint32_t waiting; 87}; 88 89static apr_status_t call_port_getn(int port, port_event_t list[], 90 unsigned int max, unsigned int *nget, 91 apr_interval_time_t timeout) 92{ 93 struct timespec tv, *tvptr; 94 int ret; 95 apr_status_t rv = APR_SUCCESS; 96 97 if (timeout < 0) { 98 tvptr = NULL; 99 } 100 else { 101 tv.tv_sec = (long) apr_time_sec(timeout); 102 tv.tv_nsec = (long) apr_time_usec(timeout) * 1000; 103 tvptr = &tv; 104 } 105 106 list[0].portev_user = (void *)-1; /* so we can double check that an 107 * event was returned 108 */ 109 110 ret = port_getn(port, list, max, nget, tvptr); 111 /* Note: 32-bit port_getn() on Solaris 10 x86 returns large negative 112 * values instead of 0 when returning immediately. 113 */ 114 115 if (ret == -1) { 116 rv = apr_get_netos_error(); 117 118 switch(rv) { 119 case EINTR: 120 case ETIME: 121 if (*nget > 0 && list[0].portev_user != (void *)-1) { 122 /* This confusing API can return an event at the same time 123 * that it reports EINTR or ETIME. If that occurs, just 124 * report the event. With EINTR, nget can be > 0 without 125 * any event, so check that portev_user was filled in. 126 * 127 * (Maybe it will be simplified; see thread 128 * http://mail.opensolaris.org 129 * /pipermail/networking-discuss/2009-August/011979.html 130 * This code will still work afterwards.) 131 */ 132 rv = APR_SUCCESS; 133 break; 134 } 135 if (rv == ETIME) { 136 rv = APR_TIMEUP; 137 } 138 /* fall-through */ 139 default: 140 *nget = 0; 141 } 142 } 143 else if (*nget == 0) { 144 rv = APR_TIMEUP; 145 } 146 147 return rv; 148} 149 150static apr_status_t impl_pollset_cleanup(apr_pollset_t *pollset) 151{ 152 close(pollset->p->port_fd); 153 return APR_SUCCESS; 154} 155 156static apr_status_t impl_pollset_create(apr_pollset_t *pollset, 157 apr_uint32_t size, 158 apr_pool_t *p, 159 apr_uint32_t flags) 160{ 161 apr_status_t rv = APR_SUCCESS; 162 pollset->p = apr_palloc(p, sizeof(apr_pollset_private_t)); 163#if APR_HAS_THREADS 164 if (flags & APR_POLLSET_THREADSAFE && 165 ((rv = apr_thread_mutex_create(&pollset->p->ring_lock, 166 APR_THREAD_MUTEX_DEFAULT, 167 p)) != APR_SUCCESS)) { 168 pollset->p = NULL; 169 return rv; 170 } 171#else 172 if (flags & APR_POLLSET_THREADSAFE) { 173 pollset->p = NULL; 174 return APR_ENOTIMPL; 175 } 176#endif 177 pollset->p->waiting = 0; 178 179 pollset->p->port_set = apr_palloc(p, size * sizeof(port_event_t)); 180 181 pollset->p->port_fd = port_create(); 182 183 if (pollset->p->port_fd < 0) { 184 pollset->p = NULL; 185 return apr_get_netos_error(); 186 } 187 188 { 189 int flags; 190 191 if ((flags = fcntl(pollset->p->port_fd, F_GETFD)) == -1) { 192 rv = errno; 193 close(pollset->p->port_fd); 194 pollset->p = NULL; 195 return rv; 196 } 197 198 flags |= FD_CLOEXEC; 199 if (fcntl(pollset->p->port_fd, F_SETFD, flags) == -1) { 200 rv = errno; 201 close(pollset->p->port_fd); 202 pollset->p = NULL; 203 return rv; 204 } 205 } 206 207 pollset->p->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); 208 209 APR_RING_INIT(&pollset->p->query_ring, pfd_elem_t, link); 210 APR_RING_INIT(&pollset->p->add_ring, pfd_elem_t, link); 211 APR_RING_INIT(&pollset->p->free_ring, pfd_elem_t, link); 212 APR_RING_INIT(&pollset->p->dead_ring, pfd_elem_t, link); 213 214 return rv; 215} 216 217static apr_status_t impl_pollset_add(apr_pollset_t *pollset, 218 const apr_pollfd_t *descriptor) 219{ 220 apr_os_sock_t fd; 221 pfd_elem_t *elem; 222 int res; 223 apr_status_t rv = APR_SUCCESS; 224 225 pollset_lock_rings(); 226 227 if (!APR_RING_EMPTY(&(pollset->p->free_ring), pfd_elem_t, link)) { 228 elem = APR_RING_FIRST(&(pollset->p->free_ring)); 229 APR_RING_REMOVE(elem, link); 230 } 231 else { 232 elem = (pfd_elem_t *) apr_palloc(pollset->pool, sizeof(pfd_elem_t)); 233 APR_RING_ELEM_INIT(elem, link); 234 elem->on_query_ring = 0; 235 } 236 elem->pfd = *descriptor; 237 238 if (descriptor->desc_type == APR_POLL_SOCKET) { 239 fd = descriptor->desc.s->socketdes; 240 } 241 else { 242 fd = descriptor->desc.f->filedes; 243 } 244 245 /* If another thread is polling, notify the kernel immediately; otherwise, 246 * wait until the next call to apr_pollset_poll(). 247 */ 248 if (apr_atomic_read32(&pollset->p->waiting)) { 249 res = port_associate(pollset->p->port_fd, PORT_SOURCE_FD, fd, 250 get_event(descriptor->reqevents), (void *)elem); 251 252 if (res < 0) { 253 rv = apr_get_netos_error(); 254 APR_RING_INSERT_TAIL(&(pollset->p->free_ring), elem, pfd_elem_t, link); 255 } 256 else { 257 elem->on_query_ring = 1; 258 APR_RING_INSERT_TAIL(&(pollset->p->query_ring), elem, pfd_elem_t, link); 259 } 260 } 261 else { 262 APR_RING_INSERT_TAIL(&(pollset->p->add_ring), elem, pfd_elem_t, link); 263 } 264 265 pollset_unlock_rings(); 266 267 return rv; 268} 269 270static apr_status_t impl_pollset_remove(apr_pollset_t *pollset, 271 const apr_pollfd_t *descriptor) 272{ 273 apr_os_sock_t fd; 274 pfd_elem_t *ep; 275 apr_status_t rv = APR_SUCCESS; 276 int res; 277 int err = 0; 278 int found; 279 280 pollset_lock_rings(); 281 282 if (descriptor->desc_type == APR_POLL_SOCKET) { 283 fd = descriptor->desc.s->socketdes; 284 } 285 else { 286 fd = descriptor->desc.f->filedes; 287 } 288 289 /* Search the add ring first. This ring is often shorter, 290 * and it often contains the descriptor being removed. 291 * (For the common scenario where apr_pollset_poll() 292 * returns activity for the descriptor and the descriptor 293 * is then removed from the pollset, it will have just 294 * been moved to the add ring by apr_pollset_poll().) 295 * 296 * If it is on the add ring, it isn't associated with the 297 * event port yet/anymore. 298 */ 299 found = 0; 300 for (ep = APR_RING_FIRST(&(pollset->p->add_ring)); 301 ep != APR_RING_SENTINEL(&(pollset->p->add_ring), 302 pfd_elem_t, link); 303 ep = APR_RING_NEXT(ep, link)) { 304 305 if (descriptor->desc.s == ep->pfd.desc.s) { 306 found = 1; 307 APR_RING_REMOVE(ep, link); 308 APR_RING_INSERT_TAIL(&(pollset->p->free_ring), 309 ep, pfd_elem_t, link); 310 break; 311 } 312 } 313 314 if (!found) { 315 res = port_dissociate(pollset->p->port_fd, PORT_SOURCE_FD, fd); 316 317 if (res < 0) { 318 /* The expected case for this failure is that another 319 * thread's call to port_getn() returned this fd and 320 * disassociated the fd from the event port, and 321 * impl_pollset_poll() is blocked on the ring lock, 322 * which this thread holds. 323 */ 324 err = errno; 325 rv = APR_NOTFOUND; 326 } 327 328 for (ep = APR_RING_FIRST(&(pollset->p->query_ring)); 329 ep != APR_RING_SENTINEL(&(pollset->p->query_ring), 330 pfd_elem_t, link); 331 ep = APR_RING_NEXT(ep, link)) { 332 333 if (descriptor->desc.s == ep->pfd.desc.s) { 334 APR_RING_REMOVE(ep, link); 335 ep->on_query_ring = 0; 336 APR_RING_INSERT_TAIL(&(pollset->p->dead_ring), 337 ep, pfd_elem_t, link); 338 if (ENOENT == err) { 339 rv = APR_SUCCESS; 340 } 341 break; 342 } 343 } 344 } 345 346 pollset_unlock_rings(); 347 348 return rv; 349} 350 351static apr_status_t impl_pollset_poll(apr_pollset_t *pollset, 352 apr_interval_time_t timeout, 353 apr_int32_t *num, 354 const apr_pollfd_t **descriptors) 355{ 356 apr_os_sock_t fd; 357 int ret; 358 unsigned int nget, i; 359 apr_int32_t j; 360 pfd_elem_t *ep; 361 apr_status_t rv = APR_SUCCESS; 362 363 *num = 0; 364 nget = 1; 365 366 pollset_lock_rings(); 367 368 apr_atomic_inc32(&pollset->p->waiting); 369 370 while (!APR_RING_EMPTY(&(pollset->p->add_ring), pfd_elem_t, link)) { 371 ep = APR_RING_FIRST(&(pollset->p->add_ring)); 372 APR_RING_REMOVE(ep, link); 373 374 if (ep->pfd.desc_type == APR_POLL_SOCKET) { 375 fd = ep->pfd.desc.s->socketdes; 376 } 377 else { 378 fd = ep->pfd.desc.f->filedes; 379 } 380 381 ret = port_associate(pollset->p->port_fd, PORT_SOURCE_FD, 382 fd, get_event(ep->pfd.reqevents), ep); 383 if (ret < 0) { 384 rv = apr_get_netos_error(); 385 APR_RING_INSERT_TAIL(&(pollset->p->free_ring), ep, pfd_elem_t, link); 386 break; 387 } 388 389 ep->on_query_ring = 1; 390 APR_RING_INSERT_TAIL(&(pollset->p->query_ring), ep, pfd_elem_t, link); 391 } 392 393 pollset_unlock_rings(); 394 395 if (rv != APR_SUCCESS) { 396 apr_atomic_dec32(&pollset->p->waiting); 397 return rv; 398 } 399 400 rv = call_port_getn(pollset->p->port_fd, pollset->p->port_set, 401 pollset->nalloc, &nget, timeout); 402 403 /* decrease the waiting ASAP to reduce the window for calling 404 port_associate within apr_pollset_add() */ 405 apr_atomic_dec32(&pollset->p->waiting); 406 407 pollset_lock_rings(); 408 409 for (i = 0, j = 0; i < nget; i++) { 410 ep = (pfd_elem_t *)pollset->p->port_set[i].portev_user; 411 if ((pollset->flags & APR_POLLSET_WAKEABLE) && 412 ep->pfd.desc_type == APR_POLL_FILE && 413 ep->pfd.desc.f == pollset->wakeup_pipe[0]) { 414 apr_poll_drain_wakeup_pipe(pollset->wakeup_pipe); 415 rv = APR_EINTR; 416 } 417 else { 418 pollset->p->result_set[j] = ep->pfd; 419 pollset->p->result_set[j].rtnevents = 420 get_revent(pollset->p->port_set[i].portev_events); 421 j++; 422 } 423 /* If the ring element is still on the query ring, move it 424 * to the add ring for re-association with the event port 425 * later. (It may have already been moved to the dead ring 426 * by a call to pollset_remove on another thread.) 427 */ 428 if (ep->on_query_ring) { 429 APR_RING_REMOVE(ep, link); 430 ep->on_query_ring = 0; 431 APR_RING_INSERT_TAIL(&(pollset->p->add_ring), ep, 432 pfd_elem_t, link); 433 } 434 } 435 if ((*num = j)) { /* any event besides wakeup pipe? */ 436 rv = APR_SUCCESS; 437 if (descriptors) { 438 *descriptors = pollset->p->result_set; 439 } 440 } 441 442 /* Shift all PFDs in the Dead Ring to the Free Ring */ 443 APR_RING_CONCAT(&(pollset->p->free_ring), &(pollset->p->dead_ring), pfd_elem_t, link); 444 445 pollset_unlock_rings(); 446 447 return rv; 448} 449 450static const apr_pollset_provider_t impl = { 451 impl_pollset_create, 452 impl_pollset_add, 453 impl_pollset_remove, 454 impl_pollset_poll, 455 impl_pollset_cleanup, 456 "port" 457}; 458 459const apr_pollset_provider_t *apr_pollset_provider_port = &impl; 460 461static apr_status_t impl_pollcb_cleanup(apr_pollcb_t *pollcb) 462{ 463 close(pollcb->fd); 464 return APR_SUCCESS; 465} 466 467static apr_status_t impl_pollcb_create(apr_pollcb_t *pollcb, 468 apr_uint32_t size, 469 apr_pool_t *p, 470 apr_uint32_t flags) 471{ 472 pollcb->fd = port_create(); 473 474 if (pollcb->fd < 0) { 475 return apr_get_netos_error(); 476 } 477 478 { 479 int flags; 480 apr_status_t rv; 481 482 if ((flags = fcntl(pollcb->fd, F_GETFD)) == -1) { 483 rv = errno; 484 close(pollcb->fd); 485 pollcb->fd = -1; 486 return rv; 487 } 488 489 flags |= FD_CLOEXEC; 490 if (fcntl(pollcb->fd, F_SETFD, flags) == -1) { 491 rv = errno; 492 close(pollcb->fd); 493 pollcb->fd = -1; 494 return rv; 495 } 496 } 497 498 pollcb->pollset.port = apr_palloc(p, size * sizeof(port_event_t)); 499 500 return APR_SUCCESS; 501} 502 503static apr_status_t impl_pollcb_add(apr_pollcb_t *pollcb, 504 apr_pollfd_t *descriptor) 505{ 506 int ret, fd; 507 508 if (descriptor->desc_type == APR_POLL_SOCKET) { 509 fd = descriptor->desc.s->socketdes; 510 } 511 else { 512 fd = descriptor->desc.f->filedes; 513 } 514 515 ret = port_associate(pollcb->fd, PORT_SOURCE_FD, fd, 516 get_event(descriptor->reqevents), descriptor); 517 518 if (ret == -1) { 519 return apr_get_netos_error(); 520 } 521 522 return APR_SUCCESS; 523} 524 525static apr_status_t impl_pollcb_remove(apr_pollcb_t *pollcb, 526 apr_pollfd_t *descriptor) 527{ 528 int fd, ret; 529 530 if (descriptor->desc_type == APR_POLL_SOCKET) { 531 fd = descriptor->desc.s->socketdes; 532 } 533 else { 534 fd = descriptor->desc.f->filedes; 535 } 536 537 ret = port_dissociate(pollcb->fd, PORT_SOURCE_FD, fd); 538 539 if (ret < 0) { 540 return APR_NOTFOUND; 541 } 542 543 return APR_SUCCESS; 544} 545 546static apr_status_t impl_pollcb_poll(apr_pollcb_t *pollcb, 547 apr_interval_time_t timeout, 548 apr_pollcb_cb_t func, 549 void *baton) 550{ 551 apr_status_t rv; 552 unsigned int nget = 1; 553 554 rv = call_port_getn(pollcb->fd, pollcb->pollset.port, pollcb->nalloc, 555 &nget, timeout); 556 557 if (nget) { 558 unsigned int i; 559 560 for (i = 0; i < nget; i++) { 561 apr_pollfd_t *pollfd = (apr_pollfd_t *)(pollcb->pollset.port[i].portev_user); 562 563 if ((pollcb->flags & APR_POLLSET_WAKEABLE) && 564 pollfd->desc_type == APR_POLL_FILE && 565 pollfd->desc.f == pollcb->wakeup_pipe[0]) { 566 apr_poll_drain_wakeup_pipe(pollcb->wakeup_pipe); 567 return APR_EINTR; 568 } 569 570 pollfd->rtnevents = get_revent(pollcb->pollset.port[i].portev_events); 571 572 rv = func(baton, pollfd); 573 if (rv) { 574 return rv; 575 } 576 rv = apr_pollcb_add(pollcb, pollfd); 577 } 578 } 579 580 return rv; 581} 582 583static const apr_pollcb_provider_t impl_cb = { 584 impl_pollcb_create, 585 impl_pollcb_add, 586 impl_pollcb_remove, 587 impl_pollcb_poll, 588 impl_pollcb_cleanup, 589 "port" 590}; 591 592const apr_pollcb_provider_t *apr_pollcb_provider_port = &impl_cb; 593 594#endif /* HAVE_PORT_CREATE */ 595