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