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/malloc.h> 26#include <avahi-common/timeval.h> 27 28#include "glib-watch.h" 29 30struct 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 43struct 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 56struct 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 68static 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 79static 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 93static 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 101static 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 109static 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 139static 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 146static 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 153static 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 166static 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 193static 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 201static 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 209static 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 216static 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 230static 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 246static 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 255static 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 291static 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 316static 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 347AvahiGLibPoll *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 386void 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 398const AvahiPoll* avahi_glib_poll_get(AvahiGLibPoll *g) { 399 assert(g); 400 401 return &g->api; 402} 403