]> git.meshlink.io Git - catta/blob - avahi-common/simple-watch.c
aab71f68c07c58d2e62b6c058eda4f51ac13ed8c
[catta] / avahi-common / simple-watch.c
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
39 struct 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
52 struct 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
65 struct 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
99 void 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
107 static 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
120 static 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
134 static 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
170 static 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
186 static 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
196 static 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
205 static 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
220 static 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
232 static 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
246 static 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
275 static 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
286 static 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
298 static 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
306 static 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
320 AvahiSimplePoll *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
369 void 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
387 static 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
427 static 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
443 static 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
452 int 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
514 finish:
515     s->prepared_timeout = timeout;
516     s->state = STATE_PREPARED;
517     return 0;
518 }
519
520 int 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
539 int 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
582 finish:
583
584     s->state = STATE_DISPATCHED;
585     return 0;
586 }
587
588 int 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
603 void 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
612 const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) {
613     assert(s);
614
615     return &s->api;
616 }
617
618 static int system_poll(struct pollfd *ufds, unsigned int nfds, int timeout, AVAHI_GCC_UNUSED void *userdata) {
619     return poll(ufds, nfds, timeout);
620 }
621
622 void 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
632 int 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 }