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