mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 17:34:18 +03:00
util: import an event loop impl based on GMainContext
The libvirt-glib project has provided a GMainContext based event loop impl for applications. This imports it and sets it up for use by libvirt as the primary event loop. This remains a private impl detail of libvirt. IOW, applications must *NOT* assume that a call to "virEventRegisterDefaultImpl" results in a GLib based event loop. They should continue to use the libvirt-glib API gvir_event_register() if they explicitly want to guarantee a GLib event loop. This follows the general principle that the libvirt public API should not expose the fact that GLib is being used internally. Reviewed-by: Pavel Hrdina <phrdina@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
parent
7d4350bcac
commit
f8ab47cb44
@ -2176,7 +2176,7 @@ exclude_file_name_regexp--sc_avoid_write = \
|
||||
|
||||
exclude_file_name_regexp--sc_bindtextdomain = .*
|
||||
|
||||
exclude_file_name_regexp--sc_gettext_init = ^((tests|examples)/|tools/virt-login-shell.c)
|
||||
exclude_file_name_regexp--sc_gettext_init = ^((tests|examples)/|tools/virt-login-shell.c|src/util/vireventglib\.c)
|
||||
|
||||
exclude_file_name_regexp--sc_copyright_format = \
|
||||
^build-aux/syntax-check\.mk$$
|
||||
|
@ -1933,6 +1933,11 @@ virSetErrorLogPriorityFunc;
|
||||
virStrerror;
|
||||
|
||||
|
||||
# util/vireventglib.h
|
||||
virEventGLibRegister;
|
||||
virEventGLibRunOnce;
|
||||
|
||||
|
||||
# util/vireventpoll.h
|
||||
virEventPollAddHandle;
|
||||
virEventPollAddTimeout;
|
||||
|
@ -15,6 +15,20 @@ provider libvirt {
|
||||
|
||||
probe event_poll_run(int nfds, int timeout);
|
||||
|
||||
# file: src/util/vireventglib.c
|
||||
# prefix: event_glib
|
||||
probe event_glib_add_handle(int watch, int fd, int events, void *cb, void *opaque, void *ff);
|
||||
probe event_glib_update_handle(int watch, int events);
|
||||
probe event_glib_remove_handle(int watch);
|
||||
probe event_glib_remove_handle_idle(int watch, void *ff, void *opaque);
|
||||
probe event_glib_dispatch_handle(int watch, int events, void *cb, void *opaque);
|
||||
|
||||
probe event_glib_add_timeout(int timer, int frequency, void *cb, void *opaque, void *ff);
|
||||
probe event_glib_update_timeout(int timer, int frequency);
|
||||
probe event_glib_remove_timeout(int timer);
|
||||
probe event_glib_remove_timeout_idle(int timer, void *ff, void *opaque);
|
||||
probe event_glib_dispatch_timeout(int timer, void *cb, void *opaque);
|
||||
|
||||
# file: src/util/virdbus.c
|
||||
# prefix: dbus
|
||||
probe dbus_method_call(const char *interface, const char *member, const char *object, const char *destination);
|
||||
|
@ -61,6 +61,8 @@ UTIL_SOURCES = \
|
||||
util/virerrorpriv.h \
|
||||
util/virevent.c \
|
||||
util/virevent.h \
|
||||
util/vireventglib.c \
|
||||
util/vireventglib.h \
|
||||
util/vireventglibwatch.c \
|
||||
util/vireventglibwatch.h \
|
||||
util/vireventpoll.c \
|
||||
|
499
src/util/vireventglib.c
Normal file
499
src/util/vireventglib.c
Normal file
@ -0,0 +1,499 @@
|
||||
/*
|
||||
* vireventglib.c: GMainContext based event loop
|
||||
*
|
||||
* Copyright (C) 2008 Daniel P. Berrange
|
||||
* Copyright (C) 2010-2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "vireventglib.h"
|
||||
#include "vireventglibwatch.h"
|
||||
#include "virerror.h"
|
||||
#include "virlog.h"
|
||||
#include "virprobe.h"
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_EVENT
|
||||
|
||||
VIR_LOG_INIT("util.eventglib");
|
||||
|
||||
struct virEventGLibHandle
|
||||
{
|
||||
int watch;
|
||||
int fd;
|
||||
int events;
|
||||
int removed;
|
||||
guint source;
|
||||
virEventHandleCallback cb;
|
||||
void *opaque;
|
||||
virFreeCallback ff;
|
||||
};
|
||||
|
||||
struct virEventGLibTimeout
|
||||
{
|
||||
int timer;
|
||||
int interval;
|
||||
int removed;
|
||||
guint source;
|
||||
virEventTimeoutCallback cb;
|
||||
void *opaque;
|
||||
virFreeCallback ff;
|
||||
};
|
||||
|
||||
static GMutex *eventlock;
|
||||
|
||||
static int nextwatch = 1;
|
||||
static GPtrArray *handles;
|
||||
|
||||
static int nexttimer = 1;
|
||||
static GPtrArray *timeouts;
|
||||
|
||||
static GIOCondition
|
||||
virEventGLibEventsToCondition(int events)
|
||||
{
|
||||
GIOCondition cond = 0;
|
||||
if (events & VIR_EVENT_HANDLE_READABLE)
|
||||
cond |= G_IO_IN;
|
||||
if (events & VIR_EVENT_HANDLE_WRITABLE)
|
||||
cond |= G_IO_OUT;
|
||||
if (events & VIR_EVENT_HANDLE_ERROR)
|
||||
cond |= G_IO_ERR;
|
||||
if (events & VIR_EVENT_HANDLE_HANGUP)
|
||||
cond |= G_IO_HUP;
|
||||
return cond;
|
||||
}
|
||||
|
||||
static int
|
||||
virEventGLibConditionToEvents(GIOCondition cond)
|
||||
{
|
||||
int events = 0;
|
||||
if (cond & G_IO_IN)
|
||||
events |= VIR_EVENT_HANDLE_READABLE;
|
||||
if (cond & G_IO_OUT)
|
||||
events |= VIR_EVENT_HANDLE_WRITABLE;
|
||||
if (cond & G_IO_ERR)
|
||||
events |= VIR_EVENT_HANDLE_ERROR;
|
||||
if (cond & G_IO_NVAL) /* Treat NVAL as error, since libvirt doesn't distinguish */
|
||||
events |= VIR_EVENT_HANDLE_ERROR;
|
||||
if (cond & G_IO_HUP)
|
||||
events |= VIR_EVENT_HANDLE_HANGUP;
|
||||
return events;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
virEventGLibHandleDispatch(int fd G_GNUC_UNUSED,
|
||||
GIOCondition condition,
|
||||
gpointer opaque)
|
||||
{
|
||||
struct virEventGLibHandle *data = opaque;
|
||||
int events = virEventGLibConditionToEvents(condition);
|
||||
|
||||
VIR_DEBUG("Dispatch handler data=%p watch=%d fd=%d events=%d opaque=%p",
|
||||
data, data->watch, data->fd, events, data->opaque);
|
||||
|
||||
PROBE(EVENT_GLIB_DISPATCH_HANDLE,
|
||||
"watch=%d events=%d cb=%p opaque=%p",
|
||||
data->watch, events, data->cb, data->opaque);
|
||||
|
||||
(data->cb)(data->watch, data->fd, events, data->opaque);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virEventGLibHandleAdd(int fd,
|
||||
int events,
|
||||
virEventHandleCallback cb,
|
||||
void *opaque,
|
||||
virFreeCallback ff)
|
||||
{
|
||||
struct virEventGLibHandle *data;
|
||||
GIOCondition cond = virEventGLibEventsToCondition(events);
|
||||
int ret;
|
||||
|
||||
g_mutex_lock(eventlock);
|
||||
|
||||
data = g_new0(struct virEventGLibHandle, 1);
|
||||
|
||||
data->watch = nextwatch++;
|
||||
data->fd = fd;
|
||||
data->events = events;
|
||||
data->cb = cb;
|
||||
data->opaque = opaque;
|
||||
data->ff = ff;
|
||||
|
||||
VIR_DEBUG("Add handle data=%p watch=%d fd=%d events=%d opaque=%p",
|
||||
data, data->watch, data->fd, events, data->opaque);
|
||||
|
||||
if (events != 0) {
|
||||
data->source = virEventGLibAddSocketWatch(
|
||||
fd, cond, NULL, virEventGLibHandleDispatch, data, NULL);
|
||||
}
|
||||
|
||||
g_ptr_array_add(handles, data);
|
||||
|
||||
ret = data->watch;
|
||||
|
||||
PROBE(EVENT_GLIB_ADD_HANDLE,
|
||||
"watch=%d fd=%d events=%d cb=%p opaque=%p ff=%p",
|
||||
ret, fd, events, cb, opaque, ff);
|
||||
g_mutex_unlock(eventlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct virEventGLibHandle *
|
||||
virEventGLibHandleFind(int watch)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < handles->len; i++) {
|
||||
struct virEventGLibHandle *h = g_ptr_array_index(handles, i);
|
||||
|
||||
if (h == NULL) {
|
||||
g_warn_if_reached();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((h->watch == watch) && !h->removed)
|
||||
return h;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
virEventGLibHandleUpdate(int watch,
|
||||
int events)
|
||||
{
|
||||
struct virEventGLibHandle *data;
|
||||
|
||||
PROBE(EVENT_GLIB_UPDATE_HANDLE,
|
||||
"watch=%d events=%d",
|
||||
watch, events);
|
||||
g_mutex_lock(eventlock);
|
||||
|
||||
data = virEventGLibHandleFind(watch);
|
||||
if (!data) {
|
||||
VIR_DEBUG("Update for missing handle watch=%d", watch);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
VIR_DEBUG("Update handle data=%p watch=%d fd=%d events=%d",
|
||||
data, watch, data->fd, events);
|
||||
|
||||
if (events != 0) {
|
||||
GIOCondition cond = virEventGLibEventsToCondition(events);
|
||||
if (events == data->events)
|
||||
goto cleanup;
|
||||
|
||||
if (data->source != 0) {
|
||||
VIR_DEBUG("Removed old handle watch=%d", data->source);
|
||||
g_source_remove(data->source);
|
||||
}
|
||||
|
||||
data->source = virEventGLibAddSocketWatch(
|
||||
data->fd, cond, NULL, virEventGLibHandleDispatch, data, NULL);
|
||||
|
||||
data->events = events;
|
||||
VIR_DEBUG("Added new handle watch=%d", data->source);
|
||||
} else {
|
||||
if (data->source == 0)
|
||||
goto cleanup;
|
||||
|
||||
VIR_DEBUG("Removed old handle watch=%d", data->source);
|
||||
g_source_remove(data->source);
|
||||
data->source = 0;
|
||||
data->events = 0;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
g_mutex_unlock(eventlock);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
virEventGLibHandleRemoveIdle(gpointer data)
|
||||
{
|
||||
struct virEventGLibHandle *h = data;
|
||||
|
||||
PROBE(EVENT_GLIB_REMOVE_HANDLE_IDLE,
|
||||
"watch=%d ff=%p opaque=%p",
|
||||
h->watch, h->ff, h->opaque);
|
||||
if (h->ff)
|
||||
(h->ff)(h->opaque);
|
||||
|
||||
g_mutex_lock(eventlock);
|
||||
g_ptr_array_remove_fast(handles, h);
|
||||
g_mutex_unlock(eventlock);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
virEventGLibHandleRemove(int watch)
|
||||
{
|
||||
struct virEventGLibHandle *data;
|
||||
int ret = -1;
|
||||
|
||||
PROBE(EVENT_GLIB_REMOVE_HANDLE,
|
||||
"watch=%d",
|
||||
watch);
|
||||
g_mutex_lock(eventlock);
|
||||
|
||||
data = virEventGLibHandleFind(watch);
|
||||
if (!data) {
|
||||
VIR_DEBUG("Remove of missing handle watch=%d", watch);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
VIR_DEBUG("Remove handle data=%p watch=%d fd=%d",
|
||||
data, watch, data->fd);
|
||||
|
||||
if (data->source != 0) {
|
||||
g_source_remove(data->source);
|
||||
data->source = 0;
|
||||
data->events = 0;
|
||||
}
|
||||
|
||||
/* since the actual watch deletion is done asynchronously, a handleUpdate call may
|
||||
* reschedule the watch before it's fully deleted, that's why we need to mark it as
|
||||
* 'removed' to prevent reuse
|
||||
*/
|
||||
data->removed = TRUE;
|
||||
g_idle_add(virEventGLibHandleRemoveIdle, data);
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
g_mutex_unlock(eventlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
virEventGLibTimeoutDispatch(void *opaque)
|
||||
{
|
||||
struct virEventGLibTimeout *data = opaque;
|
||||
|
||||
VIR_DEBUG("Dispatch timeout data=%p cb=%p timer=%d opaque=%p",
|
||||
data, data->cb, data->timer, data->opaque);
|
||||
|
||||
PROBE(EVENT_GLIB_DISPATCH_TIMEOUT,
|
||||
"timer=%d cb=%p opaque=%p",
|
||||
data->timer, data->cb, data->opaque);
|
||||
(data->cb)(data->timer, data->opaque);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
virEventGLibTimeoutAdd(int interval,
|
||||
virEventTimeoutCallback cb,
|
||||
void *opaque,
|
||||
virFreeCallback ff)
|
||||
{
|
||||
struct virEventGLibTimeout *data;
|
||||
int ret;
|
||||
|
||||
g_mutex_lock(eventlock);
|
||||
|
||||
data = g_new0(struct virEventGLibTimeout, 1);
|
||||
data->timer = nexttimer++;
|
||||
data->interval = interval;
|
||||
data->cb = cb;
|
||||
data->opaque = opaque;
|
||||
data->ff = ff;
|
||||
if (interval >= 0)
|
||||
data->source = g_timeout_add(interval,
|
||||
virEventGLibTimeoutDispatch,
|
||||
data);
|
||||
|
||||
g_ptr_array_add(timeouts, data);
|
||||
|
||||
VIR_DEBUG("Add timeout data=%p interval=%d ms cb=%p opaque=%p timer=%d",
|
||||
data, interval, cb, opaque, data->timer);
|
||||
|
||||
ret = data->timer;
|
||||
|
||||
PROBE(EVENT_GLIB_ADD_TIMEOUT,
|
||||
"timer=%d interval=%d cb=%p opaque=%p ff=%p",
|
||||
ret, interval, cb, opaque, ff);
|
||||
g_mutex_unlock(eventlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static struct virEventGLibTimeout *
|
||||
virEventGLibTimeoutFind(int timer)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_return_val_if_fail(timeouts != NULL, NULL);
|
||||
|
||||
for (i = 0; i < timeouts->len; i++) {
|
||||
struct virEventGLibTimeout *t = g_ptr_array_index(timeouts, i);
|
||||
|
||||
if (t == NULL) {
|
||||
g_warn_if_reached();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((t->timer == timer) && !t->removed)
|
||||
return t;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
virEventGLibTimeoutUpdate(int timer,
|
||||
int interval)
|
||||
{
|
||||
struct virEventGLibTimeout *data;
|
||||
|
||||
PROBE(EVENT_GLIB_UPDATE_TIMEOUT,
|
||||
"timer=%d interval=%d",
|
||||
timer, interval);
|
||||
g_mutex_lock(eventlock);
|
||||
|
||||
data = virEventGLibTimeoutFind(timer);
|
||||
if (!data) {
|
||||
VIR_DEBUG("Update of missing timeout timer=%d", timer);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
VIR_DEBUG("Update timeout data=%p timer=%d interval=%d ms", data, timer, interval);
|
||||
|
||||
if (interval >= 0) {
|
||||
if (data->source != 0)
|
||||
g_source_remove(data->source);
|
||||
|
||||
data->interval = interval;
|
||||
data->source = g_timeout_add(data->interval,
|
||||
virEventGLibTimeoutDispatch,
|
||||
data);
|
||||
} else {
|
||||
if (data->source == 0)
|
||||
goto cleanup;
|
||||
|
||||
g_source_remove(data->source);
|
||||
data->source = 0;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
g_mutex_unlock(eventlock);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
virEventGLibTimeoutRemoveIdle(gpointer data)
|
||||
{
|
||||
struct virEventGLibTimeout *t = data;
|
||||
|
||||
PROBE(EVENT_GLIB_REMOVE_TIMEOUT_IDLE,
|
||||
"timer=%d ff=%p opaque=%p",
|
||||
t->timer, t->ff, t->opaque);
|
||||
|
||||
if (t->ff)
|
||||
(t->ff)(t->opaque);
|
||||
|
||||
g_mutex_lock(eventlock);
|
||||
g_ptr_array_remove_fast(timeouts, t);
|
||||
g_mutex_unlock(eventlock);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
virEventGLibTimeoutRemove(int timer)
|
||||
{
|
||||
struct virEventGLibTimeout *data;
|
||||
int ret = -1;
|
||||
|
||||
PROBE(EVENT_GLIB_REMOVE_TIMEOUT,
|
||||
"timer=%d",
|
||||
timer);
|
||||
g_mutex_lock(eventlock);
|
||||
|
||||
data = virEventGLibTimeoutFind(timer);
|
||||
if (!data) {
|
||||
VIR_DEBUG("Remove of missing timeout timer=%d", timer);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
VIR_DEBUG("Remove timeout data=%p timer=%d",
|
||||
data, timer);
|
||||
|
||||
if (data->source != 0) {
|
||||
g_source_remove(data->source);
|
||||
data->source = 0;
|
||||
}
|
||||
|
||||
/* since the actual timeout deletion is done asynchronously, a timeoutUpdate call may
|
||||
* reschedule the timeout before it's fully deleted, that's why we need to mark it as
|
||||
* 'removed' to prevent reuse
|
||||
*/
|
||||
data->removed = TRUE;
|
||||
g_idle_add(virEventGLibTimeoutRemoveIdle, data);
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
g_mutex_unlock(eventlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static gpointer virEventGLibRegisterOnce(gpointer data G_GNUC_UNUSED)
|
||||
{
|
||||
eventlock = g_new0(GMutex, 1);
|
||||
timeouts = g_ptr_array_new_with_free_func(g_free);
|
||||
handles = g_ptr_array_new_with_free_func(g_free);
|
||||
virEventRegisterImpl(virEventGLibHandleAdd,
|
||||
virEventGLibHandleUpdate,
|
||||
virEventGLibHandleRemove,
|
||||
virEventGLibTimeoutAdd,
|
||||
virEventGLibTimeoutUpdate,
|
||||
virEventGLibTimeoutRemove);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void virEventGLibRegister(void)
|
||||
{
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
g_once(&once, virEventGLibRegisterOnce, NULL);
|
||||
}
|
||||
|
||||
|
||||
int virEventGLibRunOnce(void)
|
||||
{
|
||||
g_main_context_iteration(NULL, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
28
src/util/vireventglib.h
Normal file
28
src/util/vireventglib.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* vireventglib.h: GMainContext based event loop
|
||||
*
|
||||
* Copyright (C) 2008 Daniel P. Berrange
|
||||
* Copyright (C) 2010-2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
void virEventGLibRegister(void);
|
||||
|
||||
int virEventGLibRunOnce(void);
|
Loading…
Reference in New Issue
Block a user