1 /* 2 * Copyright 2016 Keith Packard 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that copyright 7 * notice and this permission notice appear in supporting documentation, and 8 * that the name of the copyright holders not be used in advertising or 9 * publicity pertaining to distribution of the software without specific, 10 * written prior permission. The copyright holders make no representations 11 * about the suitability of this software for any purpose. It is provided "as 12 * is" without express or implied warranty. 13 * 14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20 * OF THIS SOFTWARE. 21 */ 22 23 #ifdef HAVE_DIX_CONFIG_H 24 #include <dix-config.h> 25 #endif 26 27 #include <X11/X.h> 28 #include <X11/Xproto.h> 29 #include <assert.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include "misc.h" /* for typedef of pointer */ 33 #include "ospoll.h" 34 #include "list.h" 35 36 #if !HAVE_OSPOLL && defined(HAVE_POLLSET_CREATE) 37 #include <sys/pollset.h> 38 #define POLLSET 1 39 #define HAVE_OSPOLL 1 40 #endif 41 42 #if !HAVE_OSPOLL && defined(HAVE_PORT_CREATE) 43 #include <port.h> 44 #include <poll.h> 45 #define PORT 1 46 #define HAVE_OSPOLL 1 47 #endif 48 49 #if !HAVE_OSPOLL && defined(HAVE_EPOLL_CREATE1) 50 #include <sys/epoll.h> 51 #define EPOLL 1 52 #define HAVE_OSPOLL 1 53 #endif 54 55 #if !HAVE_OSPOLL 56 #include "xserver_poll.h" 57 #define POLL 1 58 #define HAVE_OSPOLL 1 59 #endif 60 61 #if POLLSET 62 63 // pollset-based implementation (as seen on AIX) 64 struct ospollfd { 65 int fd; 66 int xevents; 67 short revents; 68 enum ospoll_trigger trigger; 69 void (*callback)(int fd, int xevents, void *data); 70 void *data; 71 }; 72 73 struct ospoll { 74 pollset_t ps; 75 struct ospollfd *fds; 76 int num; 77 int size; 78 }; 79 80 #endif 81 82 #if EPOLL || PORT 83 84 /* epoll-based implementation */ 85 struct ospollfd { 86 int fd; 87 int xevents; 88 enum ospoll_trigger trigger; 89 void (*callback)(int fd, int xevents, void *data); 90 void *data; 91 struct xorg_list deleted; 92 }; 93 94 struct ospoll { 95 int epoll_fd; 96 struct ospollfd **fds; 97 int num; 98 int size; 99 struct xorg_list deleted; 100 }; 101 102 #endif 103 104 #if POLL 105 106 /* poll-based implementation */ 107 struct ospollfd { 108 short revents; 109 enum ospoll_trigger trigger; 110 void (*callback)(int fd, int revents, void *data); 111 void *data; 112 }; 113 114 struct ospoll { 115 struct pollfd *fds; 116 struct ospollfd *osfds; 117 int num; 118 int size; 119 Bool changed; 120 }; 121 122 #endif 123 124 /* Binary search for the specified file descriptor 125 * 126 * Returns position if found 127 * Returns -position - 1 if not found 128 */ 129 130 static int 131 ospoll_find(struct ospoll *ospoll, int fd) 132 { 133 int lo = 0; 134 int hi = ospoll->num - 1; 135 136 while (lo <= hi) { 137 int m = (lo + hi) >> 1; 138 #if EPOLL || PORT 139 int t = ospoll->fds[m]->fd; 140 #endif 141 #if POLL || POLLSET 142 int t = ospoll->fds[m].fd; 143 #endif 144 145 if (t < fd) 146 lo = m + 1; 147 else if (t > fd) 148 hi = m - 1; 149 else 150 return m; 151 } 152 return -(lo + 1); 153 } 154 155 #if EPOLL || PORT 156 static void 157 ospoll_clean_deleted(struct ospoll *ospoll) 158 { 159 struct ospollfd *osfd, *tmp; 160 161 xorg_list_for_each_entry_safe(osfd, tmp, &ospoll->deleted, deleted) { 162 xorg_list_del(&osfd->deleted); 163 free(osfd); 164 } 165 } 166 #endif 167 168 /* Insert an element into an array 169 * 170 * base: base address of array 171 * num: number of elements in the array before the insert 172 * size: size of each element 173 * pos: position to insert at 174 */ 175 static inline void 176 array_insert(void *base, size_t num, size_t size, size_t pos) 177 { 178 char *b = base; 179 180 memmove(b + (pos+1) * size, 181 b + pos * size, 182 (num - pos) * size); 183 } 184 185 /* Delete an element from an array 186 * 187 * base: base address of array 188 * num: number of elements in the array before the delete 189 * size: size of each element 190 * pos: position to delete from 191 */ 192 static inline void 193 array_delete(void *base, size_t num, size_t size, size_t pos) 194 { 195 char *b = base; 196 197 memmove(b + pos * size, b + (pos + 1) * size, 198 (num - pos - 1) * size); 199 } 200 201 202 struct ospoll * 203 ospoll_create(void) 204 { 205 #if POLLSET 206 struct ospoll *ospoll = calloc(1, sizeof (struct ospoll)); 207 208 ospoll->ps = pollset_create(-1); 209 if (ospoll->ps < 0) { 210 free (ospoll); 211 return NULL; 212 } 213 return ospoll; 214 #endif 215 #if PORT 216 struct ospoll *ospoll = calloc(1, sizeof (struct ospoll)); 217 218 ospoll->epoll_fd = port_create(); 219 if (ospoll->epoll_fd < 0) { 220 free (ospoll); 221 return NULL; 222 } 223 xorg_list_init(&ospoll->deleted); 224 return ospoll; 225 #endif 226 #if EPOLL 227 struct ospoll *ospoll = calloc(1, sizeof (struct ospoll)); 228 229 ospoll->epoll_fd = epoll_create1(EPOLL_CLOEXEC); 230 if (ospoll->epoll_fd < 0) { 231 free (ospoll); 232 return NULL; 233 } 234 xorg_list_init(&ospoll->deleted); 235 return ospoll; 236 #endif 237 #if POLL 238 return calloc(1, sizeof (struct ospoll)); 239 #endif 240 } 241 242 void 243 ospoll_destroy(struct ospoll *ospoll) 244 { 245 #if POLLSET 246 if (ospoll) { 247 assert (ospoll->num == 0); 248 pollset_destroy(ospoll->ps); 249 free(ospoll->fds); 250 free(ospoll); 251 } 252 #endif 253 #if EPOLL || PORT 254 if (ospoll) { 255 assert (ospoll->num == 0); 256 close(ospoll->epoll_fd); 257 ospoll_clean_deleted(ospoll); 258 free(ospoll->fds); 259 free(ospoll); 260 } 261 #endif 262 #if POLL 263 if (ospoll) { 264 assert (ospoll->num == 0); 265 free (ospoll->fds); 266 free (ospoll->osfds); 267 free (ospoll); 268 } 269 #endif 270 } 271 272 Bool 273 ospoll_add(struct ospoll *ospoll, int fd, 274 enum ospoll_trigger trigger, 275 void (*callback)(int fd, int xevents, void *data), 276 void *data) 277 { 278 int pos = ospoll_find(ospoll, fd); 279 #if POLLSET 280 if (pos < 0) { 281 if (ospoll->num == ospoll->size) { 282 struct ospollfd *new_fds; 283 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2; 284 285 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0])); 286 if (!new_fds) 287 return FALSE; 288 ospoll->fds = new_fds; 289 ospoll->size = new_size; 290 } 291 pos = -pos - 1; 292 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 293 ospoll->num++; 294 295 ospoll->fds[pos].fd = fd; 296 ospoll->fds[pos].xevents = 0; 297 ospoll->fds[pos].revents = 0; 298 } 299 ospoll->fds[pos].trigger = trigger; 300 ospoll->fds[pos].callback = callback; 301 ospoll->fds[pos].data = data; 302 #endif 303 #if PORT 304 struct ospollfd *osfd; 305 306 if (pos < 0) { 307 osfd = calloc(1, sizeof (struct ospollfd)); 308 if (!osfd) 309 return FALSE; 310 311 if (ospoll->num >= ospoll->size) { 312 struct ospollfd **new_fds; 313 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2; 314 315 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0])); 316 if (!new_fds) { 317 free (osfd); 318 return FALSE; 319 } 320 ospoll->fds = new_fds; 321 ospoll->size = new_size; 322 } 323 324 osfd->fd = fd; 325 osfd->xevents = 0; 326 327 pos = -pos - 1; 328 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 329 ospoll->fds[pos] = osfd; 330 ospoll->num++; 331 } else { 332 osfd = ospoll->fds[pos]; 333 } 334 osfd->data = data; 335 osfd->callback = callback; 336 osfd->trigger = trigger; 337 #endif 338 #if EPOLL 339 struct ospollfd *osfd; 340 341 if (pos < 0) { 342 343 struct epoll_event ev; 344 345 osfd = calloc(1, sizeof (struct ospollfd)); 346 if (!osfd) 347 return FALSE; 348 349 if (ospoll->num >= ospoll->size) { 350 struct ospollfd **new_fds; 351 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2; 352 353 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0])); 354 if (!new_fds) { 355 free (osfd); 356 return FALSE; 357 } 358 ospoll->fds = new_fds; 359 ospoll->size = new_size; 360 } 361 362 ev.events = 0; 363 ev.data.ptr = osfd; 364 if (trigger == ospoll_trigger_edge) 365 ev.events |= EPOLLET; 366 if (epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { 367 free(osfd); 368 return FALSE; 369 } 370 osfd->fd = fd; 371 osfd->xevents = 0; 372 373 pos = -pos - 1; 374 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 375 ospoll->fds[pos] = osfd; 376 ospoll->num++; 377 } else { 378 osfd = ospoll->fds[pos]; 379 } 380 osfd->data = data; 381 osfd->callback = callback; 382 osfd->trigger = trigger; 383 #endif 384 #if POLL 385 if (pos < 0) { 386 if (ospoll->num == ospoll->size) { 387 struct pollfd *new_fds; 388 struct ospollfd *new_osfds; 389 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2; 390 391 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0])); 392 if (!new_fds) 393 return FALSE; 394 ospoll->fds = new_fds; 395 new_osfds = reallocarray(ospoll->osfds, new_size, sizeof (ospoll->osfds[0])); 396 if (!new_osfds) 397 return FALSE; 398 ospoll->osfds = new_osfds; 399 ospoll->size = new_size; 400 } 401 pos = -pos - 1; 402 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 403 array_insert(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos); 404 ospoll->num++; 405 ospoll->changed = TRUE; 406 407 ospoll->fds[pos].fd = fd; 408 ospoll->fds[pos].events = 0; 409 ospoll->fds[pos].revents = 0; 410 ospoll->osfds[pos].revents = 0; 411 } 412 ospoll->osfds[pos].trigger = trigger; 413 ospoll->osfds[pos].callback = callback; 414 ospoll->osfds[pos].data = data; 415 #endif 416 return TRUE; 417 } 418 419 void 420 ospoll_remove(struct ospoll *ospoll, int fd) 421 { 422 int pos = ospoll_find(ospoll, fd); 423 424 pos = ospoll_find(ospoll, fd); 425 if (pos >= 0) { 426 #if POLLSET 427 struct ospollfd *osfd = &ospoll->fds[pos]; 428 struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd }; 429 pollset_ctl(ospoll->ps, &ctl, 1); 430 431 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 432 ospoll->num--; 433 #endif 434 #if PORT 435 struct ospollfd *osfd = ospoll->fds[pos]; 436 port_dissociate(ospoll->epoll_fd, PORT_SOURCE_FD, fd); 437 438 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 439 ospoll->num--; 440 osfd->callback = NULL; 441 osfd->data = NULL; 442 xorg_list_add(&osfd->deleted, &ospoll->deleted); 443 #endif 444 #if EPOLL 445 struct ospollfd *osfd = ospoll->fds[pos]; 446 struct epoll_event ev; 447 ev.events = 0; 448 ev.data.ptr = osfd; 449 (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_DEL, fd, &ev); 450 451 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 452 ospoll->num--; 453 osfd->callback = NULL; 454 osfd->data = NULL; 455 xorg_list_add(&osfd->deleted, &ospoll->deleted); 456 #endif 457 #if POLL 458 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 459 array_delete(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos); 460 ospoll->num--; 461 ospoll->changed = TRUE; 462 #endif 463 } 464 } 465 466 #if PORT 467 static void 468 epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd) 469 { 470 int events = 0; 471 if (osfd->xevents & X_NOTIFY_READ) 472 events |= POLLIN; 473 if (osfd->xevents & X_NOTIFY_WRITE) 474 events |= POLLOUT; 475 port_associate(ospoll->epoll_fd, PORT_SOURCE_FD, osfd->fd, events, osfd); 476 } 477 #endif 478 479 #if EPOLL 480 static void 481 epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd) 482 { 483 struct epoll_event ev; 484 ev.events = 0; 485 if (osfd->xevents & X_NOTIFY_READ) 486 ev.events |= EPOLLIN; 487 if (osfd->xevents & X_NOTIFY_WRITE) 488 ev.events |= EPOLLOUT; 489 if (osfd->trigger == ospoll_trigger_edge) 490 ev.events |= EPOLLET; 491 ev.data.ptr = osfd; 492 (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_MOD, osfd->fd, &ev); 493 } 494 #endif 495 496 void 497 ospoll_listen(struct ospoll *ospoll, int fd, int xevents) 498 { 499 int pos = ospoll_find(ospoll, fd); 500 501 if (pos >= 0) { 502 #if POLLSET 503 struct poll_ctl ctl = { .cmd = PS_MOD, .fd = fd }; 504 if (xevents & X_NOTIFY_READ) { 505 ctl.events |= POLLIN; 506 ospoll->fds[pos].revents &= ~POLLIN; 507 } 508 if (xevents & X_NOTIFY_WRITE) { 509 ctl.events |= POLLOUT; 510 ospoll->fds[pos].revents &= ~POLLOUT; 511 } 512 pollset_ctl(ospoll->ps, &ctl, 1); 513 ospoll->fds[pos].xevents |= xevents; 514 #endif 515 #if EPOLL || PORT 516 struct ospollfd *osfd = ospoll->fds[pos]; 517 osfd->xevents |= xevents; 518 epoll_mod(ospoll, osfd); 519 #endif 520 #if POLL 521 if (xevents & X_NOTIFY_READ) { 522 ospoll->fds[pos].events |= POLLIN; 523 ospoll->osfds[pos].revents &= ~POLLIN; 524 } 525 if (xevents & X_NOTIFY_WRITE) { 526 ospoll->fds[pos].events |= POLLOUT; 527 ospoll->osfds[pos].revents &= ~POLLOUT; 528 } 529 #endif 530 } 531 } 532 533 void 534 ospoll_mute(struct ospoll *ospoll, int fd, int xevents) 535 { 536 int pos = ospoll_find(ospoll, fd); 537 538 if (pos >= 0) { 539 #if POLLSET 540 struct ospollfd *osfd = &ospoll->fds[pos]; 541 osfd->xevents &= ~xevents; 542 struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd }; 543 pollset_ctl(ospoll->ps, &ctl, 1); 544 if (osfd->xevents) { 545 ctl.cmd = PS_ADD; 546 if (osfd->xevents & X_NOTIFY_READ) { 547 ctl.events |= POLLIN; 548 } 549 if (osfd->xevents & X_NOTIFY_WRITE) { 550 ctl.events |= POLLOUT; 551 } 552 pollset_ctl(ospoll->ps, &ctl, 1); 553 } 554 #endif 555 #if EPOLL || PORT 556 struct ospollfd *osfd = ospoll->fds[pos]; 557 osfd->xevents &= ~xevents; 558 epoll_mod(ospoll, osfd); 559 #endif 560 #if POLL 561 if (xevents & X_NOTIFY_READ) 562 ospoll->fds[pos].events &= ~POLLIN; 563 if (xevents & X_NOTIFY_WRITE) 564 ospoll->fds[pos].events &= ~POLLOUT; 565 #endif 566 } 567 } 568 569 570 int 571 ospoll_wait(struct ospoll *ospoll, int timeout) 572 { 573 int nready; 574 #if POLLSET 575 #define MAX_EVENTS 256 576 struct pollfd events[MAX_EVENTS]; 577 578 nready = pollset_poll(ospoll->ps, events, MAX_EVENTS, timeout); 579 for (int i = 0; i < nready; i++) { 580 struct pollfd *ev = &events[i]; 581 int pos = ospoll_find(ospoll, ev->fd); 582 struct ospollfd *osfd = &ospoll->fds[pos]; 583 short revents = ev->revents; 584 short oldevents = osfd->revents; 585 586 osfd->revents = (revents & (POLLIN|POLLOUT)); 587 if (osfd->trigger == ospoll_trigger_edge) 588 revents &= ~oldevents; 589 if (revents) { 590 int xevents = 0; 591 if (revents & POLLIN) 592 xevents |= X_NOTIFY_READ; 593 if (revents & POLLOUT) 594 xevents |= X_NOTIFY_WRITE; 595 if (revents & (~(POLLIN|POLLOUT))) 596 xevents |= X_NOTIFY_ERROR; 597 osfd->callback(osfd->fd, xevents, osfd->data); 598 } 599 } 600 #endif 601 #if PORT 602 #define MAX_EVENTS 256 603 port_event_t events[MAX_EVENTS]; 604 uint_t nget = 1; 605 timespec_t port_timeout = { 606 .tv_sec = timeout / 1000, 607 .tv_nsec = (timeout % 1000) * 1000000 608 }; 609 610 nready = 0; 611 if (port_getn(ospoll->epoll_fd, events, MAX_EVENTS, &nget, &port_timeout) 612 == 0) { 613 nready = nget; 614 } 615 for (int i = 0; i < nready; i++) { 616 port_event_t *ev = &events[i]; 617 struct ospollfd *osfd = ev->portev_user; 618 uint32_t revents = ev->portev_events; 619 int xevents = 0; 620 621 if (revents & POLLIN) 622 xevents |= X_NOTIFY_READ; 623 if (revents & POLLOUT) 624 xevents |= X_NOTIFY_WRITE; 625 if (revents & (~(POLLIN|POLLOUT))) 626 xevents |= X_NOTIFY_ERROR; 627 628 if (osfd->callback) 629 osfd->callback(osfd->fd, xevents, osfd->data); 630 631 if (osfd->trigger == ospoll_trigger_level && 632 !xorg_list_is_empty(&osfd->deleted)) { 633 epoll_mod(ospoll, osfd); 634 } 635 } 636 ospoll_clean_deleted(ospoll); 637 #endif 638 #if EPOLL 639 #define MAX_EVENTS 256 640 struct epoll_event events[MAX_EVENTS]; 641 int i; 642 643 nready = epoll_wait(ospoll->epoll_fd, events, MAX_EVENTS, timeout); 644 for (i = 0; i < nready; i++) { 645 struct epoll_event *ev = &events[i]; 646 struct ospollfd *osfd = ev->data.ptr; 647 uint32_t revents = ev->events; 648 int xevents = 0; 649 650 if (revents & EPOLLIN) 651 xevents |= X_NOTIFY_READ; 652 if (revents & EPOLLOUT) 653 xevents |= X_NOTIFY_WRITE; 654 if (revents & (~(EPOLLIN|EPOLLOUT))) 655 xevents |= X_NOTIFY_ERROR; 656 657 if (osfd->callback) 658 osfd->callback(osfd->fd, xevents, osfd->data); 659 } 660 ospoll_clean_deleted(ospoll); 661 #endif 662 #if POLL 663 nready = xserver_poll(ospoll->fds, ospoll->num, timeout); 664 ospoll->changed = FALSE; 665 if (nready > 0) { 666 int f; 667 for (f = 0; f < ospoll->num; f++) { 668 short revents = ospoll->fds[f].revents; 669 short oldevents = ospoll->osfds[f].revents; 670 671 ospoll->osfds[f].revents = (revents & (POLLIN|POLLOUT)); 672 if (ospoll->osfds[f].trigger == ospoll_trigger_edge) 673 revents &= ~oldevents; 674 if (revents) { 675 int xevents = 0; 676 if (revents & POLLIN) 677 xevents |= X_NOTIFY_READ; 678 if (revents & POLLOUT) 679 xevents |= X_NOTIFY_WRITE; 680 if (revents & (~(POLLIN|POLLOUT))) 681 xevents |= X_NOTIFY_ERROR; 682 ospoll->osfds[f].callback(ospoll->fds[f].fd, xevents, 683 ospoll->osfds[f].data); 684 685 /* Check to see if the arrays have changed, and just go back 686 * around again 687 */ 688 if (ospoll->changed) 689 break; 690 } 691 } 692 } 693 #endif 694 return nready; 695 } 696 697 void 698 ospoll_reset_events(struct ospoll *ospoll, int fd) 699 { 700 #if POLLSET 701 int pos = ospoll_find(ospoll, fd); 702 703 if (pos < 0) 704 return; 705 706 ospoll->fds[pos].revents = 0; 707 #endif 708 #if PORT 709 int pos = ospoll_find(ospoll, fd); 710 711 if (pos < 0) 712 return; 713 714 epoll_mod(ospoll, ospoll->fds[pos]); 715 #endif 716 #if POLL 717 int pos = ospoll_find(ospoll, fd); 718 719 if (pos < 0) 720 return; 721 722 ospoll->osfds[pos].revents = 0; 723 #endif 724 } 725 726 void * 727 ospoll_data(struct ospoll *ospoll, int fd) 728 { 729 int pos = ospoll_find(ospoll, fd); 730 731 if (pos < 0) 732 return NULL; 733 #if POLLSET 734 return ospoll->fds[pos].data; 735 #endif 736 #if EPOLL || PORT 737 return ospoll->fds[pos]->data; 738 #endif 739 #if POLL 740 return ospoll->osfds[pos].data; 741 #endif 742 } 743