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