]> git.meshlink.io Git - catta/blob - avahi-common/simple-watch.c
fde41e4800bae2627b370e0585a10cde9de9147e
[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
162     assert(!w->dead);
163
164     remove_pollfd(w);
165     
166     w->dead = 1;
167     w->simple_poll->n_watches --;
168     w->simple_poll->watch_req_cleanup = 1;
169 }
170
171 static void destroy_watch(AvahiWatch *w) {
172     assert(w);
173
174     remove_pollfd(w);
175     AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->simple_poll->watches, w);
176
177     if (!w->dead)
178         w->simple_poll->n_watches --;
179     
180     avahi_free(w);
181 }
182
183 static void cleanup_watches(AvahiSimplePoll *s, int all) {
184     AvahiWatch *w, *next;
185     assert(s);
186
187     for (w = s->watches; w; w = next) {
188         next = w->watches_next;
189
190         if (all || w->dead)
191             destroy_watch(w);
192     }
193
194     s->timeout_req_cleanup = 0;
195 }
196
197 static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) {
198     AvahiTimeout *t;
199     AvahiSimplePoll *s;
200     
201     assert(api);
202     assert(callback);
203
204     s = api->userdata;
205     assert(s);
206
207     if (!(t = avahi_new(AvahiTimeout, 1)))
208         return NULL;
209     
210     t->simple_poll = s;
211     t->dead = 0;
212
213     if ((t->enabled = !!tv))
214         t->expiry = *tv;
215         
216     t->callback = callback;
217     t->userdata = userdata;
218
219     AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, s->timeouts, t);
220
221     return t;
222 }
223
224 static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
225     assert(t);
226     assert(!t->dead);
227     
228     if ((t->enabled = !!tv))
229         t->expiry = *tv;
230 }
231
232 static void timeout_free(AvahiTimeout *t) {
233     assert(t);
234     assert(!t->dead);
235
236     t->dead = 1;
237     t->simple_poll->timeout_req_cleanup = 1;
238 }
239
240
241 static void destroy_timeout(AvahiTimeout *t) {
242     assert(t);
243
244     AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->simple_poll->timeouts, t);
245
246     avahi_free(t);
247 }
248
249 static void cleanup_timeouts(AvahiSimplePoll *s, int all) {
250     AvahiTimeout *t, *next;
251     assert(s);
252
253     for (t = s->timeouts; t; t = next) {
254         next = t->timeouts_next;
255
256         if (all || t->dead)
257             destroy_timeout(t);
258     }
259
260     s->timeout_req_cleanup = 0;
261 }
262
263 AvahiSimplePoll *avahi_simple_poll_new(void) {
264     AvahiSimplePoll *s;
265
266     if (!(s = avahi_new(AvahiSimplePoll, 1)))
267         return NULL;
268     
269     s->api.userdata = s;
270
271     s->api.watch_new = watch_new;
272     s->api.watch_free = watch_free;
273     s->api.watch_update = watch_update;
274     s->api.watch_get_events = watch_get_events;
275
276     s->api.timeout_new = timeout_new;
277     s->api.timeout_free = timeout_free;
278     s->api.timeout_update = timeout_update;
279     
280     s->pollfds = NULL;
281     s->max_pollfds = s->n_pollfds = 0;
282     s->rebuild_pollfds = 0;
283     s->quit = 0;
284     s->n_watches = 0;
285     s->events_valid = 0;
286     
287     s->watch_req_cleanup = 0;
288     s->timeout_req_cleanup = 0;
289
290     avahi_simple_poll_set_func(s, NULL);
291
292     AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches);
293     AVAHI_LLIST_HEAD_INIT(AvahiTimeout, s->timeouts);
294
295     return s;
296 }
297
298 void avahi_simple_poll_free(AvahiSimplePoll *s) {
299     assert(s);
300
301     cleanup_timeouts(s, 1);
302     cleanup_watches(s, 1);
303     assert(s->n_watches == 0);
304     
305     avahi_free(s->pollfds);
306     avahi_free(s);
307 }
308
309 static int rebuild(AvahiSimplePoll *s) {
310     AvahiWatch *w;
311     int idx;
312     
313     assert(s);
314
315     if (s->n_watches > s->max_pollfds) {
316         struct pollfd *n;
317
318         s->max_pollfds = s->n_watches + 10;
319         
320         if (!(n = avahi_realloc(s->pollfds, sizeof(struct pollfd) * s->max_pollfds)))
321             return -1;
322
323         s->pollfds = n;
324     }
325
326     for (idx = 0, w = s->watches; w; w = w->watches_next) {
327
328         if(w->dead)
329             continue;
330
331         assert(w->idx < s->max_pollfds);
332         s->pollfds[w->idx = idx++] = w->pollfd;
333     }
334
335     s->n_pollfds = idx;
336     s->events_valid = 0;
337     s->rebuild_pollfds = 0;
338
339     return 0;
340 }
341
342 static AvahiTimeout* find_next_timeout(AvahiSimplePoll *s) {
343     AvahiTimeout *t, *n = NULL;
344     assert(s);
345
346     for (t = s->timeouts; t; t = t->timeouts_next) {
347         
348         if (t->dead || !t->enabled)
349             continue;
350         
351         if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0)
352             n = t;
353     }
354
355     return n;
356 }
357
358 static int start_timeout_callback(AvahiTimeout *t) {
359     assert(t);
360     assert(!t->dead);
361     assert(t->enabled);
362
363     t->enabled = 0;
364     t->callback(t, t->userdata);
365     return 0;
366 }
367
368 int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) {
369     int r;
370     AvahiTimeout *next_timeout;
371     assert(s);
372
373     /* Cleanup things first */
374     if (s->watch_req_cleanup)
375         cleanup_watches(s, 0);
376
377     if (s->timeout_req_cleanup)
378         cleanup_timeouts(s, 0);
379
380     /* Check whether a quit was requested */
381     if (s->quit)
382         return 1;
383
384     /* Do we need to rebuild our array of pollfds? */
385     if (s->rebuild_pollfds)
386         if (rebuild(s) < 0)
387             return -1;
388
389
390     /* Calculate the wakeup time */
391     if ((next_timeout = find_next_timeout(s))) {
392         struct timeval now;
393         int t;
394         AvahiUsec usec;
395
396         if (next_timeout->expiry.tv_sec == 0 &&
397             next_timeout->expiry.tv_usec == 0) {
398
399             /* Just a shortcut so that we don't need to call gettimeofday() */
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             
407         gettimeofday(&now, NULL);
408         usec = avahi_timeval_diff(&next_timeout->expiry, &now);
409
410         if (usec <= 0) {
411             /* Timeout elapsed */
412
413             /* The events poll() returned in the last call are now no longer valid */
414             s->events_valid = 0;
415             return start_timeout_callback(next_timeout);
416         }
417
418         /* Calculate sleep time. We add 1ms because otherwise we'd
419          * wake up too early most of the time */
420         t = (int) (usec / 1000) + 1;
421
422         if (timeout < 0 || timeout > t)
423             timeout = t;
424     }
425
426     if ((r = s->poll_func(s->pollfds, s->n_pollfds, timeout)) < 0)
427         return -1;
428
429     /* The pollf events are now valid again */
430     s->events_valid = 1;
431
432     /* Check whether the wakeup time has been reached now */
433     if ((next_timeout = find_next_timeout(s))) {
434         struct timeval now;
435         
436         gettimeofday(&now, NULL);
437
438         if (avahi_timeval_compare(&next_timeout->expiry, &now) <= 0)
439             /* Time elapsed */
440             return start_timeout_callback(next_timeout);
441     }
442     
443     if (r > 0) {
444         AvahiWatch *w;
445
446         /* Look for some kind of I/O event */
447
448         for (w = s->watches; w; w = w->watches_next) {
449
450             if (w->dead)
451                 continue;
452
453             assert(w->idx >= 0);
454             assert(w->idx < s->n_pollfds);
455
456             if (s->pollfds[w->idx].revents > 0) {
457                 /* We execute only on callback in every iteration */
458                 w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata);
459                 return 0;
460             }
461         }
462     }
463
464     return 0;
465 }
466
467 void avahi_simple_poll_quit(AvahiSimplePoll *w) {
468     assert(w);
469
470     w->quit = 1;
471 }
472
473 const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) {
474     assert(s);
475     
476     return &s->api;
477 }
478
479 void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func) {
480     assert(s);
481
482     s->poll_func = func ? func : (AvahiPollFunc) poll;
483 }