]> git.meshlink.io Git - catta/blob - avahi-common/simple-watch.c
* Use a recursive mutex for locking DNSServiceRef
[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 <avahi-common/llist.h>
35 #include <avahi-common/malloc.h>
36
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
157     w->callback = callback;
158     w->userdata = userdata;
159
160     w->idx = -1;
161     s->rebuild_pollfds = 1;
162
163     AVAHI_LLIST_PREPEND(AvahiWatch, watches, s->watches, w);
164     s->n_watches++;
165
166     return w;
167 }
168
169 static void watch_update(AvahiWatch *w, AvahiWatchEvent events) {
170     assert(w);
171     assert(!w->dead);
172
173     /* If there is a background thread running the poll() for us, tell it to exit the poll() */
174     avahi_simple_poll_wakeup(w->simple_poll);
175
176     w->pollfd.events = events;
177
178     if (w->idx != -1) {
179         assert(w->simple_poll);
180         w->simple_poll->pollfds[w->idx] = w->pollfd;
181     } else
182         w->simple_poll->rebuild_pollfds = 1;
183 }
184
185 static AvahiWatchEvent watch_get_events(AvahiWatch *w) {
186     assert(w);
187     assert(!w->dead);
188
189     if (w->idx != -1 && w->simple_poll->events_valid)
190         return w->simple_poll->pollfds[w->idx].revents;
191
192     return 0;
193 }
194
195 static void remove_pollfd(AvahiWatch *w) {
196     assert(w);
197
198     if (w->idx == -1)
199         return;
200     
201     w->simple_poll->rebuild_pollfds = 1;
202 }
203
204 static void watch_free(AvahiWatch *w) {
205     assert(w);
206
207     assert(!w->dead);
208
209     /* If there is a background thread running the poll() for us, tell it to exit the poll() */
210     avahi_simple_poll_wakeup(w->simple_poll);
211     
212     remove_pollfd(w);
213     
214     w->dead = 1;
215     w->simple_poll->n_watches --;
216     w->simple_poll->watch_req_cleanup = 1;
217 }
218
219 static void destroy_watch(AvahiWatch *w) {
220     assert(w);
221
222     remove_pollfd(w);
223     AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->simple_poll->watches, w);
224
225     if (!w->dead)
226         w->simple_poll->n_watches --;
227     
228     avahi_free(w);
229 }
230
231 static void cleanup_watches(AvahiSimplePoll *s, int all) {
232     AvahiWatch *w, *next;
233     assert(s);
234
235     for (w = s->watches; w; w = next) {
236         next = w->watches_next;
237
238         if (all || w->dead)
239             destroy_watch(w);
240     }
241
242     s->timeout_req_cleanup = 0;
243 }
244
245 static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) {
246     AvahiTimeout *t;
247     AvahiSimplePoll *s;
248     
249     assert(api);
250     assert(callback);
251
252     s = api->userdata;
253     assert(s);
254
255     if (!(t = avahi_new(AvahiTimeout, 1)))
256         return NULL;
257
258     /* If there is a background thread running the poll() for us, tell it to exit the poll() */
259     avahi_simple_poll_wakeup(s);
260     
261     t->simple_poll = s;
262     t->dead = 0;
263
264     if ((t->enabled = !!tv))
265         t->expiry = *tv;
266         
267     t->callback = callback;
268     t->userdata = userdata;
269
270     AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, s->timeouts, t);
271     return t;
272 }
273
274 static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
275     assert(t);
276     assert(!t->dead);
277
278     /* If there is a background thread running the poll() for us, tell it to exit the poll() */
279     avahi_simple_poll_wakeup(t->simple_poll);
280     
281     if ((t->enabled = !!tv))
282         t->expiry = *tv;
283 }
284
285 static void timeout_free(AvahiTimeout *t) {
286     assert(t);
287     assert(!t->dead);
288
289     /* If there is a background thread running the poll() for us, tell it to exit the poll() */
290     avahi_simple_poll_wakeup(t->simple_poll);
291
292     t->dead = 1;
293     t->simple_poll->timeout_req_cleanup = 1;
294 }
295
296
297 static void destroy_timeout(AvahiTimeout *t) {
298     assert(t);
299
300     AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->simple_poll->timeouts, t);
301
302     avahi_free(t);
303 }
304
305 static void cleanup_timeouts(AvahiSimplePoll *s, int all) {
306     AvahiTimeout *t, *next;
307     assert(s);
308
309     for (t = s->timeouts; t; t = next) {
310         next = t->timeouts_next;
311
312         if (all || t->dead)
313             destroy_timeout(t);
314     }
315
316     s->timeout_req_cleanup = 0;
317 }
318
319 AvahiSimplePoll *avahi_simple_poll_new(void) {
320     AvahiSimplePoll *s;
321
322     if (!(s = avahi_new(AvahiSimplePoll, 1)))
323         return NULL;
324
325     if (pipe(s->wakeup_pipe) < 0) {
326         avahi_free(s);
327         return NULL;
328     }
329
330     set_nonblock(s->wakeup_pipe[0]);
331     set_nonblock(s->wakeup_pipe[1]);
332     
333     s->api.userdata = s;
334
335     s->api.watch_new = watch_new;
336     s->api.watch_free = watch_free;
337     s->api.watch_update = watch_update;
338     s->api.watch_get_events = watch_get_events;
339
340     s->api.timeout_new = timeout_new;
341     s->api.timeout_free = timeout_free;
342     s->api.timeout_update = timeout_update;
343     
344     s->pollfds = NULL;
345     s->max_pollfds = s->n_pollfds = 0;
346     s->rebuild_pollfds = 1;
347     s->quit = 0;
348     s->n_watches = 0;
349     s->events_valid = 0;
350     
351     s->watch_req_cleanup = 0;
352     s->timeout_req_cleanup = 0;
353
354     s->prepared_timeout = 0;
355
356     s->state = STATE_INIT;
357
358     s->wakeup_issued = 0;
359     
360     avahi_simple_poll_set_func(s, NULL, NULL);
361
362     AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches);
363     AVAHI_LLIST_HEAD_INIT(AvahiTimeout, s->timeouts);
364
365     return s;
366 }
367
368 void avahi_simple_poll_free(AvahiSimplePoll *s) {
369     assert(s);
370
371     cleanup_timeouts(s, 1);
372     cleanup_watches(s, 1);
373     assert(s->n_watches == 0);
374     
375     avahi_free(s->pollfds);
376
377     if (s->wakeup_pipe[0] >= 0)
378         close(s->wakeup_pipe[0]);
379
380     if (s->wakeup_pipe[1] >= 0)
381         close(s->wakeup_pipe[1]);
382         
383     avahi_free(s);
384 }
385
386 static int rebuild(AvahiSimplePoll *s) {
387     AvahiWatch *w;
388     int idx;
389     
390     assert(s);
391
392     if (s->n_watches+1 > s->max_pollfds) {
393         struct pollfd *n;
394
395         s->max_pollfds = s->n_watches + 10;
396         
397         if (!(n = avahi_realloc(s->pollfds, sizeof(struct pollfd) * s->max_pollfds)))
398             return -1;
399
400         s->pollfds = n;
401     }
402
403     
404     s->pollfds[0].fd = s->wakeup_pipe[0];
405     s->pollfds[0].events = POLLIN;
406     s->pollfds[0].revents = 0;
407
408     idx = 1;
409     
410     for (w = s->watches; w; w = w->watches_next) {
411
412         if(w->dead)
413             continue;
414
415         assert(w->idx < s->max_pollfds);
416         s->pollfds[w->idx = idx++] = w->pollfd;
417     }
418
419     s->n_pollfds = idx;
420     s->events_valid = 0;
421     s->rebuild_pollfds = 0;
422
423     return 0;
424 }
425
426 static AvahiTimeout* find_next_timeout(AvahiSimplePoll *s) {
427     AvahiTimeout *t, *n = NULL;
428     assert(s);
429
430     for (t = s->timeouts; t; t = t->timeouts_next) {
431         
432         if (t->dead || !t->enabled)
433             continue;
434         
435         if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0)
436             n = t;
437     }
438
439     return n;
440 }
441
442 static void timeout_callback(AvahiTimeout *t) {
443     assert(t);
444     assert(!t->dead);
445     assert(t->enabled);
446
447     t->enabled = 0;
448     t->callback(t, t->userdata);
449 }
450
451 int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout) {
452     AvahiTimeout *next_timeout;
453     
454     assert(s);
455     assert(s->state == STATE_INIT || s->state == STATE_DISPATCHED || s->state == STATE_FAILURE);
456     s->state = STATE_PREPARING;
457     
458     /* Clear pending wakeup requests */
459     clear_wakeup(s);
460
461     /* Cleanup things first */
462     if (s->watch_req_cleanup)
463         cleanup_watches(s, 0);
464
465     if (s->timeout_req_cleanup)
466         cleanup_timeouts(s, 0);
467
468     /* Check whether a quit was requested */
469     if (s->quit) {
470         s->state = STATE_QUIT;
471         return 1;
472     }
473
474     /* Do we need to rebuild our array of pollfds? */
475     if (s->rebuild_pollfds)
476         if (rebuild(s) < 0) {
477             s->state = STATE_FAILURE;
478             return -1;
479         }
480
481     /* Calculate the wakeup time */
482     if ((next_timeout = find_next_timeout(s))) {
483         struct timeval now;
484         int t;
485         AvahiUsec usec;
486
487         if (next_timeout->expiry.tv_sec == 0 &&
488             next_timeout->expiry.tv_usec == 0) {
489
490             /* Just a shortcut so that we don't need to call gettimeofday() */
491             timeout = 0;
492             goto finish;
493         }
494             
495         gettimeofday(&now, NULL);
496         usec = avahi_timeval_diff(&next_timeout->expiry, &now);
497
498         if (usec <= 0) {
499             /* Timeout elapsed */
500
501             timeout = 0;
502             goto finish;
503         }
504
505         /* Calculate sleep time. We add 1ms because otherwise we'd
506          * wake up too early most of the time */
507         t = (int) (usec / 1000) + 1;
508
509         if (timeout < 0 || timeout > t)
510             timeout = t;
511     }
512
513 finish:
514     s->prepared_timeout = timeout;
515     s->state = STATE_PREPARED;
516     return 0;
517 }
518
519 int avahi_simple_poll_run(AvahiSimplePoll *s) {
520     assert(s);
521     assert(s->state == STATE_PREPARED);
522     
523     s->state = STATE_RUNNING;
524
525     if (s->prepared_timeout != 0) {
526     
527         if (s->poll_func(s->pollfds, s->n_pollfds, s->prepared_timeout, s->poll_func_userdata) < 0) {
528             s->state = STATE_FAILURE;
529             return -1;
530         }
531         
532         /* The poll events are now valid again */
533         s->events_valid = 1;
534     } else
535         s->events_valid = 0;
536         
537     /* Update state */
538     s->state = STATE_RAN;
539     return 0;
540 }
541
542 int avahi_simple_poll_dispatch(AvahiSimplePoll *s) {
543     AvahiTimeout *next_timeout;
544     AvahiWatch *w;
545
546     assert(s);
547     assert(s->state == STATE_RAN);
548     s->state = STATE_DISPATCHING;
549
550     /* We execute only on callback in every iteration */
551     
552     /* Check whether the wakeup time has been reached now */
553     if ((next_timeout = find_next_timeout(s))) {
554         
555         if (next_timeout->expiry.tv_sec == 0 && next_timeout->expiry.tv_usec == 0) {
556
557             /* Just a shortcut so that we don't need to call gettimeofday() */
558             timeout_callback(next_timeout);
559             goto finish;
560         }
561         
562         if (avahi_age(&next_timeout->expiry) >= 0) {
563
564             /* Timeout elapsed */
565             timeout_callback(next_timeout);
566             goto finish;
567         }
568     }
569
570     /* Look for some kind of I/O event */
571     for (w = s->watches; w; w = w->watches_next) {
572         
573         if (w->dead)
574             continue;
575         
576         assert(w->idx >= 0);
577         assert(w->idx < s->n_pollfds);
578         
579         if (s->pollfds[w->idx].revents != 0) {
580             w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata);
581             goto finish;
582         }
583     }
584
585 finish:
586
587     s->state = STATE_DISPATCHED;
588     return 0;
589 }
590
591 int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) {
592     int r;
593
594     if ((r = avahi_simple_poll_prepare(s, timeout)) != 0)
595         return r;
596
597     if ((r = avahi_simple_poll_run(s)) != 0)
598         return r;
599
600     if ((r = avahi_simple_poll_dispatch(s)) != 0)
601         return r;
602     
603     return 0;
604 }
605
606 void avahi_simple_poll_quit(AvahiSimplePoll *s) {
607     assert(s);
608
609     s->quit = 1;
610
611     /* If there is a background thread running the poll() for us, tell it to exit the poll() */
612     avahi_simple_poll_wakeup(s);
613 }
614
615 const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) {
616     assert(s);
617     
618     return &s->api;
619 }
620
621 static int system_poll(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
622     return poll(ufds, nfds, timeout);
623 }
624
625 void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata) {
626     assert(s);
627
628     s->poll_func = func ? func : system_poll;
629     s->poll_func_userdata = func ? userdata : NULL;
630
631     /* If there is a background thread running the poll() for us, tell it to exit the poll() */
632     avahi_simple_poll_wakeup(s);
633 }
634
635 int avahi_simple_poll_loop(AvahiSimplePoll *s) {
636     int r;
637
638     assert(s);
639     
640     for (;;)
641         if ((r = avahi_simple_poll_iterate(s, -1)) != 0)
642             return r;
643 }