]> git.meshlink.io Git - catta/blob - avahi-common/simple-watch.c
* add wakeup stuff to AvahiSimplePoll to facilitate integration in threaded environments
[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
33 #include <avahi-common/llist.h>
34 #include <avahi-common/malloc.h>
35
36 #include "simple-watch.h"
37
38 struct AvahiWatch {
39     AvahiSimplePoll *simple_poll;
40     int dead;
41
42     int idx;
43     struct pollfd pollfd;
44     
45     AvahiWatchCallback callback;
46     void *userdata;
47
48     AVAHI_LLIST_FIELDS(AvahiWatch, watches);
49 };
50
51 struct AvahiTimeout {
52     AvahiSimplePoll *simple_poll;
53     int dead;
54
55     int enabled;
56     struct timeval expiry;
57     
58     AvahiTimeoutCallback callback;
59     void  *userdata;
60     
61     AVAHI_LLIST_FIELDS(AvahiTimeout, timeouts);
62 };
63
64 struct AvahiSimplePoll {
65     AvahiPoll api;
66     AvahiPollFunc poll_func;
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 };
81
82 static void wakeup(AvahiSimplePoll *s) {
83     char c = 'x';
84     assert(s);
85
86     write(s->wakeup_pipe[1], &c, sizeof(c));
87 }
88
89 static void clear_wakeup(AvahiSimplePoll *s) {
90     char c[10]; /* Read ten at a time */
91
92     for(;;)
93         if (read(s->wakeup_pipe[0], &c, sizeof(c)) != sizeof(c))
94             break;
95 }
96
97 static int set_nonblock(int fd) {
98     int n;
99     
100     assert(fd >= 0);
101
102     if ((n = fcntl(fd, F_GETFL)) < 0)
103         return -1;
104
105     if (n & O_NONBLOCK)
106         return 0;
107
108     return fcntl(fd, F_SETFL, n|O_NONBLOCK);
109 }
110
111 static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) {
112     AvahiWatch *w;
113     AvahiSimplePoll *s;
114     
115     assert(api);
116     assert(fd >= 0);
117     assert(callback);
118
119     s = api->userdata;
120     assert(s);
121
122     if (!(w = avahi_new(AvahiWatch, 1)))
123         return NULL;
124     
125     w->simple_poll = s;
126     w->dead = 0;
127
128     w->pollfd.fd = fd;
129     w->pollfd.events = event;
130
131     w->callback = callback;
132     w->userdata = userdata;
133
134     if (s->n_pollfds < s->max_pollfds) {
135         /* If there's space for this pollfd, go on and allocate it */
136         w->idx = s->n_pollfds++;
137         s->pollfds[w->idx] = w->pollfd;
138         
139     } else {
140         /* Unfortunately there's no place for this pollfd, so request a rebuild of the array */
141         w->idx = -1;
142         s->rebuild_pollfds = 1;
143     }
144
145     AVAHI_LLIST_PREPEND(AvahiWatch, watches, s->watches, w);
146     s->n_watches++;
147
148     wakeup(w->simple_poll);
149
150     return w;
151 }
152
153 static void watch_update(AvahiWatch *w, AvahiWatchEvent events) {
154     assert(w);
155     assert(!w->dead);
156
157     w->pollfd.events = events;
158
159     if (w->idx != -1) {
160         assert(w->simple_poll);
161         w->simple_poll->pollfds[w->idx] = w->pollfd;
162     } else
163         w->simple_poll->rebuild_pollfds = 1;
164
165     wakeup(w->simple_poll);
166 }
167
168 static AvahiWatchEvent watch_get_events(AvahiWatch *w) {
169     assert(w);
170     assert(!w->dead);
171
172     if (w->idx != -1 && w->simple_poll->events_valid)
173         return w->simple_poll->pollfds[w->idx].revents;
174
175     return 0;
176 }
177
178 static void remove_pollfd(AvahiWatch *w) {
179     assert(w);
180
181     if (w->idx == -1)
182         return;
183     
184     if (w->idx == w->simple_poll->n_pollfds-1) {
185
186         /* This pollfd is at the end of the array, so we can easily cut it */
187
188         assert(w->simple_poll->n_pollfds > 0);
189         w->simple_poll->n_pollfds -= 1;
190     } else
191
192         /* Unfortunately this pollfd is in the middle of the array, so request a rebuild of it */
193         w->simple_poll->rebuild_pollfds = 1;
194 }
195
196 static void watch_free(AvahiWatch *w) {
197     assert(w);
198
199     assert(!w->dead);
200
201     remove_pollfd(w);
202     
203     w->dead = 1;
204     w->simple_poll->n_watches --;
205     w->simple_poll->watch_req_cleanup = 1;
206
207     wakeup(w->simple_poll);
208 }
209
210 static void destroy_watch(AvahiWatch *w) {
211     assert(w);
212
213     remove_pollfd(w);
214     AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->simple_poll->watches, w);
215
216     if (!w->dead)
217         w->simple_poll->n_watches --;
218     
219     avahi_free(w);
220 }
221
222 static void cleanup_watches(AvahiSimplePoll *s, int all) {
223     AvahiWatch *w, *next;
224     assert(s);
225
226     for (w = s->watches; w; w = next) {
227         next = w->watches_next;
228
229         if (all || w->dead)
230             destroy_watch(w);
231     }
232
233     s->timeout_req_cleanup = 0;
234 }
235
236 static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) {
237     AvahiTimeout *t;
238     AvahiSimplePoll *s;
239     
240     assert(api);
241     assert(callback);
242
243     s = api->userdata;
244     assert(s);
245
246     if (!(t = avahi_new(AvahiTimeout, 1)))
247         return NULL;
248     
249     t->simple_poll = s;
250     t->dead = 0;
251
252     if ((t->enabled = !!tv))
253         t->expiry = *tv;
254         
255     t->callback = callback;
256     t->userdata = userdata;
257
258     AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, s->timeouts, t);
259
260     wakeup(t->simple_poll);
261     return t;
262 }
263
264 static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
265     assert(t);
266     assert(!t->dead);
267     
268     if ((t->enabled = !!tv))
269         t->expiry = *tv;
270
271     wakeup(t->simple_poll);
272 }
273
274 static void timeout_free(AvahiTimeout *t) {
275     assert(t);
276     assert(!t->dead);
277
278     t->dead = 1;
279     t->simple_poll->timeout_req_cleanup = 1;
280
281     wakeup(t->simple_poll);
282 }
283
284
285 static void destroy_timeout(AvahiTimeout *t) {
286     assert(t);
287
288     AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->simple_poll->timeouts, t);
289
290     avahi_free(t);
291 }
292
293 static void cleanup_timeouts(AvahiSimplePoll *s, int all) {
294     AvahiTimeout *t, *next;
295     assert(s);
296
297     for (t = s->timeouts; t; t = next) {
298         next = t->timeouts_next;
299
300         if (all || t->dead)
301             destroy_timeout(t);
302     }
303
304     s->timeout_req_cleanup = 0;
305 }
306
307 AvahiSimplePoll *avahi_simple_poll_new(void) {
308     AvahiSimplePoll *s;
309
310     if (!(s = avahi_new(AvahiSimplePoll, 1)))
311         return NULL;
312
313     if (pipe(s->wakeup_pipe) < 0) {
314         avahi_free(s);
315         return NULL;
316     }
317
318     set_nonblock(s->wakeup_pipe[0]);
319     set_nonblock(s->wakeup_pipe[1]);
320     
321     s->api.userdata = s;
322
323     s->api.watch_new = watch_new;
324     s->api.watch_free = watch_free;
325     s->api.watch_update = watch_update;
326     s->api.watch_get_events = watch_get_events;
327
328     s->api.timeout_new = timeout_new;
329     s->api.timeout_free = timeout_free;
330     s->api.timeout_update = timeout_update;
331     
332     s->pollfds = NULL;
333     s->max_pollfds = s->n_pollfds = 0;
334     s->rebuild_pollfds = 0;
335     s->quit = 0;
336     s->n_watches = 0;
337     s->events_valid = 0;
338     
339     s->watch_req_cleanup = 0;
340     s->timeout_req_cleanup = 0;
341
342     avahi_simple_poll_set_func(s, NULL);
343
344     AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches);
345     AVAHI_LLIST_HEAD_INIT(AvahiTimeout, s->timeouts);
346
347     return s;
348 }
349
350 void avahi_simple_poll_free(AvahiSimplePoll *s) {
351     assert(s);
352
353     cleanup_timeouts(s, 1);
354     cleanup_watches(s, 1);
355     assert(s->n_watches == 0);
356     
357     avahi_free(s->pollfds);
358
359     if (s->wakeup_pipe[0] >= 0)
360         close(s->wakeup_pipe[0]);
361
362     if (s->wakeup_pipe[1] >= 0)
363         close(s->wakeup_pipe[1]);
364         
365     avahi_free(s);
366 }
367
368 static int rebuild(AvahiSimplePoll *s) {
369     AvahiWatch *w;
370     int idx;
371     
372     assert(s);
373
374     if (s->n_watches+1 > s->max_pollfds) {
375         struct pollfd *n;
376
377         s->max_pollfds = s->n_watches + 10;
378         
379         if (!(n = avahi_realloc(s->pollfds, sizeof(struct pollfd) * s->max_pollfds)))
380             return -1;
381
382         s->pollfds = n;
383     }
384
385     
386     s->pollfds[0].fd = s->wakeup_pipe[0];
387     s->pollfds[0].events = POLLIN;
388     s->pollfds[0].revents = 0;
389     
390     for (idx = 1, w = s->watches; w; w = w->watches_next) {
391
392         if(w->dead)
393             continue;
394
395         assert(w->idx < s->max_pollfds);
396         s->pollfds[w->idx = idx++] = w->pollfd;
397     }
398
399     s->n_pollfds = idx;
400     s->events_valid = 0;
401     s->rebuild_pollfds = 0;
402
403     return 0;
404 }
405
406 static AvahiTimeout* find_next_timeout(AvahiSimplePoll *s) {
407     AvahiTimeout *t, *n = NULL;
408     assert(s);
409
410     for (t = s->timeouts; t; t = t->timeouts_next) {
411         
412         if (t->dead || !t->enabled)
413             continue;
414         
415         if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0)
416             n = t;
417     }
418
419     return n;
420 }
421
422 static int start_timeout_callback(AvahiTimeout *t) {
423     assert(t);
424     assert(!t->dead);
425     assert(t->enabled);
426
427     t->enabled = 0;
428     t->callback(t, t->userdata);
429     return 0;
430 }
431
432 int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) {
433     int r;
434     AvahiTimeout *next_timeout;
435     assert(s);
436
437     /* Clear pending wakeup requests */
438     clear_wakeup(s);
439
440     /* Cleanup things first */
441     if (s->watch_req_cleanup)
442         cleanup_watches(s, 0);
443
444     if (s->timeout_req_cleanup)
445         cleanup_timeouts(s, 0);
446
447     /* Check whether a quit was requested */
448     if (s->quit)
449         return 1;
450
451     /* Do we need to rebuild our array of pollfds? */
452     if (s->rebuild_pollfds)
453         if (rebuild(s) < 0)
454             return -1;
455
456     /* Calculate the wakeup time */
457     if ((next_timeout = find_next_timeout(s))) {
458         struct timeval now;
459         int t;
460         AvahiUsec usec;
461
462         if (next_timeout->expiry.tv_sec == 0 &&
463             next_timeout->expiry.tv_usec == 0) {
464
465             /* Just a shortcut so that we don't need to call gettimeofday() */
466             
467             /* The events poll() returned in the last call are now no longer valid */
468             s->events_valid = 0;
469             return start_timeout_callback(next_timeout);
470         }
471
472             
473         gettimeofday(&now, NULL);
474         usec = avahi_timeval_diff(&next_timeout->expiry, &now);
475
476         if (usec <= 0) {
477             /* Timeout elapsed */
478
479             /* The events poll() returned in the last call are now no longer valid */
480             s->events_valid = 0;
481             return start_timeout_callback(next_timeout);
482         }
483
484         /* Calculate sleep time. We add 1ms because otherwise we'd
485          * wake up too early most of the time */
486         t = (int) (usec / 1000) + 1;
487
488         if (timeout < 0 || timeout > t)
489             timeout = t;
490     }
491
492     if ((r = s->poll_func(s->pollfds, s->n_pollfds, timeout)) < 0)
493         return -1;
494
495     /* The pollf events are now valid again */
496     s->events_valid = 1;
497
498     /* Check whether the wakeup time has been reached now */
499     if ((next_timeout = find_next_timeout(s))) {
500         struct timeval now;
501         
502         gettimeofday(&now, NULL);
503
504         if (avahi_timeval_compare(&next_timeout->expiry, &now) <= 0)
505             /* Time elapsed */
506             return start_timeout_callback(next_timeout);
507     }
508     
509     if (r > 0) {
510         AvahiWatch *w;
511
512         /* Look for some kind of I/O event */
513
514         for (w = s->watches; w; w = w->watches_next) {
515
516             if (w->dead)
517                 continue;
518
519             assert(w->idx >= 0);
520             assert(w->idx < s->n_pollfds);
521
522             if (s->pollfds[w->idx].revents > 0) {
523                 /* We execute only on callback in every iteration */
524                 w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata);
525                 return 0;
526             }
527         }
528     }
529
530     return 0;
531 }
532
533 void avahi_simple_poll_quit(AvahiSimplePoll *w) {
534     assert(w);
535
536     w->quit = 1;
537 }
538
539 const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) {
540     assert(s);
541     
542     return &s->api;
543 }
544
545 void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func) {
546     assert(s);
547
548     s->poll_func = func ? func : (AvahiPollFunc) poll;
549 }