]> git.meshlink.io Git - catta/blob - avahi-common/simple-watch.c
* implement ini file parser
[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         gettimeofday(&now, NULL);
397         usec = avahi_timeval_diff(&next_timeout->expiry, &now);
398
399         if (usec <= 0) {
400             /* Timeout elapsed */
401
402             /* The events poll() returned in the last call are now no longer valid */
403             s->events_valid = 0;
404             return start_timeout_callback(next_timeout);
405         }
406
407         /* Calculate sleep time. We add 1ms because otherwise we'd
408          * wake up too early most of the time */
409         t = (int) (usec / 1000) + 1;
410
411         if (timeout < 0 || timeout > t)
412             timeout = t;
413     }
414
415     if ((r = s->poll_func(s->pollfds, s->n_pollfds, timeout)) < 0)
416         return -1;
417
418     /* The pollf events are now valid again */
419     s->events_valid = 1;
420
421     /* Check whether the wakeup time has been reached now */
422     if (next_timeout) {
423         struct timeval now;
424         
425         gettimeofday(&now, NULL);
426
427         if (avahi_timeval_compare(&next_timeout->expiry, &now) <= 0)
428             /* Time elapsed */
429             return start_timeout_callback(next_timeout);
430     }
431     
432     if (r > 0) {
433         AvahiWatch *w;
434
435         /* Look for some kind of I/O event */
436
437         for (w = s->watches; w; w = w->watches_next) {
438
439             if (w->dead)
440                 continue;
441
442             assert(w->idx >= 0);
443             assert(w->idx < s->n_pollfds);
444
445             if (s->pollfds[w->idx].revents > 0) {
446                 /* We execute only on callback in every iteration */
447                 w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata);
448                 return 0;
449             }
450         }
451     }
452
453     return 0;
454 }
455
456 void avahi_simple_poll_quit(AvahiSimplePoll *w) {
457     assert(w);
458
459     w->quit = 1;
460 }
461
462 const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) {
463     assert(s);
464     
465     return &s->api;
466 }
467
468 void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func) {
469     assert(s);
470
471     s->poll_func = func ? func : (AvahiPollFunc) poll;
472 }