Update NEWS for 0.2.0 release
[libvirt-glib.git] / libvirt-glib / libvirt-glib-event.c
1 /*
2  * libvirt-glib-event.c: libvirt glib integration
3  *
4  * Copyright (C) 2008 Daniel P. Berrange
5  * Copyright (C) 2010-2011 Red Hat, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
20  *
21  * Author: Daniel P. Berrange <berrange@redhat.com>
22  */
23
24 #include <config.h>
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <libvirt/libvirt.h>
31
32 #include "libvirt-glib/libvirt-glib.h"
33
34 /**
35  * SECTION:libvirt-glib-event
36  * @short_description: Integrate libvirt with the GMain event framework
37  * @title: Event loop
38  * @stability: Stable
39  * @include: libvirt-glib/libvirt-glib.h
40  *
41  * The libvirt API has the ability to provide applications with asynchronous
42  * notifications of interesting events. To enable this functionality though,
43  * applications must provide libvirt with an event loop implementation. The
44  * libvirt-glib API provides such an implementation, which naturally integrates
45  * with the GMain event loop framework.
46  *
47  * To enable use of the GMain event loop glue, the <code>gvir_event_register()</code>
48  * should be invoked. Once this is done, it is mandatory to have the default
49  * GMain event loop run by a thread in the application, usually the primary
50  * thread, eg by using <code>gtk_main()</code> or <code>g_application_run()</code>
51  *
52  * <example>
53  * <title>Registering for events with a GTK application</title>
54  * <programlisting><![CDATA[
55  * int main(int argc, char **argv) {
56  *   ...setup...
57  *   gvir_event_register();
58  *   ...more setup...
59  *   gtk_main();
60  *   return 0;
61  * }
62  * ]]></programlisting>
63  * </example>
64  *
65  * <example>
66  * <title>Registering for events using Appplication</title>
67  * <programlisting><![CDATA[
68  * int main(int argc, char **argv) {
69  *   ...setup...
70  *   GApplication *app = ...create some impl of GApplication...
71  *   gvir_event_register();
72  *   ...more setup...
73  *   g_application_run(app);
74  *   return 0;
75  * }
76  * ]]></programlisting>
77  * </example>
78  */
79
80
81 #if GLIB_CHECK_VERSION(2, 31, 0)
82 #define g_mutex_new() g_new0(GMutex, 1)
83 #endif
84
85 struct gvir_event_handle
86 {
87     int watch;
88     int fd;
89     int events;
90     int enabled;
91     GIOChannel *channel;
92     guint source;
93     virEventHandleCallback cb;
94     void *opaque;
95     virFreeCallback ff;
96 };
97
98 struct gvir_event_timeout
99 {
100     int timer;
101     int interval;
102     guint source;
103     virEventTimeoutCallback cb;
104     void *opaque;
105     virFreeCallback ff;
106 };
107
108 GMutex *eventlock = NULL;
109
110 static int nextwatch = 1;
111 static GPtrArray *handles;
112
113 static int nexttimer = 1;
114 static GPtrArray *timeouts;
115
116 static gboolean
117 gvir_event_handle_dispatch(GIOChannel *source G_GNUC_UNUSED,
118                            GIOCondition condition,
119                            gpointer opaque)
120 {
121     struct gvir_event_handle *data = opaque;
122     int events = 0;
123
124     if (condition & G_IO_IN)
125         events |= VIR_EVENT_HANDLE_READABLE;
126     if (condition & G_IO_OUT)
127         events |= VIR_EVENT_HANDLE_WRITABLE;
128     if (condition & G_IO_HUP)
129         events |= VIR_EVENT_HANDLE_HANGUP;
130     if (condition & G_IO_ERR)
131         events |= VIR_EVENT_HANDLE_ERROR;
132
133     g_debug("Dispatch handler %d %d %p\n", data->fd, events, data->opaque);
134
135     (data->cb)(data->watch, data->fd, events, data->opaque);
136
137     return TRUE;
138 }
139
140
141 static int
142 gvir_event_handle_add(int fd,
143                       int events,
144                       virEventHandleCallback cb,
145                       void *opaque,
146                       virFreeCallback ff)
147 {
148     struct gvir_event_handle *data;
149     GIOCondition cond = 0;
150     int ret;
151
152     g_mutex_lock(eventlock);
153
154     data = g_new0(struct gvir_event_handle, 1);
155
156     if (events & VIR_EVENT_HANDLE_READABLE)
157         cond |= G_IO_IN;
158     if (events & VIR_EVENT_HANDLE_WRITABLE)
159         cond |= G_IO_OUT;
160
161     data->watch = nextwatch++;
162     data->fd = fd;
163     data->events = events;
164     data->cb = cb;
165     data->opaque = opaque;
166     data->channel = g_io_channel_unix_new(fd);
167     data->ff = ff;
168
169     g_debug("Add handle %d %d %p\n", data->fd, events, data->opaque);
170
171     data->source = g_io_add_watch(data->channel,
172                                   cond,
173                                   gvir_event_handle_dispatch,
174                                   data);
175
176     g_ptr_array_add(handles, data);
177
178     ret = data->watch;
179
180     g_mutex_unlock(eventlock);
181
182     return ret;
183 }
184
185 static struct gvir_event_handle *
186 gvir_event_handle_find(int watch, guint *idx)
187 {
188     guint i;
189
190     for (i = 0 ; i < handles->len ; i++) {
191         struct gvir_event_handle *h = g_ptr_array_index(handles, i);
192
193         if (h == NULL) {
194             g_warn_if_reached ();
195             continue;
196         }
197
198         if (h->watch == watch) {
199             if (idx != NULL)
200                 *idx = i;
201             return h;
202         }
203     }
204
205     return NULL;
206 }
207
208 static void
209 gvir_event_handle_update(int watch,
210                          int events)
211 {
212     struct gvir_event_handle *data;
213
214     g_mutex_lock(eventlock);
215
216     data = gvir_event_handle_find(watch, NULL);
217     if (!data) {
218         g_debug("Update for missing handle watch %d", watch);
219         goto cleanup;
220     }
221
222     if (events) {
223         GIOCondition cond = 0;
224         if (events == data->events)
225             goto cleanup;
226
227         if (data->source)
228             g_source_remove(data->source);
229
230         cond |= G_IO_HUP;
231         if (events & VIR_EVENT_HANDLE_READABLE)
232             cond |= G_IO_IN;
233         if (events & VIR_EVENT_HANDLE_WRITABLE)
234             cond |= G_IO_OUT;
235         data->source = g_io_add_watch(data->channel,
236                                       cond,
237                                       gvir_event_handle_dispatch,
238                                       data);
239         data->events = events;
240     } else {
241         if (!data->source)
242             goto cleanup;
243
244         g_source_remove(data->source);
245         data->source = 0;
246         data->events = 0;
247     }
248
249 cleanup:
250     g_mutex_unlock(eventlock);
251 }
252
253 static gboolean
254 _event_handle_remove(gpointer data)
255 {
256     struct gvir_event_handle *h = data;
257
258     if (h->ff)
259         (h->ff)(h->opaque);
260
261     g_ptr_array_remove_fast(handles, h);
262
263     return FALSE;
264 }
265
266 static int
267 gvir_event_handle_remove(int watch)
268 {
269     struct gvir_event_handle *data;
270     int ret = -1;
271     guint idx;
272
273     g_mutex_lock(eventlock);
274
275     data = gvir_event_handle_find(watch, &idx);
276     if (!data) {
277         g_debug("Remove of missing watch %d", watch);
278         goto cleanup;
279     }
280
281     g_debug("Remove handle %d %d\n", watch, data->fd);
282
283     if (!data->source)
284         goto cleanup;
285
286     g_source_remove(data->source);
287     data->source = 0;
288     data->events = 0;
289     g_idle_add(_event_handle_remove, data);
290
291     ret = 0;
292
293 cleanup:
294     g_mutex_unlock(eventlock);
295     return ret;
296 }
297
298
299 static gboolean
300 gvir_event_timeout_dispatch(void *opaque)
301 {
302     struct gvir_event_timeout *data = opaque;
303     g_debug("Dispatch timeout %p %p %d %p\n", data, data->cb, data->timer, data->opaque);
304     (data->cb)(data->timer, data->opaque);
305
306     return TRUE;
307 }
308
309 static int
310 gvir_event_timeout_add(int interval,
311                        virEventTimeoutCallback cb,
312                        void *opaque,
313                        virFreeCallback ff)
314 {
315     struct gvir_event_timeout *data;
316     int ret;
317
318     g_mutex_lock(eventlock);
319
320     data = g_new0(struct gvir_event_timeout, 1);
321     data->timer = nexttimer++;
322     data->interval = interval;
323     data->cb = cb;
324     data->opaque = opaque;
325     data->ff = ff;
326     if (interval >= 0)
327         data->source = g_timeout_add(interval,
328                                      gvir_event_timeout_dispatch,
329                                      data);
330
331     g_ptr_array_add(timeouts, data);
332
333     g_debug("Add timeout %p %d %p %p %d\n", data, interval, cb, opaque, data->timer);
334
335     ret = data->timer;
336
337     g_mutex_unlock(eventlock);
338
339     return ret;
340 }
341
342
343 static struct gvir_event_timeout *
344 gvir_event_timeout_find(int timer, guint *idx)
345 {
346     guint i;
347
348     g_return_val_if_fail(timeouts != NULL, NULL);
349
350     for (i = 0 ; i < timeouts->len ; i++) {
351         struct gvir_event_timeout *t = g_ptr_array_index(timeouts, i);
352
353         if (t == NULL) {
354             g_warn_if_reached ();
355             continue;
356         }
357
358         if (t->timer == timer) {
359             if (idx != NULL)
360                 *idx = i;
361             return t;
362         }
363     }
364
365     return NULL;
366 }
367
368
369 static void
370 gvir_event_timeout_update(int timer,
371                           int interval)
372 {
373     struct gvir_event_timeout *data;
374
375     g_mutex_lock(eventlock);
376
377     data = gvir_event_timeout_find(timer, NULL);
378     if (!data) {
379         g_debug("Update of missing timer %d", timer);
380         goto cleanup;
381     }
382
383     g_debug("Update timeout %p %d %d\n", data, timer, interval);
384
385     if (interval >= 0) {
386         if (data->source)
387             goto cleanup;
388
389         data->interval = interval;
390         data->source = g_timeout_add(data->interval,
391                                      gvir_event_timeout_dispatch,
392                                      data);
393     } else {
394         if (!data->source)
395             goto cleanup;
396
397         g_source_remove(data->source);
398         data->source = 0;
399     }
400
401 cleanup:
402     g_mutex_unlock(eventlock);
403 }
404
405 static gboolean
406 _event_timeout_remove(gpointer data)
407 {
408     struct gvir_event_timeout *t = data;
409
410     if (t->ff)
411         (t->ff)(t->opaque);
412
413     g_ptr_array_remove_fast(timeouts, t);
414
415     return FALSE;
416 }
417
418 static int
419 gvir_event_timeout_remove(int timer)
420 {
421     struct gvir_event_timeout *data;
422     guint idx;
423     int ret = -1;
424
425     g_mutex_lock(eventlock);
426
427     data = gvir_event_timeout_find(timer, &idx);
428     if (!data) {
429         g_debug("Remove of missing timer %d", timer);
430         goto cleanup;
431     }
432
433     g_debug("Remove timeout %p %d\n", data, timer);
434
435     if (!data->source)
436         goto cleanup;
437
438     g_source_remove(data->source);
439     data->source = 0;
440     g_idle_add(_event_timeout_remove, data);
441
442     ret = 0;
443
444 cleanup:
445     g_mutex_unlock(eventlock);
446     return ret;
447 }
448
449
450 static gpointer event_register_once(gpointer data G_GNUC_UNUSED)
451 {
452     eventlock = g_mutex_new();
453     timeouts = g_ptr_array_new_with_free_func(g_free);
454     handles = g_ptr_array_new_with_free_func(g_free);
455     virEventRegisterImpl(gvir_event_handle_add,
456                          gvir_event_handle_update,
457                          gvir_event_handle_remove,
458                          gvir_event_timeout_add,
459                          gvir_event_timeout_update,
460                          gvir_event_timeout_remove);
461     return NULL;
462 }
463
464
465 /**
466  * gvir_event_register:
467  *
468  * Registers a libvirt event loop implementation that is backed
469  * by the default <code>GMain</code> context. If invoked more
470  * than once this method will be a no-op. Applications should,
471  * however, take care not to register any another non-GLib
472  * event loop with libvirt.
473  *
474  * After invoking this method, it is mandatory to run the
475  * default GMain event loop. Typically this can be satisfied
476  * by invoking <code>gtk_main</code> or <code>g_application_run</code>
477  * in the application's main thread. Failure to run the event
478  * loop will mean no libvirt events get dispatched, and the
479  * libvirt keepalive timer will kill off libvirt connections
480  * frequently.
481  */
482 void gvir_event_register(void)
483 {
484     static GOnce once = G_ONCE_INIT;
485
486     g_once(&once, event_register_once, NULL);
487 }