1 /***
2 This file is part of avahi.
3
4 avahi is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 avahi is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12 Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with avahi; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <avahi-common/llist.h>
25 #include "avahi-common/avahi-malloc.h"
26 #include <avahi-common/timeval.h>
27
28 #include "glib-watch.h"
29
30 struct AvahiWatch {
31 AvahiGLibPoll *glib_poll;
32 int dead;
33
34 GPollFD pollfd;
35 int pollfd_added;
36
37 AvahiWatchCallback callback;
38 void *userdata;
39
40 AVAHI_LLIST_FIELDS(AvahiWatch, watches);
41 };
42
43 struct AvahiTimeout {
44 AvahiGLibPoll *glib_poll;
45 gboolean dead;
46
47 gboolean enabled;
48 struct timeval expiry;
49
50 AvahiTimeoutCallback callback;
51 void *userdata;
52
53 AVAHI_LLIST_FIELDS(AvahiTimeout, timeouts);
54 };
55
56 struct AvahiGLibPoll {
57 GSource source;
58 AvahiPoll api;
59 GMainContext *context;
60
61 gboolean timeout_req_cleanup;
62 gboolean watch_req_cleanup;
63
64 AVAHI_LLIST_HEAD(AvahiWatch, watches);
65 AVAHI_LLIST_HEAD(AvahiTimeout, timeouts);
66 };
67
destroy_watch(AvahiWatch * w)68 static void destroy_watch(AvahiWatch *w) {
69 assert(w);
70
71 if (w->pollfd_added)
72 g_source_remove_poll(&w->glib_poll->source, &w->pollfd);
73
74 AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->glib_poll->watches, w);
75
76 avahi_free(w);
77 }
78
cleanup_watches(AvahiGLibPoll * g,int all)79 static void cleanup_watches(AvahiGLibPoll *g, int all) {
80 AvahiWatch *w, *next;
81 assert(g);
82
83 for (w = g->watches; w; w = next) {
84 next = w->watches_next;
85
86 if (all || w->dead)
87 destroy_watch(w);
88 }
89
90 g->watch_req_cleanup = 0;
91 }
92
map_events_to_glib(AvahiWatchEvent events)93 static gushort map_events_to_glib(AvahiWatchEvent events) {
94 return
95 (events & AVAHI_WATCH_IN ? G_IO_IN : 0) |
96 (events & AVAHI_WATCH_OUT ? G_IO_OUT : 0) |
97 (events & AVAHI_WATCH_ERR ? G_IO_ERR : 0) |
98 (events & AVAHI_WATCH_HUP ? G_IO_HUP : 0);
99 }
100
map_events_from_glib(gushort events)101 static AvahiWatchEvent map_events_from_glib(gushort events) {
102 return
103 (events & G_IO_IN ? AVAHI_WATCH_IN : 0) |
104 (events & G_IO_OUT ? AVAHI_WATCH_OUT : 0) |
105 (events & G_IO_ERR ? AVAHI_WATCH_ERR : 0) |
106 (events & G_IO_HUP ? AVAHI_WATCH_HUP : 0);
107 }
108
watch_new(const AvahiPoll * api,int fd,AvahiWatchEvent events,AvahiWatchCallback callback,void * userdata)109 static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent events, AvahiWatchCallback callback, void *userdata) {
110 AvahiWatch *w;
111 AvahiGLibPoll *g;
112
113 assert(api);
114 assert(fd >= 0);
115 assert(callback);
116
117 g = api->userdata;
118 assert(g);
119
120 if (!(w = avahi_new(AvahiWatch, 1)))
121 return NULL;
122
123 w->glib_poll = g;
124 w->pollfd.fd = fd;
125 w->pollfd.events = map_events_to_glib(events);
126 w->pollfd.revents = 0;
127 w->callback = callback;
128 w->userdata = userdata;
129 w->dead = FALSE;
130
131 g_source_add_poll(&g->source, &w->pollfd);
132 w->pollfd_added = TRUE;
133
134 AVAHI_LLIST_PREPEND(AvahiWatch, watches, g->watches, w);
135
136 return w;
137 }
138
watch_update(AvahiWatch * w,AvahiWatchEvent events)139 static void watch_update(AvahiWatch *w, AvahiWatchEvent events) {
140 assert(w);
141 assert(!w->dead);
142
143 w->pollfd.events = map_events_to_glib(events);
144 }
145
watch_get_events(AvahiWatch * w)146 static AvahiWatchEvent watch_get_events(AvahiWatch *w) {
147 assert(w);
148 assert(!w->dead);
149
150 return map_events_from_glib(w->pollfd.revents);
151 }
152
watch_free(AvahiWatch * w)153 static void watch_free(AvahiWatch *w) {
154 assert(w);
155 assert(!w->dead);
156
157 if (w->pollfd_added) {
158 g_source_remove_poll(&w->glib_poll->source, &w->pollfd);
159 w->pollfd_added = FALSE;
160 }
161
162 w->dead = TRUE;
163 w->glib_poll->timeout_req_cleanup = TRUE;
164 }
165
timeout_new(const AvahiPoll * api,const struct timeval * tv,AvahiTimeoutCallback callback,void * userdata)166 static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) {
167 AvahiTimeout *t;
168 AvahiGLibPoll *g;
169
170 assert(api);
171 assert(callback);
172
173 g = api->userdata;
174 assert(g);
175
176 if (!(t = avahi_new(AvahiTimeout, 1)))
177 return NULL;
178
179 t->glib_poll = g;
180 t->dead = FALSE;
181
182 if ((t->enabled = !!tv))
183 t->expiry = *tv;
184
185 t->callback = callback;
186 t->userdata = userdata;
187
188 AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, g->timeouts, t);
189
190 return t;
191 }
192
timeout_update(AvahiTimeout * t,const struct timeval * tv)193 static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
194 assert(t);
195 assert(!t->dead);
196
197 if ((t->enabled = !!tv))
198 t->expiry = *tv;
199 }
200
timeout_free(AvahiTimeout * t)201 static void timeout_free(AvahiTimeout *t) {
202 assert(t);
203 assert(!t->dead);
204
205 t->dead = TRUE;
206 t->glib_poll->timeout_req_cleanup = TRUE;
207 }
208
destroy_timeout(AvahiTimeout * t)209 static void destroy_timeout(AvahiTimeout *t) {
210 assert(t);
211
212 AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->glib_poll->timeouts, t);
213 avahi_free(t);
214 }
215
cleanup_timeouts(AvahiGLibPoll * g,int all)216 static void cleanup_timeouts(AvahiGLibPoll *g, int all) {
217 AvahiTimeout *t, *next;
218 assert(g);
219
220 for (t = g->timeouts; t; t = next) {
221 next = t->timeouts_next;
222
223 if (all || t->dead)
224 destroy_timeout(t);
225 }
226
227 g->timeout_req_cleanup = FALSE;
228 }
229
find_next_timeout(AvahiGLibPoll * g)230 static AvahiTimeout* find_next_timeout(AvahiGLibPoll *g) {
231 AvahiTimeout *t, *n = NULL;
232 assert(g);
233
234 for (t = g->timeouts; t; t = t->timeouts_next) {
235
236 if (t->dead || !t->enabled)
237 continue;
238
239 if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0)
240 n = t;
241 }
242
243 return n;
244 }
245
start_timeout_callback(AvahiTimeout * t)246 static void start_timeout_callback(AvahiTimeout *t) {
247 assert(t);
248 assert(!t->dead);
249 assert(t->enabled);
250
251 t->enabled = 0;
252 t->callback(t, t->userdata);
253 }
254
prepare_func(GSource * source,gint * timeout)255 static gboolean prepare_func(GSource *source, gint *timeout) {
256 AvahiGLibPoll *g = (AvahiGLibPoll*) source;
257 AvahiTimeout *next_timeout;
258
259 g_assert(g);
260 g_assert(timeout);
261
262 if (g->watch_req_cleanup)
263 cleanup_watches(g, 0);
264
265 if (g->timeout_req_cleanup)
266 cleanup_timeouts(g, 0);
267
268 if ((next_timeout = find_next_timeout(g))) {
269 GTimeVal now;
270 struct timeval tvnow;
271 AvahiUsec usec;
272
273 g_source_get_current_time(source, &now);
274 tvnow.tv_sec = now.tv_sec;
275 tvnow.tv_usec = now.tv_usec;
276
277 usec = avahi_timeval_diff(&next_timeout->expiry, &tvnow);
278
279 if (usec <= 0) {
280 *timeout = 0;
281 return TRUE;
282 }
283
284 *timeout = (gint) (usec / 1000);
285 } else
286 *timeout = -1;
287
288 return FALSE;
289 }
290
check_func(GSource * source)291 static gboolean check_func(GSource *source) {
292 AvahiGLibPoll *g = (AvahiGLibPoll*) source;
293 AvahiWatch *w;
294 AvahiTimeout *next_timeout;
295
296 g_assert(g);
297
298 if ((next_timeout = find_next_timeout(g))) {
299 GTimeVal now;
300 struct timeval tvnow;
301 g_source_get_current_time(source, &now);
302 tvnow.tv_sec = now.tv_sec;
303 tvnow.tv_usec = now.tv_usec;
304
305 if (avahi_timeval_compare(&next_timeout->expiry, &tvnow) <= 0)
306 return TRUE;
307 }
308
309 for (w = g->watches; w; w = w->watches_next)
310 if (w->pollfd.revents > 0)
311 return TRUE;
312
313 return FALSE;
314 }
315
dispatch_func(GSource * source,AVAHI_GCC_UNUSED GSourceFunc callback,AVAHI_GCC_UNUSED gpointer userdata)316 static gboolean dispatch_func(GSource *source, AVAHI_GCC_UNUSED GSourceFunc callback, AVAHI_GCC_UNUSED gpointer userdata) {
317 AvahiGLibPoll* g = (AvahiGLibPoll*) source;
318 AvahiWatch *w;
319 AvahiTimeout *next_timeout;
320
321 g_assert(g);
322
323 if ((next_timeout = find_next_timeout(g))) {
324 GTimeVal now;
325 struct timeval tvnow;
326 g_source_get_current_time(source, &now);
327 tvnow.tv_sec = now.tv_sec;
328 tvnow.tv_usec = now.tv_usec;
329
330 if (avahi_timeval_compare(&next_timeout->expiry, &tvnow) < 0) {
331 start_timeout_callback(next_timeout);
332 return TRUE;
333 }
334 }
335
336 for (w = g->watches; w; w = w->watches_next)
337 if (w->pollfd.revents > 0) {
338 assert(w->callback);
339 w->callback(w, w->pollfd.fd, map_events_from_glib(w->pollfd.revents), w->userdata);
340 w->pollfd.revents = 0;
341 return TRUE;
342 }
343
344 return TRUE;
345 }
346
avahi_glib_poll_new(GMainContext * context,gint priority)347 AvahiGLibPoll *avahi_glib_poll_new(GMainContext *context, gint priority) {
348 AvahiGLibPoll *g;
349
350 static GSourceFuncs source_funcs = {
351 prepare_func,
352 check_func,
353 dispatch_func,
354 NULL,
355 NULL,
356 NULL
357 };
358
359 g = (AvahiGLibPoll*) g_source_new(&source_funcs, sizeof(AvahiGLibPoll));
360 g_main_context_ref(g->context = context ? context : g_main_context_default());
361
362 g->api.userdata = g;
363
364 g->api.watch_new = watch_new;
365 g->api.watch_free = watch_free;
366 g->api.watch_update = watch_update;
367 g->api.watch_get_events = watch_get_events;
368
369 g->api.timeout_new = timeout_new;
370 g->api.timeout_free = timeout_free;
371 g->api.timeout_update = timeout_update;
372
373 g->watch_req_cleanup = FALSE;
374 g->timeout_req_cleanup = FALSE;
375
376 AVAHI_LLIST_HEAD_INIT(AvahiWatch, g->watches);
377 AVAHI_LLIST_HEAD_INIT(AvahiTimeout, g->timeouts);
378
379 g_source_attach(&g->source, g->context);
380 g_source_set_priority(&g->source, priority);
381 g_source_set_can_recurse(&g->source, FALSE);
382
383 return g;
384 }
385
avahi_glib_poll_free(AvahiGLibPoll * g)386 void avahi_glib_poll_free(AvahiGLibPoll *g) {
387 GSource *s = &g->source;
388 assert(g);
389
390 cleanup_watches(g, 1);
391 cleanup_timeouts(g, 1);
392
393 g_main_context_unref(g->context);
394 g_source_destroy(s);
395 g_source_unref(s);
396 }
397
avahi_glib_poll_get(AvahiGLibPoll * g)398 const AvahiPoll* avahi_glib_poll_get(AvahiGLibPoll *g) {
399 assert(g);
400
401 return &g->api;
402 }
403