1/* $Id$ */ 2 3/*** 4 This file is part of avahi. 5 6 avahi is free software; you can redistribute it and/or modify it 7 under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 avahi is distributed in the hope that it will be useful, but WITHOUT 12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 14 Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with avahi; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 19 USA. 20***/ 21 22#ifdef HAVE_CONFIG_H 23#include <config.h> 24#endif 25 26#include <sys/poll.h> 27#include <assert.h> 28#include <string.h> 29#include <errno.h> 30#include <unistd.h> 31#include <fcntl.h> 32#include <stdio.h> 33 34#include "llist.h" 35#include "malloc.h" 36#include "timeval.h" 37#include "simple-watch.h" 38 39struct AvahiWatch { 40 AvahiSimplePoll *simple_poll; 41 int dead; 42 43 int idx; 44 struct pollfd pollfd; 45 46 AvahiWatchCallback callback; 47 void *userdata; 48 49 AVAHI_LLIST_FIELDS(AvahiWatch, watches); 50}; 51 52struct AvahiTimeout { 53 AvahiSimplePoll *simple_poll; 54 int dead; 55 56 int enabled; 57 struct timeval expiry; 58 59 AvahiTimeoutCallback callback; 60 void *userdata; 61 62 AVAHI_LLIST_FIELDS(AvahiTimeout, timeouts); 63}; 64 65struct AvahiSimplePoll { 66 AvahiPoll api; 67 AvahiPollFunc poll_func; 68 void *poll_func_userdata; 69 70 struct pollfd* pollfds; 71 int n_pollfds, max_pollfds, rebuild_pollfds; 72 73 int watch_req_cleanup, timeout_req_cleanup; 74 int quit; 75 int events_valid; 76 77 int n_watches; 78 AVAHI_LLIST_HEAD(AvahiWatch, watches); 79 AVAHI_LLIST_HEAD(AvahiTimeout, timeouts); 80 81 int wakeup_pipe[2]; 82 int wakeup_issued; 83 84 int prepared_timeout; 85 86 enum { 87 STATE_INIT, 88 STATE_PREPARING, 89 STATE_PREPARED, 90 STATE_RUNNING, 91 STATE_RAN, 92 STATE_DISPATCHING, 93 STATE_DISPATCHED, 94 STATE_QUIT, 95 STATE_FAILURE 96 } state; 97}; 98 99void avahi_simple_poll_wakeup(AvahiSimplePoll *s) { 100 char c = 'W'; 101 assert(s); 102 103 write(s->wakeup_pipe[1], &c, sizeof(c)); 104 s->wakeup_issued = 1; 105} 106 107static void clear_wakeup(AvahiSimplePoll *s) { 108 char c[10]; /* Read ten at a time */ 109 110 if (!s->wakeup_issued) 111 return; 112 113 s->wakeup_issued = 0; 114 115 for(;;) 116 if (read(s->wakeup_pipe[0], &c, sizeof(c)) != sizeof(c)) 117 break; 118} 119 120static int set_nonblock(int fd) { 121 int n; 122 123 assert(fd >= 0); 124 125 if ((n = fcntl(fd, F_GETFL)) < 0) 126 return -1; 127 128 if (n & O_NONBLOCK) 129 return 0; 130 131 return fcntl(fd, F_SETFL, n|O_NONBLOCK); 132} 133 134static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) { 135 AvahiWatch *w; 136 AvahiSimplePoll *s; 137 138 assert(api); 139 assert(fd >= 0); 140 assert(callback); 141 142 s = api->userdata; 143 assert(s); 144 145 if (!(w = avahi_new(AvahiWatch, 1))) 146 return NULL; 147 148 /* If there is a background thread running the poll() for us, tell it to exit the poll() */ 149 avahi_simple_poll_wakeup(s); 150 151 w->simple_poll = s; 152 w->dead = 0; 153 154 w->pollfd.fd = fd; 155 w->pollfd.events = event; 156 w->pollfd.revents = 0; 157 158 w->callback = callback; 159 w->userdata = userdata; 160 161 w->idx = -1; 162 s->rebuild_pollfds = 1; 163 164 AVAHI_LLIST_PREPEND(AvahiWatch, watches, s->watches, w); 165 s->n_watches++; 166 167 return w; 168} 169 170static void watch_update(AvahiWatch *w, AvahiWatchEvent events) { 171 assert(w); 172 assert(!w->dead); 173 174 /* If there is a background thread running the poll() for us, tell it to exit the poll() */ 175 avahi_simple_poll_wakeup(w->simple_poll); 176 177 w->pollfd.events = events; 178 179 if (w->idx != -1) { 180 assert(w->simple_poll); 181 w->simple_poll->pollfds[w->idx] = w->pollfd; 182 } else 183 w->simple_poll->rebuild_pollfds = 1; 184} 185 186static AvahiWatchEvent watch_get_events(AvahiWatch *w) { 187 assert(w); 188 assert(!w->dead); 189 190 if (w->idx != -1 && w->simple_poll->events_valid) 191 return w->simple_poll->pollfds[w->idx].revents; 192 193 return 0; 194} 195 196static void remove_pollfd(AvahiWatch *w) { 197 assert(w); 198 199 if (w->idx == -1) 200 return; 201 202 w->simple_poll->rebuild_pollfds = 1; 203} 204 205static void watch_free(AvahiWatch *w) { 206 assert(w); 207 208 assert(!w->dead); 209 210 /* If there is a background thread running the poll() for us, tell it to exit the poll() */ 211 avahi_simple_poll_wakeup(w->simple_poll); 212 213 remove_pollfd(w); 214 215 w->dead = 1; 216 w->simple_poll->n_watches --; 217 w->simple_poll->watch_req_cleanup = 1; 218} 219 220static void destroy_watch(AvahiWatch *w) { 221 assert(w); 222 223 remove_pollfd(w); 224 AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->simple_poll->watches, w); 225 226 if (!w->dead) 227 w->simple_poll->n_watches --; 228 229 avahi_free(w); 230} 231 232static void cleanup_watches(AvahiSimplePoll *s, int all) { 233 AvahiWatch *w, *next; 234 assert(s); 235 236 for (w = s->watches; w; w = next) { 237 next = w->watches_next; 238 239 if (all || w->dead) 240 destroy_watch(w); 241 } 242 243 s->timeout_req_cleanup = 0; 244} 245 246static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) { 247 AvahiTimeout *t; 248 AvahiSimplePoll *s; 249 250 assert(api); 251 assert(callback); 252 253 s = api->userdata; 254 assert(s); 255 256 if (!(t = avahi_new(AvahiTimeout, 1))) 257 return NULL; 258 259 /* If there is a background thread running the poll() for us, tell it to exit the poll() */ 260 avahi_simple_poll_wakeup(s); 261 262 t->simple_poll = s; 263 t->dead = 0; 264 265 if ((t->enabled = !!tv)) 266 t->expiry = *tv; 267 268 t->callback = callback; 269 t->userdata = userdata; 270 271 AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, s->timeouts, t); 272 return t; 273} 274 275static void timeout_update(AvahiTimeout *t, const struct timeval *tv) { 276 assert(t); 277 assert(!t->dead); 278 279 /* If there is a background thread running the poll() for us, tell it to exit the poll() */ 280 avahi_simple_poll_wakeup(t->simple_poll); 281 282 if ((t->enabled = !!tv)) 283 t->expiry = *tv; 284} 285 286static void timeout_free(AvahiTimeout *t) { 287 assert(t); 288 assert(!t->dead); 289 290 /* If there is a background thread running the poll() for us, tell it to exit the poll() */ 291 avahi_simple_poll_wakeup(t->simple_poll); 292 293 t->dead = 1; 294 t->simple_poll->timeout_req_cleanup = 1; 295} 296 297 298static void destroy_timeout(AvahiTimeout *t) { 299 assert(t); 300 301 AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->simple_poll->timeouts, t); 302 303 avahi_free(t); 304} 305 306static void cleanup_timeouts(AvahiSimplePoll *s, int all) { 307 AvahiTimeout *t, *next; 308 assert(s); 309 310 for (t = s->timeouts; t; t = next) { 311 next = t->timeouts_next; 312 313 if (all || t->dead) 314 destroy_timeout(t); 315 } 316 317 s->timeout_req_cleanup = 0; 318} 319 320AvahiSimplePoll *avahi_simple_poll_new(void) { 321 AvahiSimplePoll *s; 322 323 if (!(s = avahi_new(AvahiSimplePoll, 1))) 324 return NULL; 325 326 if (pipe(s->wakeup_pipe) < 0) { 327 avahi_free(s); 328 return NULL; 329 } 330 331 set_nonblock(s->wakeup_pipe[0]); 332 set_nonblock(s->wakeup_pipe[1]); 333 334 s->api.userdata = s; 335 336 s->api.watch_new = watch_new; 337 s->api.watch_free = watch_free; 338 s->api.watch_update = watch_update; 339 s->api.watch_get_events = watch_get_events; 340 341 s->api.timeout_new = timeout_new; 342 s->api.timeout_free = timeout_free; 343 s->api.timeout_update = timeout_update; 344 345 s->pollfds = NULL; 346 s->max_pollfds = s->n_pollfds = 0; 347 s->rebuild_pollfds = 1; 348 s->quit = 0; 349 s->n_watches = 0; 350 s->events_valid = 0; 351 352 s->watch_req_cleanup = 0; 353 s->timeout_req_cleanup = 0; 354 355 s->prepared_timeout = 0; 356 357 s->state = STATE_INIT; 358 359 s->wakeup_issued = 0; 360 361 avahi_simple_poll_set_func(s, NULL, NULL); 362 363 AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches); 364 AVAHI_LLIST_HEAD_INIT(AvahiTimeout, s->timeouts); 365 366 return s; 367} 368 369void avahi_simple_poll_free(AvahiSimplePoll *s) { 370 assert(s); 371 372 cleanup_timeouts(s, 1); 373 cleanup_watches(s, 1); 374 assert(s->n_watches == 0); 375 376 avahi_free(s->pollfds); 377 378 if (s->wakeup_pipe[0] >= 0) 379 close(s->wakeup_pipe[0]); 380 381 if (s->wakeup_pipe[1] >= 0) 382 close(s->wakeup_pipe[1]); 383 384 avahi_free(s); 385} 386 387static int rebuild(AvahiSimplePoll *s) { 388 AvahiWatch *w; 389 int idx; 390 391 assert(s); 392 393 if (s->n_watches+1 > s->max_pollfds) { 394 struct pollfd *n; 395 396 s->max_pollfds = s->n_watches + 10; 397 398 if (!(n = avahi_realloc(s->pollfds, sizeof(struct pollfd) * s->max_pollfds))) 399 return -1; 400 401 s->pollfds = n; 402 } 403 404 405 s->pollfds[0].fd = s->wakeup_pipe[0]; 406 s->pollfds[0].events = POLLIN; 407 s->pollfds[0].revents = 0; 408 409 idx = 1; 410 411 for (w = s->watches; w; w = w->watches_next) { 412 413 if(w->dead) 414 continue; 415 416 assert(w->idx < s->max_pollfds); 417 s->pollfds[w->idx = idx++] = w->pollfd; 418 } 419 420 s->n_pollfds = idx; 421 s->events_valid = 0; 422 s->rebuild_pollfds = 0; 423 424 return 0; 425} 426 427static AvahiTimeout* find_next_timeout(AvahiSimplePoll *s) { 428 AvahiTimeout *t, *n = NULL; 429 assert(s); 430 431 for (t = s->timeouts; t; t = t->timeouts_next) { 432 433 if (t->dead || !t->enabled) 434 continue; 435 436 if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0) 437 n = t; 438 } 439 440 return n; 441} 442 443static void timeout_callback(AvahiTimeout *t) { 444 assert(t); 445 assert(!t->dead); 446 assert(t->enabled); 447 448 t->enabled = 0; 449 t->callback(t, t->userdata); 450} 451 452int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout) { 453 AvahiTimeout *next_timeout; 454 455 assert(s); 456 assert(s->state == STATE_INIT || s->state == STATE_DISPATCHED || s->state == STATE_FAILURE); 457 s->state = STATE_PREPARING; 458 459 /* Clear pending wakeup requests */ 460 clear_wakeup(s); 461 462 /* Cleanup things first */ 463 if (s->watch_req_cleanup) 464 cleanup_watches(s, 0); 465 466 if (s->timeout_req_cleanup) 467 cleanup_timeouts(s, 0); 468 469 /* Check whether a quit was requested */ 470 if (s->quit) { 471 s->state = STATE_QUIT; 472 return 1; 473 } 474 475 /* Do we need to rebuild our array of pollfds? */ 476 if (s->rebuild_pollfds) 477 if (rebuild(s) < 0) { 478 s->state = STATE_FAILURE; 479 return -1; 480 } 481 482 /* Calculate the wakeup time */ 483 if ((next_timeout = find_next_timeout(s))) { 484 struct timeval now; 485 int t; 486 AvahiUsec usec; 487 488 if (next_timeout->expiry.tv_sec == 0 && 489 next_timeout->expiry.tv_usec == 0) { 490 491 /* Just a shortcut so that we don't need to call gettimeofday() */ 492 timeout = 0; 493 goto finish; 494 } 495 496 gettimeofday(&now, NULL); 497 usec = avahi_timeval_diff(&next_timeout->expiry, &now); 498 499 if (usec <= 0) { 500 /* Timeout elapsed */ 501 502 timeout = 0; 503 goto finish; 504 } 505 506 /* Calculate sleep time. We add 1ms because otherwise we'd 507 * wake up too early most of the time */ 508 t = (int) (usec / 1000) + 1; 509 510 if (timeout < 0 || timeout > t) 511 timeout = t; 512 } 513 514finish: 515 s->prepared_timeout = timeout; 516 s->state = STATE_PREPARED; 517 return 0; 518} 519 520int avahi_simple_poll_run(AvahiSimplePoll *s) { 521 assert(s); 522 assert(s->state == STATE_PREPARED || s->state == STATE_FAILURE); 523 524 s->state = STATE_RUNNING; 525 526 if (s->poll_func(s->pollfds, s->n_pollfds, s->prepared_timeout, s->poll_func_userdata) < 0) { 527 s->state = STATE_FAILURE; 528 return -1; 529 } 530 531 /* The poll events are now valid again */ 532 s->events_valid = 1; 533 534 /* Update state */ 535 s->state = STATE_RAN; 536 return 0; 537} 538 539int avahi_simple_poll_dispatch(AvahiSimplePoll *s) { 540 AvahiTimeout *next_timeout; 541 AvahiWatch *w; 542 543 assert(s); 544 assert(s->state == STATE_RAN); 545 s->state = STATE_DISPATCHING; 546 547 /* We execute only on callback in every iteration */ 548 549 /* Check whether the wakeup time has been reached now */ 550 if ((next_timeout = find_next_timeout(s))) { 551 552 if (next_timeout->expiry.tv_sec == 0 && next_timeout->expiry.tv_usec == 0) { 553 554 /* Just a shortcut so that we don't need to call gettimeofday() */ 555 timeout_callback(next_timeout); 556 goto finish; 557 } 558 559 if (avahi_age(&next_timeout->expiry) >= 0) { 560 561 /* Timeout elapsed */ 562 timeout_callback(next_timeout); 563 goto finish; 564 } 565 } 566 567 /* Look for some kind of I/O event */ 568 for (w = s->watches; w; w = w->watches_next) { 569 570 if (w->dead) 571 continue; 572 573 assert(w->idx >= 0); 574 assert(w->idx < s->n_pollfds); 575 576 if (s->pollfds[w->idx].revents != 0) { 577 w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata); 578 goto finish; 579 } 580 } 581 582finish: 583 584 s->state = STATE_DISPATCHED; 585 return 0; 586} 587 588int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) { 589 int r; 590 591 if ((r = avahi_simple_poll_prepare(s, timeout)) != 0) 592 return r; 593 594 if ((r = avahi_simple_poll_run(s)) != 0) 595 return r; 596 597 if ((r = avahi_simple_poll_dispatch(s)) != 0) 598 return r; 599 600 return 0; 601} 602 603void avahi_simple_poll_quit(AvahiSimplePoll *s) { 604 assert(s); 605 606 s->quit = 1; 607 608 /* If there is a background thread running the poll() for us, tell it to exit the poll() */ 609 avahi_simple_poll_wakeup(s); 610} 611 612const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) { 613 assert(s); 614 615 return &s->api; 616} 617 618static int system_poll(struct pollfd *ufds, unsigned int nfds, int timeout, AVAHI_GCC_UNUSED void *userdata) { 619 return poll(ufds, nfds, timeout); 620} 621 622void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata) { 623 assert(s); 624 625 s->poll_func = func ? func : system_poll; 626 s->poll_func_userdata = func ? userdata : NULL; 627 628 /* If there is a background thread running the poll() for us, tell it to exit the poll() */ 629 avahi_simple_poll_wakeup(s); 630} 631 632int avahi_simple_poll_loop(AvahiSimplePoll *s) { 633 int r; 634 635 assert(s); 636 637 for (;;) 638 if ((r = avahi_simple_poll_iterate(s, -1)) != 0) 639 if (r >= 0 || errno != EINTR) 640 return r; 641} 642