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