]> git.meshlink.io Git - catta/blob - avahi-glib/glib-watch.c
e6def467050da96cbe02bfb918cc57613261aecb
[catta] / avahi-glib / glib-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 <avahi-common/llist.h>
27 #include <avahi-common/malloc.h>
28
29 #include "glib-watch.h"
30
31 struct AvahiWatch {
32     AvahiGLibPoll *glib_poll;
33     int dead;
34     
35     GPollFD pollfd;
36     int pollfd_added;
37     
38     AvahiWatchCallback callback;
39     void *userdata;
40
41     AVAHI_LLIST_FIELDS(AvahiWatch, watches);
42 };
43
44 struct AvahiTimeout {
45     AvahiGLibPoll *glib_poll;
46     gboolean dead;
47
48     gboolean enabled;
49     struct timeval expiry;
50
51     AvahiTimeoutCallback callback;
52     void  *userdata;
53     
54     AVAHI_LLIST_FIELDS(AvahiTimeout, timeouts);
55 };
56
57 struct AvahiGLibPoll {
58     GSource source;
59     AvahiPoll api;
60     GMainContext *context;
61
62     gboolean timeout_req_cleanup;
63     gboolean watch_req_cleanup;
64     
65     AVAHI_LLIST_HEAD(AvahiWatch, watches);
66     AVAHI_LLIST_HEAD(AvahiTimeout, timeouts);
67 };
68
69 static void destroy_watch(AvahiWatch *w) {
70     assert(w);
71
72     if (w->pollfd_added)
73         g_source_remove_poll(&w->glib_poll->source, &w->pollfd);
74
75     AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->glib_poll->watches, w);
76
77     avahi_free(w);
78 }
79
80 static void cleanup_watches(AvahiGLibPoll *g, int all) {
81     AvahiWatch *w, *next;
82     assert(g);
83
84     for (w = g->watches; w; w = next) {
85         next = w->watches_next;
86
87         if (all || w->dead)
88             destroy_watch(w);
89     }
90
91     g->watch_req_cleanup = 0;
92 }
93
94 static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) {
95     AvahiWatch *w;
96     AvahiGLibPoll *g;
97     
98     assert(api);
99     assert(fd >= 0);
100     assert(callback);
101
102     g = api->userdata;
103     assert(g);
104
105     if (!(w = avahi_new(AvahiWatch, 1)))
106         return NULL;
107     
108     w->glib_poll = g;
109     w->pollfd.fd = fd;
110     w->pollfd.events =
111         (event & AVAHI_WATCH_IN ? G_IO_IN : 0) |
112         (event & AVAHI_WATCH_OUT ? G_IO_OUT : 0) |
113         (event & AVAHI_WATCH_ERR ? G_IO_ERR : 0) |
114         (event & AVAHI_WATCH_HUP ? G_IO_HUP : 0);
115     w->pollfd.revents = 0;
116     w->callback = callback;
117     w->userdata = userdata;
118     w->dead = FALSE;
119
120     g_source_add_poll(&g->source, &w->pollfd);
121     w->pollfd_added = TRUE;
122
123     AVAHI_LLIST_PREPEND(AvahiWatch, watches, g->watches, w);
124
125     return w;
126 }
127
128 static void watch_update(AvahiWatch *w, AvahiWatchEvent events) {
129     assert(w);
130     assert(!w->dead);
131
132     w->pollfd.events = events;
133 }
134
135 static AvahiWatchEvent watch_get_events(AvahiWatch *w) {
136     assert(w);
137     assert(!w->dead);
138
139     return w->pollfd.revents;
140 }
141
142 static void watch_free(AvahiWatch *w) {
143     assert(w);
144     assert(!w->dead);
145
146     if (w->pollfd_added) {
147         g_source_remove_poll(&w->glib_poll->source, &w->pollfd);
148         w->pollfd_added = TRUE;
149     }
150     
151     w->dead = TRUE;
152     w->glib_poll->timeout_req_cleanup = TRUE;
153 }
154
155 static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) {
156     AvahiTimeout *t;
157     AvahiGLibPoll *g;
158     
159     assert(api);
160     assert(callback);
161
162     g = api->userdata;
163     assert(g);
164
165     if (!(t = avahi_new(AvahiTimeout, 1)))
166         return NULL;
167     
168     t->glib_poll = g;
169     t->dead = FALSE;
170
171     if ((t->enabled = !!tv))
172         t->expiry = *tv;
173         
174     t->callback = callback;
175     t->userdata = userdata;
176
177     AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, g->timeouts, t);
178
179     return t;
180 }
181
182 static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
183     assert(t);
184     assert(!t->dead);
185     
186     if ((t->enabled = !!tv))
187         t->expiry = *tv;
188 }
189
190 static void timeout_free(AvahiTimeout *t) {
191     assert(t);
192     assert(!t->dead);
193
194     t->dead = TRUE;
195     t->glib_poll->timeout_req_cleanup = TRUE;
196 }
197
198 static void destroy_timeout(AvahiTimeout *t) {
199     assert(t);
200
201     AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->glib_poll->timeouts, t);
202     avahi_free(t);
203 }
204
205 static void cleanup_timeouts(AvahiGLibPoll *g, int all) {
206     AvahiTimeout *t, *next;
207     assert(g);
208
209     for (t = g->timeouts; t; t = next) {
210         next = t->timeouts_next;
211
212         if (all || t->dead)
213             destroy_timeout(t);
214     }
215
216     g->timeout_req_cleanup = FALSE;
217 }
218
219 static AvahiTimeout* find_next_timeout(AvahiGLibPoll *g) {
220     AvahiTimeout *t, *n = NULL;
221     assert(g);
222
223     for (t = g->timeouts; t; t = t->timeouts_next) {
224         
225         if (t->dead || !t->enabled)
226             continue;
227         
228         if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0)
229             n = t;
230     }
231
232     return n;
233 }
234
235 static void start_timeout_callback(AvahiTimeout *t) {
236     assert(t);
237     assert(!t->dead);
238     assert(t->enabled);
239
240     t->enabled = 0;
241     t->callback(t, t->userdata);
242 }
243
244 static gboolean prepare_func(GSource *source, gint *timeout) {
245     AvahiGLibPoll *g = (AvahiGLibPoll*) source;
246     AvahiTimeout *next_timeout;
247
248     g_assert(g);
249     g_assert(timeout);
250
251     if (g->watch_req_cleanup)
252         cleanup_watches(g, 0);
253
254     if (g->timeout_req_cleanup)
255         cleanup_timeouts(g, 0);
256     
257     if ((next_timeout = find_next_timeout(g))) {
258         GTimeVal now;
259         struct timeval tvnow;
260         AvahiUsec usec;
261
262         g_source_get_current_time(source, &now);
263         tvnow.tv_sec = now.tv_sec;
264         tvnow.tv_usec = now.tv_usec;
265     
266         usec = avahi_timeval_diff(&next_timeout->expiry, &tvnow);
267
268         if (usec <= 0)
269             return TRUE;
270
271         *timeout = (gint) (usec / 1000);
272     }
273         
274     return FALSE;
275 }
276
277 static gboolean check_func(GSource *source) {
278     AvahiGLibPoll *g = (AvahiGLibPoll*) source;
279     AvahiWatch *w;
280     AvahiTimeout *next_timeout;
281
282     g_assert(g);
283
284     if ((next_timeout = find_next_timeout(g))) {
285         GTimeVal now;
286         struct timeval tvnow;
287         g_source_get_current_time(source, &now);
288         tvnow.tv_sec = now.tv_sec;
289         tvnow.tv_usec = now.tv_usec;
290         
291         if (avahi_timeval_compare(&next_timeout->expiry, &tvnow) < 0)
292             return TRUE;
293     }
294
295     for (w = g->watches; w; w = w->watches_next)
296         if (w->pollfd.revents > 0)
297             return TRUE;
298     
299     return FALSE;
300 }
301
302 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer userdata) {
303     AvahiGLibPoll* g = (AvahiGLibPoll*) source;
304     AvahiWatch *w;
305     AvahiTimeout *next_timeout;
306     
307     g_assert(g);
308
309     if ((next_timeout = find_next_timeout(g))) {
310         GTimeVal now;
311         struct timeval tvnow;
312         g_source_get_current_time(source, &now);
313         tvnow.tv_sec = now.tv_sec;
314         tvnow.tv_usec = now.tv_usec;
315         
316         if (avahi_timeval_compare(&next_timeout->expiry, &tvnow) < 0) {
317             start_timeout_callback(next_timeout);
318             return TRUE;
319         }
320     }
321     
322     for (w = g->watches; w; w = w->watches_next)
323         if (w->pollfd.revents > 0) {
324             assert(w->callback);
325             w->callback(w, w->pollfd.fd, w->pollfd.revents, w->userdata);
326             w->pollfd.revents = 0;
327             return TRUE;
328         }
329
330     return TRUE;
331 }
332
333 AvahiGLibPoll *avahi_glib_poll_new(GMainContext *context, gint priority) {
334     AvahiGLibPoll *g;
335     
336     static GSourceFuncs source_funcs = {
337         prepare_func,
338         check_func,
339         dispatch_func,
340         NULL,
341         NULL,
342         NULL
343     };
344
345     g = (AvahiGLibPoll*) g_source_new(&source_funcs, sizeof(AvahiGLibPoll));
346     g_main_context_ref(g->context = context ? context : g_main_context_default());
347
348     g->api.userdata = g;
349     
350     g->api.watch_new = watch_new;
351     g->api.watch_free = watch_free;
352     g->api.watch_update = watch_update;
353     g->api.watch_get_events = watch_get_events;
354     
355     g->api.timeout_new = timeout_new;
356     g->api.timeout_free = timeout_free;
357     g->api.timeout_update = timeout_update;
358
359     g->watch_req_cleanup = FALSE;
360     g->timeout_req_cleanup = FALSE;
361     
362     AVAHI_LLIST_HEAD_INIT(AvahiWatch, g->watches);
363     AVAHI_LLIST_HEAD_INIT(AvahiTimeout, g->timeouts);
364     
365     g_source_attach(&g->source, g->context);
366     g_source_set_priority(&g->source, priority);
367
368     return g;
369 }
370
371 void avahi_glib_poll_free(AvahiGLibPoll *g) {
372     GSource *s = &g->source;
373     assert(g);
374
375     cleanup_watches(g, 1);
376     cleanup_timeouts(g, 1);
377
378     g_main_context_unref(g->context);
379     g_source_destroy(s);
380     g_source_unref(s);
381 }
382
383 const AvahiPoll* avahi_glib_poll_get(AvahiGLibPoll *g) {
384     assert(g);
385
386     return &g->api;
387 }