Annotation of embedaddon/bird/proto/bfd/io.c, revision 1.1
1.1 ! misho 1: /*
! 2: * BIRD -- I/O and event loop
! 3: *
! 4: * Can be freely distributed and used under the terms of the GNU GPL.
! 5: */
! 6:
! 7: #include <stdio.h>
! 8: #include <stdlib.h>
! 9: #include <unistd.h>
! 10: #include <errno.h>
! 11: #include <fcntl.h>
! 12: #include <poll.h>
! 13: #include <pthread.h>
! 14: #include <time.h>
! 15: #include <sys/time.h>
! 16:
! 17: #include "nest/bird.h"
! 18: #include "proto/bfd/io.h"
! 19:
! 20: #include "lib/buffer.h"
! 21: #include "lib/heap.h"
! 22: #include "lib/lists.h"
! 23: #include "lib/resource.h"
! 24: #include "lib/event.h"
! 25: #include "lib/socket.h"
! 26:
! 27:
! 28: struct birdloop
! 29: {
! 30: pool *pool;
! 31: pthread_t thread;
! 32: pthread_mutex_t mutex;
! 33:
! 34: btime last_time;
! 35: btime real_time;
! 36: u8 use_monotonic_clock;
! 37:
! 38: u8 stop_called;
! 39: u8 poll_active;
! 40: u8 wakeup_masked;
! 41: int wakeup_fds[2];
! 42:
! 43: BUFFER(timer2 *) timers;
! 44: list event_list;
! 45: list sock_list;
! 46: uint sock_num;
! 47:
! 48: BUFFER(sock *) poll_sk;
! 49: BUFFER(struct pollfd) poll_fd;
! 50: u8 poll_changed;
! 51: u8 close_scheduled;
! 52: };
! 53:
! 54:
! 55: /*
! 56: * Current thread context
! 57: */
! 58:
! 59: static pthread_key_t current_loop_key;
! 60:
! 61: static inline struct birdloop *
! 62: birdloop_current(void)
! 63: {
! 64: return pthread_getspecific(current_loop_key);
! 65: }
! 66:
! 67: static inline void
! 68: birdloop_set_current(struct birdloop *loop)
! 69: {
! 70: pthread_setspecific(current_loop_key, loop);
! 71: }
! 72:
! 73: static inline void
! 74: birdloop_init_current(void)
! 75: {
! 76: pthread_key_create(¤t_loop_key, NULL);
! 77: }
! 78:
! 79:
! 80: /*
! 81: * Time clock
! 82: */
! 83:
! 84: static void times_update_alt(struct birdloop *loop);
! 85:
! 86: static void
! 87: times_init(struct birdloop *loop)
! 88: {
! 89: struct timespec ts;
! 90: int rv;
! 91:
! 92: rv = clock_gettime(CLOCK_MONOTONIC, &ts);
! 93: if (rv < 0)
! 94: {
! 95: log(L_WARN "Monotonic clock is missing");
! 96:
! 97: loop->use_monotonic_clock = 0;
! 98: loop->last_time = 0;
! 99: loop->real_time = 0;
! 100: times_update_alt(loop);
! 101: return;
! 102: }
! 103:
! 104: if ((ts.tv_sec < 0) || (((s64) ts.tv_sec) > ((s64) 1 << 40)))
! 105: log(L_WARN "Monotonic clock is crazy");
! 106:
! 107: loop->use_monotonic_clock = 1;
! 108: loop->last_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000);
! 109: loop->real_time = 0;
! 110: }
! 111:
! 112: static void
! 113: times_update_pri(struct birdloop *loop)
! 114: {
! 115: struct timespec ts;
! 116: int rv;
! 117:
! 118: rv = clock_gettime(CLOCK_MONOTONIC, &ts);
! 119: if (rv < 0)
! 120: die("clock_gettime: %m");
! 121:
! 122: btime new_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000);
! 123:
! 124: if (new_time < loop->last_time)
! 125: log(L_ERR "Monotonic clock is broken");
! 126:
! 127: loop->last_time = new_time;
! 128: loop->real_time = 0;
! 129: }
! 130:
! 131: static void
! 132: times_update_alt(struct birdloop *loop)
! 133: {
! 134: struct timeval tv;
! 135: int rv;
! 136:
! 137: rv = gettimeofday(&tv, NULL);
! 138: if (rv < 0)
! 139: die("gettimeofday: %m");
! 140:
! 141: btime new_time = ((s64) tv.tv_sec S) + tv.tv_usec;
! 142: btime delta = new_time - loop->real_time;
! 143:
! 144: if ((delta < 0) || (delta > (60 S)))
! 145: {
! 146: if (loop->real_time)
! 147: log(L_WARN "Time jump, delta %d us", (int) delta);
! 148:
! 149: delta = 100 MS;
! 150: }
! 151:
! 152: loop->last_time += delta;
! 153: loop->real_time = new_time;
! 154: }
! 155:
! 156: static void
! 157: times_update(struct birdloop *loop)
! 158: {
! 159: if (loop->use_monotonic_clock)
! 160: times_update_pri(loop);
! 161: else
! 162: times_update_alt(loop);
! 163: }
! 164:
! 165: btime
! 166: current_time(void)
! 167: {
! 168: return birdloop_current()->last_time;
! 169: }
! 170:
! 171:
! 172: /*
! 173: * Wakeup code for birdloop
! 174: */
! 175:
! 176: static void
! 177: pipe_new(int *pfds)
! 178: {
! 179: int rv = pipe(pfds);
! 180: if (rv < 0)
! 181: die("pipe: %m");
! 182:
! 183: if (fcntl(pfds[0], F_SETFL, O_NONBLOCK) < 0)
! 184: die("fcntl(O_NONBLOCK): %m");
! 185:
! 186: if (fcntl(pfds[1], F_SETFL, O_NONBLOCK) < 0)
! 187: die("fcntl(O_NONBLOCK): %m");
! 188: }
! 189:
! 190: void
! 191: pipe_drain(int fd)
! 192: {
! 193: char buf[64];
! 194: int rv;
! 195:
! 196: try:
! 197: rv = read(fd, buf, 64);
! 198: if (rv < 0)
! 199: {
! 200: if (errno == EINTR)
! 201: goto try;
! 202: if (errno == EAGAIN)
! 203: return;
! 204: die("wakeup read: %m");
! 205: }
! 206: if (rv == 64)
! 207: goto try;
! 208: }
! 209:
! 210: void
! 211: pipe_kick(int fd)
! 212: {
! 213: u64 v = 1;
! 214: int rv;
! 215:
! 216: try:
! 217: rv = write(fd, &v, sizeof(u64));
! 218: if (rv < 0)
! 219: {
! 220: if (errno == EINTR)
! 221: goto try;
! 222: if (errno == EAGAIN)
! 223: return;
! 224: die("wakeup write: %m");
! 225: }
! 226: }
! 227:
! 228: static inline void
! 229: wakeup_init(struct birdloop *loop)
! 230: {
! 231: pipe_new(loop->wakeup_fds);
! 232: }
! 233:
! 234: static inline void
! 235: wakeup_drain(struct birdloop *loop)
! 236: {
! 237: pipe_drain(loop->wakeup_fds[0]);
! 238: }
! 239:
! 240: static inline void
! 241: wakeup_do_kick(struct birdloop *loop)
! 242: {
! 243: pipe_kick(loop->wakeup_fds[1]);
! 244: }
! 245:
! 246: static inline void
! 247: wakeup_kick(struct birdloop *loop)
! 248: {
! 249: if (!loop->wakeup_masked)
! 250: wakeup_do_kick(loop);
! 251: else
! 252: loop->wakeup_masked = 2;
! 253: }
! 254:
! 255:
! 256: /*
! 257: * Events
! 258: */
! 259:
! 260: static inline uint
! 261: events_waiting(struct birdloop *loop)
! 262: {
! 263: return !EMPTY_LIST(loop->event_list);
! 264: }
! 265:
! 266: static inline void
! 267: events_init(struct birdloop *loop)
! 268: {
! 269: init_list(&loop->event_list);
! 270: }
! 271:
! 272: static void
! 273: events_fire(struct birdloop *loop)
! 274: {
! 275: times_update(loop);
! 276: ev_run_list(&loop->event_list);
! 277: }
! 278:
! 279: void
! 280: ev2_schedule(event *e)
! 281: {
! 282: struct birdloop *loop = birdloop_current();
! 283:
! 284: if (loop->poll_active && EMPTY_LIST(loop->event_list))
! 285: wakeup_kick(loop);
! 286:
! 287: if (e->n.next)
! 288: rem_node(&e->n);
! 289:
! 290: add_tail(&loop->event_list, &e->n);
! 291: }
! 292:
! 293:
! 294: /*
! 295: * Timers
! 296: */
! 297:
! 298: #define TIMER_LESS(a,b) ((a)->expires < (b)->expires)
! 299: #define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \
! 300: heap[a]->index = (a), heap[b]->index = (b))
! 301:
! 302: static inline uint timers_count(struct birdloop *loop)
! 303: { return loop->timers.used - 1; }
! 304:
! 305: static inline timer2 *timers_first(struct birdloop *loop)
! 306: { return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
! 307:
! 308:
! 309: static void
! 310: tm2_free(resource *r)
! 311: {
! 312: timer2 *t = (timer2 *) r;
! 313:
! 314: tm2_stop(t);
! 315: }
! 316:
! 317: static void
! 318: tm2_dump(resource *r)
! 319: {
! 320: timer2 *t = (timer2 *) r;
! 321:
! 322: debug("(code %p, data %p, ", t->hook, t->data);
! 323: if (t->randomize)
! 324: debug("rand %d, ", t->randomize);
! 325: if (t->recurrent)
! 326: debug("recur %d, ", t->recurrent);
! 327: if (t->expires)
! 328: debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS);
! 329: else
! 330: debug("inactive)\n");
! 331: }
! 332:
! 333:
! 334: static struct resclass tm2_class = {
! 335: "Timer",
! 336: sizeof(timer2),
! 337: tm2_free,
! 338: tm2_dump,
! 339: NULL,
! 340: NULL
! 341: };
! 342:
! 343: timer2 *
! 344: tm2_new(pool *p)
! 345: {
! 346: timer2 *t = ralloc(p, &tm2_class);
! 347: t->index = -1;
! 348: return t;
! 349: }
! 350:
! 351: void
! 352: tm2_set(timer2 *t, btime when)
! 353: {
! 354: struct birdloop *loop = birdloop_current();
! 355: uint tc = timers_count(loop);
! 356:
! 357: if (!t->expires)
! 358: {
! 359: t->index = ++tc;
! 360: t->expires = when;
! 361: BUFFER_PUSH(loop->timers) = t;
! 362: HEAP_INSERT(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP);
! 363: }
! 364: else if (t->expires < when)
! 365: {
! 366: t->expires = when;
! 367: HEAP_INCREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
! 368: }
! 369: else if (t->expires > when)
! 370: {
! 371: t->expires = when;
! 372: HEAP_DECREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
! 373: }
! 374:
! 375: if (loop->poll_active && (t->index == 1))
! 376: wakeup_kick(loop);
! 377: }
! 378:
! 379: void
! 380: tm2_start(timer2 *t, btime after)
! 381: {
! 382: tm2_set(t, current_time() + MAX(after, 0));
! 383: }
! 384:
! 385: void
! 386: tm2_stop(timer2 *t)
! 387: {
! 388: if (!t->expires)
! 389: return;
! 390:
! 391: struct birdloop *loop = birdloop_current();
! 392: uint tc = timers_count(loop);
! 393:
! 394: HEAP_DELETE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
! 395: BUFFER_POP(loop->timers);
! 396:
! 397: t->index = -1;
! 398: t->expires = 0;
! 399: }
! 400:
! 401: static void
! 402: timers_init(struct birdloop *loop)
! 403: {
! 404: BUFFER_INIT(loop->timers, loop->pool, 4);
! 405: BUFFER_PUSH(loop->timers) = NULL;
! 406: }
! 407:
! 408: static void
! 409: timers_fire(struct birdloop *loop)
! 410: {
! 411: btime base_time;
! 412: timer2 *t;
! 413:
! 414: times_update(loop);
! 415: base_time = loop->last_time;
! 416:
! 417: while (t = timers_first(loop))
! 418: {
! 419: if (t->expires > base_time)
! 420: return;
! 421:
! 422: if (t->recurrent)
! 423: {
! 424: btime when = t->expires + t->recurrent;
! 425:
! 426: if (when <= loop->last_time)
! 427: when = loop->last_time + t->recurrent;
! 428:
! 429: if (t->randomize)
! 430: when += random() % (t->randomize + 1);
! 431:
! 432: tm2_set(t, when);
! 433: }
! 434: else
! 435: tm2_stop(t);
! 436:
! 437: t->hook(t);
! 438: }
! 439: }
! 440:
! 441:
! 442: /*
! 443: * Sockets
! 444: */
! 445:
! 446: static void
! 447: sockets_init(struct birdloop *loop)
! 448: {
! 449: init_list(&loop->sock_list);
! 450: loop->sock_num = 0;
! 451:
! 452: BUFFER_INIT(loop->poll_sk, loop->pool, 4);
! 453: BUFFER_INIT(loop->poll_fd, loop->pool, 4);
! 454: loop->poll_changed = 1; /* add wakeup fd */
! 455: }
! 456:
! 457: static void
! 458: sockets_add(struct birdloop *loop, sock *s)
! 459: {
! 460: add_tail(&loop->sock_list, &s->n);
! 461: loop->sock_num++;
! 462:
! 463: s->index = -1;
! 464: loop->poll_changed = 1;
! 465:
! 466: if (loop->poll_active)
! 467: wakeup_kick(loop);
! 468: }
! 469:
! 470: void
! 471: sk_start(sock *s)
! 472: {
! 473: struct birdloop *loop = birdloop_current();
! 474:
! 475: sockets_add(loop, s);
! 476: }
! 477:
! 478: static void
! 479: sockets_remove(struct birdloop *loop, sock *s)
! 480: {
! 481: rem_node(&s->n);
! 482: loop->sock_num--;
! 483:
! 484: if (s->index >= 0)
! 485: loop->poll_sk.data[s->index] = NULL;
! 486:
! 487: s->index = -1;
! 488: loop->poll_changed = 1;
! 489:
! 490: /* Wakeup moved to sk_stop() */
! 491: }
! 492:
! 493: void
! 494: sk_stop(sock *s)
! 495: {
! 496: struct birdloop *loop = birdloop_current();
! 497:
! 498: sockets_remove(loop, s);
! 499:
! 500: if (loop->poll_active)
! 501: {
! 502: loop->close_scheduled = 1;
! 503: wakeup_kick(loop);
! 504: }
! 505: else
! 506: close(s->fd);
! 507:
! 508: s->fd = -1;
! 509: }
! 510:
! 511: static inline uint sk_want_events(sock *s)
! 512: { return (s->rx_hook ? POLLIN : 0) | ((s->ttx != s->tpos) ? POLLOUT : 0); }
! 513:
! 514: /*
! 515: FIXME: this should be called from sock code
! 516:
! 517: static void
! 518: sockets_update(struct birdloop *loop, sock *s)
! 519: {
! 520: if (s->index >= 0)
! 521: loop->poll_fd.data[s->index].events = sk_want_events(s);
! 522: }
! 523: */
! 524:
! 525: static void
! 526: sockets_prepare(struct birdloop *loop)
! 527: {
! 528: BUFFER_SET(loop->poll_sk, loop->sock_num + 1);
! 529: BUFFER_SET(loop->poll_fd, loop->sock_num + 1);
! 530:
! 531: struct pollfd *pfd = loop->poll_fd.data;
! 532: sock **psk = loop->poll_sk.data;
! 533: int i = 0;
! 534: node *n;
! 535:
! 536: WALK_LIST(n, loop->sock_list)
! 537: {
! 538: sock *s = SKIP_BACK(sock, n, n);
! 539:
! 540: ASSERT(i < loop->sock_num);
! 541:
! 542: s->index = i;
! 543: *psk = s;
! 544: pfd->fd = s->fd;
! 545: pfd->events = sk_want_events(s);
! 546: pfd->revents = 0;
! 547:
! 548: pfd++;
! 549: psk++;
! 550: i++;
! 551: }
! 552:
! 553: ASSERT(i == loop->sock_num);
! 554:
! 555: /* Add internal wakeup fd */
! 556: *psk = NULL;
! 557: pfd->fd = loop->wakeup_fds[0];
! 558: pfd->events = POLLIN;
! 559: pfd->revents = 0;
! 560:
! 561: loop->poll_changed = 0;
! 562: }
! 563:
! 564: static void
! 565: sockets_close_fds(struct birdloop *loop)
! 566: {
! 567: struct pollfd *pfd = loop->poll_fd.data;
! 568: sock **psk = loop->poll_sk.data;
! 569: int poll_num = loop->poll_fd.used - 1;
! 570:
! 571: int i;
! 572: for (i = 0; i < poll_num; i++)
! 573: if (psk[i] == NULL)
! 574: close(pfd[i].fd);
! 575:
! 576: loop->close_scheduled = 0;
! 577: }
! 578:
! 579: int sk_read(sock *s, int revents);
! 580: int sk_write(sock *s);
! 581:
! 582: static void
! 583: sockets_fire(struct birdloop *loop)
! 584: {
! 585: struct pollfd *pfd = loop->poll_fd.data;
! 586: sock **psk = loop->poll_sk.data;
! 587: int poll_num = loop->poll_fd.used - 1;
! 588:
! 589: times_update(loop);
! 590:
! 591: /* Last fd is internal wakeup fd */
! 592: if (pfd[poll_num].revents & POLLIN)
! 593: wakeup_drain(loop);
! 594:
! 595: int i;
! 596: for (i = 0; i < poll_num; pfd++, psk++, i++)
! 597: {
! 598: int e = 1;
! 599:
! 600: if (! pfd->revents)
! 601: continue;
! 602:
! 603: if (pfd->revents & POLLNVAL)
! 604: die("poll: invalid fd %d", pfd->fd);
! 605:
! 606: if (pfd->revents & POLLIN)
! 607: while (e && *psk && (*psk)->rx_hook)
! 608: e = sk_read(*psk, 0);
! 609:
! 610: e = 1;
! 611: if (pfd->revents & POLLOUT)
! 612: while (e && *psk)
! 613: e = sk_write(*psk);
! 614: }
! 615: }
! 616:
! 617:
! 618: /*
! 619: * Birdloop
! 620: */
! 621:
! 622: static void * birdloop_main(void *arg);
! 623:
! 624: struct birdloop *
! 625: birdloop_new(void)
! 626: {
! 627: /* FIXME: this init should be elsewhere and thread-safe */
! 628: static int init = 0;
! 629: if (!init)
! 630: { birdloop_init_current(); init = 1; }
! 631:
! 632: pool *p = rp_new(NULL, "Birdloop root");
! 633: struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
! 634: loop->pool = p;
! 635: pthread_mutex_init(&loop->mutex, NULL);
! 636:
! 637: times_init(loop);
! 638: wakeup_init(loop);
! 639:
! 640: events_init(loop);
! 641: timers_init(loop);
! 642: sockets_init(loop);
! 643:
! 644: return loop;
! 645: }
! 646:
! 647: void
! 648: birdloop_start(struct birdloop *loop)
! 649: {
! 650: int rv = pthread_create(&loop->thread, NULL, birdloop_main, loop);
! 651: if (rv)
! 652: die("pthread_create(): %M", rv);
! 653: }
! 654:
! 655: void
! 656: birdloop_stop(struct birdloop *loop)
! 657: {
! 658: pthread_mutex_lock(&loop->mutex);
! 659: loop->stop_called = 1;
! 660: wakeup_do_kick(loop);
! 661: pthread_mutex_unlock(&loop->mutex);
! 662:
! 663: int rv = pthread_join(loop->thread, NULL);
! 664: if (rv)
! 665: die("pthread_join(): %M", rv);
! 666: }
! 667:
! 668: void
! 669: birdloop_free(struct birdloop *loop)
! 670: {
! 671: rfree(loop->pool);
! 672: }
! 673:
! 674:
! 675: void
! 676: birdloop_enter(struct birdloop *loop)
! 677: {
! 678: /* TODO: these functions could save and restore old context */
! 679: pthread_mutex_lock(&loop->mutex);
! 680: birdloop_set_current(loop);
! 681: }
! 682:
! 683: void
! 684: birdloop_leave(struct birdloop *loop)
! 685: {
! 686: /* TODO: these functions could save and restore old context */
! 687: birdloop_set_current(NULL);
! 688: pthread_mutex_unlock(&loop->mutex);
! 689: }
! 690:
! 691: void
! 692: birdloop_mask_wakeups(struct birdloop *loop)
! 693: {
! 694: pthread_mutex_lock(&loop->mutex);
! 695: loop->wakeup_masked = 1;
! 696: pthread_mutex_unlock(&loop->mutex);
! 697: }
! 698:
! 699: void
! 700: birdloop_unmask_wakeups(struct birdloop *loop)
! 701: {
! 702: pthread_mutex_lock(&loop->mutex);
! 703: if (loop->wakeup_masked == 2)
! 704: wakeup_do_kick(loop);
! 705: loop->wakeup_masked = 0;
! 706: pthread_mutex_unlock(&loop->mutex);
! 707: }
! 708:
! 709: static void *
! 710: birdloop_main(void *arg)
! 711: {
! 712: struct birdloop *loop = arg;
! 713: timer2 *t;
! 714: int rv, timeout;
! 715:
! 716: birdloop_set_current(loop);
! 717:
! 718: pthread_mutex_lock(&loop->mutex);
! 719: while (1)
! 720: {
! 721: events_fire(loop);
! 722: timers_fire(loop);
! 723:
! 724: times_update(loop);
! 725: if (events_waiting(loop))
! 726: timeout = 0;
! 727: else if (t = timers_first(loop))
! 728: timeout = (tm2_remains(t) TO_MS) + 1;
! 729: else
! 730: timeout = -1;
! 731:
! 732: if (loop->poll_changed)
! 733: sockets_prepare(loop);
! 734:
! 735: loop->poll_active = 1;
! 736: pthread_mutex_unlock(&loop->mutex);
! 737:
! 738: try:
! 739: rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
! 740: if (rv < 0)
! 741: {
! 742: if (errno == EINTR || errno == EAGAIN)
! 743: goto try;
! 744: die("poll: %m");
! 745: }
! 746:
! 747: pthread_mutex_lock(&loop->mutex);
! 748: loop->poll_active = 0;
! 749:
! 750: if (loop->close_scheduled)
! 751: sockets_close_fds(loop);
! 752:
! 753: if (loop->stop_called)
! 754: break;
! 755:
! 756: if (rv)
! 757: sockets_fire(loop);
! 758:
! 759: timers_fire(loop);
! 760: }
! 761:
! 762: loop->stop_called = 0;
! 763: pthread_mutex_unlock(&loop->mutex);
! 764:
! 765: return NULL;
! 766: }
! 767:
! 768:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>