diff options
| -rw-r--r-- | avahi-common/simple-watch.c | 254 | ||||
| -rw-r--r-- | avahi-common/simple-watch.h | 19 | ||||
| -rw-r--r-- | avahi-common/watch-test.c | 7 | 
3 files changed, 194 insertions, 86 deletions
diff --git a/avahi-common/simple-watch.c b/avahi-common/simple-watch.c index 6872389..b8a1b82 100644 --- a/avahi-common/simple-watch.c +++ b/avahi-common/simple-watch.c @@ -29,6 +29,7 @@  #include <errno.h>  #include <unistd.h>  #include <fcntl.h> +#include <stdio.h>  #include <avahi-common/llist.h>  #include <avahi-common/malloc.h> @@ -64,6 +65,7 @@ struct AvahiTimeout {  struct AvahiSimplePoll {      AvahiPoll api;      AvahiPollFunc poll_func; +    void *poll_func_userdata;      struct pollfd* pollfds;      int n_pollfds, max_pollfds, rebuild_pollfds; @@ -77,18 +79,39 @@ struct AvahiSimplePoll {      AVAHI_LLIST_HEAD(AvahiTimeout, timeouts);      int wakeup_pipe[2]; +    int wakeup_issued; + +    int prepared_timeout; + +    enum { +        STATE_INIT, +        STATE_PREPARING, +        STATE_PREPARED, +        STATE_RUNNING, +        STATE_RAN, +        STATE_DISPATCHING, +        STATE_DISPATCHED, +        STATE_QUIT, +        STATE_FAILURE +    } state;  }; -static void wakeup(AvahiSimplePoll *s) { -    char c = 'x'; +void avahi_simple_poll_wakeup(AvahiSimplePoll *s) { +    char c = 'W';      assert(s);      write(s->wakeup_pipe[1], &c, sizeof(c)); +    s->wakeup_issued = 1;  }  static void clear_wakeup(AvahiSimplePoll *s) {      char c[10]; /* Read ten at a time */ +    if (!s->wakeup_issued) +        return; + +    s->wakeup_issued = 0; +          for(;;)          if (read(s->wakeup_pipe[0], &c, sizeof(c)) != sizeof(c))              break; @@ -121,6 +144,9 @@ static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event      if (!(w = avahi_new(AvahiWatch, 1)))          return NULL; + +    /* If there is a background thread running the poll() for us, tell it to exit the poll() */ +    avahi_simple_poll_wakeup(s);      w->simple_poll = s;      w->dead = 0; @@ -131,22 +157,12 @@ static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event      w->callback = callback;      w->userdata = userdata; -    if (s->n_pollfds < s->max_pollfds) { -        /* If there's space for this pollfd, go on and allocate it */ -        w->idx = s->n_pollfds++; -        s->pollfds[w->idx] = w->pollfd; -         -    } else { -        /* Unfortunately there's no place for this pollfd, so request a rebuild of the array */ -        w->idx = -1; -        s->rebuild_pollfds = 1; -    } +    w->idx = -1; +    s->rebuild_pollfds = 1;      AVAHI_LLIST_PREPEND(AvahiWatch, watches, s->watches, w);      s->n_watches++; -    wakeup(w->simple_poll); -      return w;  } @@ -154,6 +170,9 @@ static void watch_update(AvahiWatch *w, AvahiWatchEvent events) {      assert(w);      assert(!w->dead); +    /* If there is a background thread running the poll() for us, tell it to exit the poll() */ +    avahi_simple_poll_wakeup(w->simple_poll); +      w->pollfd.events = events;      if (w->idx != -1) { @@ -161,8 +180,6 @@ static void watch_update(AvahiWatch *w, AvahiWatchEvent events) {          w->simple_poll->pollfds[w->idx] = w->pollfd;      } else          w->simple_poll->rebuild_pollfds = 1; - -    wakeup(w->simple_poll);  }  static AvahiWatchEvent watch_get_events(AvahiWatch *w) { @@ -181,16 +198,7 @@ static void remove_pollfd(AvahiWatch *w) {      if (w->idx == -1)          return; -    if (w->idx == w->simple_poll->n_pollfds-1) { - -        /* This pollfd is at the end of the array, so we can easily cut it */ - -        assert(w->simple_poll->n_pollfds > 0); -        w->simple_poll->n_pollfds -= 1; -    } else - -        /* Unfortunately this pollfd is in the middle of the array, so request a rebuild of it */ -        w->simple_poll->rebuild_pollfds = 1; +    w->simple_poll->rebuild_pollfds = 1;  }  static void watch_free(AvahiWatch *w) { @@ -198,13 +206,14 @@ static void watch_free(AvahiWatch *w) {      assert(!w->dead); +    /* If there is a background thread running the poll() for us, tell it to exit the poll() */ +    avahi_simple_poll_wakeup(w->simple_poll); +          remove_pollfd(w);      w->dead = 1;      w->simple_poll->n_watches --;      w->simple_poll->watch_req_cleanup = 1; - -    wakeup(w->simple_poll);  }  static void destroy_watch(AvahiWatch *w) { @@ -245,6 +254,9 @@ static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv,      if (!(t = avahi_new(AvahiTimeout, 1)))          return NULL; + +    /* If there is a background thread running the poll() for us, tell it to exit the poll() */ +    avahi_simple_poll_wakeup(s);      t->simple_poll = s;      t->dead = 0; @@ -256,29 +268,29 @@ static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv,      t->userdata = userdata;      AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, s->timeouts, t); - -    wakeup(t->simple_poll);      return t;  }  static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {      assert(t);      assert(!t->dead); + +    /* If there is a background thread running the poll() for us, tell it to exit the poll() */ +    avahi_simple_poll_wakeup(t->simple_poll);      if ((t->enabled = !!tv))          t->expiry = *tv; - -    wakeup(t->simple_poll);  }  static void timeout_free(AvahiTimeout *t) {      assert(t);      assert(!t->dead); +    /* If there is a background thread running the poll() for us, tell it to exit the poll() */ +    avahi_simple_poll_wakeup(t->simple_poll); +      t->dead = 1;      t->simple_poll->timeout_req_cleanup = 1; - -    wakeup(t->simple_poll);  } @@ -331,7 +343,7 @@ AvahiSimplePoll *avahi_simple_poll_new(void) {      s->pollfds = NULL;      s->max_pollfds = s->n_pollfds = 0; -    s->rebuild_pollfds = 0; +    s->rebuild_pollfds = 1;      s->quit = 0;      s->n_watches = 0;      s->events_valid = 0; @@ -339,7 +351,13 @@ AvahiSimplePoll *avahi_simple_poll_new(void) {      s->watch_req_cleanup = 0;      s->timeout_req_cleanup = 0; -    avahi_simple_poll_set_func(s, NULL); +    s->prepared_timeout = 0; + +    s->state = STATE_INIT; + +    s->wakeup_issued = 0; +     +    avahi_simple_poll_set_func(s, NULL, NULL);      AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches);      AVAHI_LLIST_HEAD_INIT(AvahiTimeout, s->timeouts); @@ -386,8 +404,10 @@ static int rebuild(AvahiSimplePoll *s) {      s->pollfds[0].fd = s->wakeup_pipe[0];      s->pollfds[0].events = POLLIN;      s->pollfds[0].revents = 0; + +    idx = 1; -    for (idx = 1, w = s->watches; w; w = w->watches_next) { +    for (w = s->watches; w; w = w->watches_next) {          if(w->dead)              continue; @@ -419,21 +439,22 @@ static AvahiTimeout* find_next_timeout(AvahiSimplePoll *s) {      return n;  } -static int start_timeout_callback(AvahiTimeout *t) { +static void timeout_callback(AvahiTimeout *t) {      assert(t);      assert(!t->dead);      assert(t->enabled);      t->enabled = 0;      t->callback(t, t->userdata); -    return 0;  } -int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) { -    int r; +int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout) {      AvahiTimeout *next_timeout; +          assert(s); - +    assert(s->state == STATE_INIT || s->state == STATE_DISPATCHED || s->state == STATE_FAILURE); +    s->state = STATE_PREPARING; +          /* Clear pending wakeup requests */      clear_wakeup(s); @@ -445,13 +466,17 @@ int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) {          cleanup_timeouts(s, 0);      /* Check whether a quit was requested */ -    if (s->quit) +    if (s->quit) { +        s->state = STATE_QUIT;          return 1; +    }      /* Do we need to rebuild our array of pollfds? */      if (s->rebuild_pollfds) -        if (rebuild(s) < 0) +        if (rebuild(s) < 0) { +            s->state = STATE_FAILURE;              return -1; +        }      /* Calculate the wakeup time */      if ((next_timeout = find_next_timeout(s))) { @@ -463,12 +488,9 @@ int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) {              next_timeout->expiry.tv_usec == 0) {              /* Just a shortcut so that we don't need to call gettimeofday() */ -             -            /* The events poll() returned in the last call are now no longer valid */ -            s->events_valid = 0; -            return start_timeout_callback(next_timeout); +            timeout = 0; +            goto finish;          } -          gettimeofday(&now, NULL);          usec = avahi_timeval_diff(&next_timeout->expiry, &now); @@ -476,9 +498,8 @@ int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) {          if (usec <= 0) {              /* Timeout elapsed */ -            /* The events poll() returned in the last call are now no longer valid */ -            s->events_valid = 0; -            return start_timeout_callback(next_timeout); +            timeout = 0; +            goto finish;          }          /* Calculate sleep time. We add 1ms because otherwise we'd @@ -489,44 +510,96 @@ int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) {              timeout = t;      } -    if ((r = s->poll_func(s->pollfds, s->n_pollfds, timeout)) < 0) -        return -1; +finish: +    s->prepared_timeout = timeout; +    s->state = STATE_PREPARED; +    return 0; +} + +int avahi_simple_poll_run(AvahiSimplePoll *s) { +    assert(s); +    assert(s->state == STATE_PREPARED); +     +    s->state = STATE_RUNNING; + +    if (s->prepared_timeout != 0) { +     +        if (s->poll_func(s->pollfds, s->n_pollfds, s->prepared_timeout, s->poll_func_userdata) < 0) { +            s->state = STATE_FAILURE; +            return -1; +        } +         +        /* The poll events are now valid again */ +        s->events_valid = 1; +    } else +        s->events_valid = 0; +         +    /* Update state */ +    s->state = STATE_RAN; +    return 0; +} + +int avahi_simple_poll_dispatch(AvahiSimplePoll *s) { +    AvahiTimeout *next_timeout; +    AvahiWatch *w; -    /* The pollf events are now valid again */ -    s->events_valid = 1; +    assert(s); +    assert(s->state == STATE_RAN); +    s->state = STATE_DISPATCHING; +    /* We execute only on callback in every iteration */ +          /* Check whether the wakeup time has been reached now */      if ((next_timeout = find_next_timeout(s))) { -        struct timeval now; -        gettimeofday(&now, NULL); +        if (next_timeout->expiry.tv_sec == 0 && next_timeout->expiry.tv_usec == 0) { + +            /* Just a shortcut so that we don't need to call gettimeofday() */ +            timeout_callback(next_timeout); +            goto finish; +        } +         +        if (avahi_age(&next_timeout->expiry) >= 0) { -        if (avahi_timeval_compare(&next_timeout->expiry, &now) <= 0) -            /* Time elapsed */ -            return start_timeout_callback(next_timeout); +            /* Timeout elapsed */ +            timeout_callback(next_timeout); +            goto finish; +        }      } -     -    if (r > 0) { -        AvahiWatch *w; -        /* Look for some kind of I/O event */ +    /* Look for some kind of I/O event */ +    for (w = s->watches; w; w = w->watches_next) { +         +        if (w->dead) +            continue; +         +        assert(w->idx >= 0); +        assert(w->idx < s->n_pollfds); +         +        if (s->pollfds[w->idx].revents != 0) { +            w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata); +            goto finish; +        } +    } -        for (w = s->watches; w; w = w->watches_next) { +finish: -            if (w->dead) -                continue; +    s->state = STATE_DISPATCHED; +    return 0; +} -            assert(w->idx >= 0); -            assert(w->idx < s->n_pollfds); +int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) { +    int r; -            if (s->pollfds[w->idx].revents > 0) { -                /* We execute only on callback in every iteration */ -                w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata); -                return 0; -            } -        } -    } +    if ((r = avahi_simple_poll_prepare(s, timeout)) != 0) +        return r; + +    if ((r = avahi_simple_poll_run(s)) != 0) +        return r; +    if ((r = avahi_simple_poll_dispatch(s)) != 0) +        return r; +          return 0;  } @@ -534,7 +607,9 @@ void avahi_simple_poll_quit(AvahiSimplePoll *s) {      assert(s);      s->quit = 1; -    wakeup(s); + +    /* If there is a background thread running the poll() for us, tell it to exit the poll() */ +    avahi_simple_poll_wakeup(s);  }  const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) { @@ -543,9 +618,26 @@ const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) {      return &s->api;  } -void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func) { +static int system_poll(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) { +    return poll(ufds, nfds, timeout); +} + +void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata) {      assert(s); -    s->poll_func = func ? func : (AvahiPollFunc) poll; -    wakeup(s); +    s->poll_func = func ? func : system_poll; +    s->poll_func_userdata = func ? userdata : NULL; + +    /* If there is a background thread running the poll() for us, tell it to exit the poll() */ +    avahi_simple_poll_wakeup(s); +} + +int avahi_simple_poll_loop(AvahiSimplePoll *s) { +    int r; + +    assert(s); +     +    for (;;) +        if ((r = avahi_simple_poll_iterate(s, -1)) != 0) +            return r;  } diff --git a/avahi-common/simple-watch.h b/avahi-common/simple-watch.h index c8234f2..0833570 100644 --- a/avahi-common/simple-watch.h +++ b/avahi-common/simple-watch.h @@ -65,10 +65,25 @@ int avahi_simple_poll_iterate(AvahiSimplePoll *s, int sleep_time);  void avahi_simple_poll_quit(AvahiSimplePoll *s);  /** Prototype for a poll() type function */ -typedef int (*AvahiPollFunc)(struct pollfd *ufds, unsigned int nfds, int timeout); +typedef int (*AvahiPollFunc)(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata);  /** Replace the internally used poll() function. By default the system's poll() will be used */ -void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func); +void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata); + +/** The first stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout); + +/** The second stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_run(AvahiSimplePoll *s); + +/** The third and final stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_dispatch(AvahiSimplePoll *s); + +/** Call avahi_simple_poll_iterate() in a loop and return if it returns non-zero */ +int avahi_simple_poll_loop(AvahiSimplePoll *s); + +/** Wakeup the main loop. (for threaded environments) */ +void avahi_simple_poll_wakeup(AvahiSimplePoll *s);  #ifndef DOXYGEN_SHOULD_SKIP_THIS  AVAHI_C_DECL_END diff --git a/avahi-common/watch-test.c b/avahi-common/watch-test.c index ffb2149..921a8f7 100644 --- a/avahi-common/watch-test.c +++ b/avahi-common/watch-test.c @@ -78,9 +78,10 @@ int main(int argc, char *argv[]) {      api->timeout_new(api, &tv, wakeup, NULL);      /* Our main loop */ -    for (;;) -        if (avahi_simple_poll_iterate(simple_poll, -1) != 0) -            break; +    avahi_simple_poll_loop(simple_poll); + + +    avahi_simple_poll_free(simple_poll);      return 0;  }  | 
