]> git.meshlink.io Git - catta/blob - avahi-common/simple-watch.c
5e03fe44219d60921b347e99bd614356b037264d
[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     int idx;
40     struct pollfd pollfd;
41     AvahiWatchCallback callback;
42     void *userdata;
43
44     AVAHI_LLIST_FIELDS(AvahiWatch, watches);
45 };
46
47 struct AvahiSimplePoll {
48     AvahiPoll api;
49     AvahiPollFunc poll_func;
50
51     struct pollfd* pollfds;
52     int n_pollfds, max_pollfds, rebuild_pollfds;
53
54     struct timeval wakeup;
55     AvahiWakeupCallback wakeup_callback;
56     void *wakeup_userdata;
57
58     int req_cleanup;
59     
60     int quit;
61
62     int n_watches;
63     AVAHI_LLIST_HEAD(AvahiWatch, watches);
64 };
65
66 static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) {
67     AvahiWatch *w;
68     AvahiSimplePoll *s;
69     
70     assert(api);
71     assert(fd >= 0);
72     assert(callback);
73
74     s = api->userdata;
75     assert(s);
76
77     if (!(w = avahi_new(AvahiWatch, 1)))
78         return NULL;
79     
80     w->simple_poll = s;
81     w->pollfd.fd = fd;
82     w->pollfd.events = event;
83     w->callback = callback;
84     w->userdata = userdata;
85     w->dead = 0;
86
87     if (s->n_pollfds < s->max_pollfds) {
88         /* If there's space for this pollfd, go on and allocate it */
89         w->idx = s->n_pollfds++;
90         s->pollfds[w->idx] = w->pollfd;
91         
92     } else {
93         /* Unfortunately there's no place for this pollfd, so request a rebuild of the array */
94         w->idx = -1;
95         s->rebuild_pollfds = 1;
96     }
97
98     AVAHI_LLIST_PREPEND(AvahiWatch, watches, s->watches, w);
99     s->n_watches++;
100
101     return w;
102 }
103
104 static void watch_update(AvahiWatch *w, AvahiWatchEvent events) {
105     assert(w);
106     assert(!w->dead);
107
108     w->pollfd.events = events;
109
110     if (w->idx != -1) {
111         assert(w->simple_poll);
112         w->simple_poll->pollfds[w->idx] = w->pollfd;
113     } else
114         w->simple_poll->rebuild_pollfds = 1;
115 }
116
117 static void remove_pollfd(AvahiWatch *w) {
118     assert(w);
119
120     if (w->idx == -1)
121         return;
122     
123     if (w->idx == w->simple_poll->n_pollfds-1) {
124
125         /* This pollfd is at the end of the array, so we can easily cut it */
126
127         assert(w->simple_poll->n_pollfds > 0);
128         w->simple_poll->n_pollfds -= 1;
129     } else
130
131         /* Unfortunately this pollfd is in the middle of the array, so request a rebuild of it */
132         w->simple_poll->rebuild_pollfds = 1;
133 }
134
135 static void watch_free(AvahiWatch *w) {
136     assert(w);
137     assert(!w->dead);
138
139     remove_pollfd(w);
140     
141     w->dead = 1;
142     w->simple_poll->n_watches --;
143     w->simple_poll->req_cleanup = 1;
144 }
145
146 static void set_wakeup(const AvahiPoll *api, const struct timeval *tv, AvahiWakeupCallback callback, void *userdata) {
147     AvahiSimplePoll *s;
148
149     assert(api);
150     s = api->userdata;
151
152     if (callback) {
153         if (tv)
154             s->wakeup = *tv;
155         else {
156             s->wakeup.tv_sec = 0;
157             s->wakeup.tv_usec = 0;
158         }
159         
160         s->wakeup_callback = callback;
161         s->wakeup_userdata = userdata;
162     } else
163         s->wakeup_callback = NULL;
164 }
165
166 static void destroy_watch(AvahiWatch *w) {
167     assert(w);
168
169     remove_pollfd(w);
170     AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->simple_poll->watches, w);
171
172     if (!w->dead)
173         w->simple_poll->n_watches --;
174     
175     avahi_free(w);
176 }
177
178 static void cleanup(AvahiSimplePoll *s, int all) {
179     AvahiWatch *w, *next;
180     assert(s);
181
182     for (w = s->watches; w; w = next) {
183         next = w->watches_next;
184
185         if (all || w->dead)
186             destroy_watch(w);
187     }
188
189     s->req_cleanup = 0;
190 }
191
192 AvahiSimplePoll *avahi_simple_poll_new(void) {
193     AvahiSimplePoll *s;
194
195     if (!(s = avahi_new(AvahiSimplePoll, 1)))
196         return NULL;
197     
198     s->api.userdata = s;
199     s->api.watch_new = watch_new;
200     s->api.watch_free = watch_free;
201     s->api.watch_update = watch_update;
202     s->api.set_wakeup = set_wakeup;
203     s->pollfds = NULL;
204     s->max_pollfds = s->n_pollfds = 0;
205     s->wakeup_callback = NULL;
206     s->rebuild_pollfds = 0;
207     s->quit = 0;
208     s->n_watches = 0;
209     s->req_cleanup = 0;
210
211     avahi_simple_poll_set_func(s, NULL);
212
213     AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches);
214
215     return s;
216 }
217
218 void avahi_simple_poll_free(AvahiSimplePoll *s) {
219     assert(s);
220
221     cleanup(s, 1);
222     
223     assert(s->n_watches == 0);
224     
225     avahi_free(s->pollfds);
226     avahi_free(s);
227 }
228
229 static int rebuild(AvahiSimplePoll *s) {
230     AvahiWatch *w;
231     int idx;
232     
233     assert(s);
234
235     if (s->n_watches > s->max_pollfds) {
236         struct pollfd *n;
237
238         s->max_pollfds = s->n_watches + 10;
239         
240         if (!(n = avahi_realloc(s->pollfds, sizeof(struct pollfd) * s->max_pollfds)))
241             return -1;
242
243         s->pollfds = n;
244     }
245
246     for (idx = 0, w = s->watches; w; w = w->watches_next) {
247
248         if(w->dead)
249             continue;
250
251         assert(w->idx < s->max_pollfds);
252         s->pollfds[w->idx = idx++] = w->pollfd;
253     }
254
255     s->n_pollfds = idx;
256     
257     s->rebuild_pollfds = 0;
258
259     return 0;
260 }
261
262 static int start_wakeup_callback(AvahiSimplePoll *s) {
263     AvahiWakeupCallback callback;
264     void *userdata;
265
266     assert(s);
267
268     /* Reset the wakeup functions, but allow changing of the two
269        values from the callback function */
270
271     callback = s->wakeup_callback;
272     userdata = s->wakeup_userdata;
273     s->wakeup_callback = NULL;
274     s->wakeup_userdata = NULL;
275
276     assert(callback);
277     
278     callback(&s->api, userdata);
279     return 0;
280 }
281
282 int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) {
283     int r;
284     assert(s);
285
286     /* Cleanup things first */
287     if (s->req_cleanup)
288         cleanup(s, 0);
289
290     /* Check whether a quit was requested */
291     if (s->quit)
292         return 1;
293
294     /* Do we need to rebuild our array of pollfds? */
295     if (s->rebuild_pollfds)
296         if (rebuild(s) < 0)
297             return -1;
298
299     /* Calculate the wakeup time */
300     if (s->wakeup_callback) {
301         struct timeval now;
302         int t;
303         AvahiUsec usec;
304
305         gettimeofday(&now, NULL);
306         usec = avahi_timeval_diff(&s->wakeup, &now);
307
308         if (usec <= 0)
309             /* Timeout elapsed */
310
311             return start_wakeup_callback(s);
312
313         /* Calculate sleep time. We add 1ms because otherwise we'd
314          * wake up too early most of the time */
315         t = (int) (usec / 1000) + 1;
316
317         if (timeout < 0 || timeout > t)
318             timeout = t;
319     }
320
321     if ((r = s->poll_func(s->pollfds, s->n_pollfds, timeout)) < 0)
322         return -1;
323
324     /* Check whether the wakeup time has been reached now */
325     if (s->wakeup_callback) {
326         struct timeval now;
327         
328         gettimeofday(&now, NULL);
329
330         if (avahi_timeval_compare(&s->wakeup, &now) <= 0)
331             /* Time elapsed */
332             return start_wakeup_callback(s);
333     }
334     
335     if (r > 0) {
336         AvahiWatch *w;
337
338         /* Look for some kind of I/O event */
339
340         for (w = s->watches; w; w = w->watches_next) {
341
342             if (w->dead)
343                 continue;
344
345             assert(w->idx >= 0);
346             assert(w->idx < s->n_pollfds);
347
348             if (s->pollfds[w->idx].revents > 0) {
349                 /* We execute only on callback in every iteration */
350                 w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata);
351                 return 0;
352             }
353         }
354     }
355
356     return 0;
357 }
358
359 void avahi_simple_poll_quit(AvahiSimplePoll *w) {
360     assert(w);
361
362     w->quit = 1;
363 }
364
365 const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) {
366     assert(s);
367     
368     return &s->api;
369 }
370
371 void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func) {
372     assert(s);
373
374     s->poll_func = func ? func : (AvahiPollFunc) poll;
375 }